diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 00000000..4ca0ec66 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.skills \ No newline at end of file diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 00000000..d689ffa1 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_mcc_build_guard.py\"", + "statusMessage": "Checking MCC build command policy" + } + ] + } + ] + } +} diff --git a/.codex/hooks/pre_tool_use_mcc_build_guard.py b/.codex/hooks/pre_tool_use_mcc_build_guard.py new file mode 100644 index 00000000..fb6d3c1b --- /dev/null +++ b/.codex/hooks/pre_tool_use_mcc_build_guard.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +import json +import re +import sys + + +RAW_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])dotnet\s+build(\s|$)") +ABSOLUTE_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+build(\s|$)") +RAW_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])dotnet\s+publish(\s|$)") +ABSOLUTE_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+publish(\s|$)") + + +def main() -> int: + try: + payload = json.load(sys.stdin) + except json.JSONDecodeError: + return 0 + + command = payload.get("tool_input", {}).get("command", "") + if not isinstance(command, str) or not command: + return 0 + + if ABSOLUTE_DOTNET_BUILD_RE.search(command) or ABSOLUTE_DOTNET_PUBLISH_RE.search(command): + return 0 + + if RAW_DOTNET_BUILD_RE.search(command): + response = { + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": ( + "Raw 'dotnet build' is blocked in this repository. " + "Use 'source tools/mcc-env.sh && mcc-build' instead so MCC temp-build routing stays active. " + "If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet build ...' to bypass this guard." + ), + }, + "systemMessage": ( + "Blocked raw 'dotnet build'. Use 'source tools/mcc-env.sh && mcc-build'. " + "If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet build ...' explicitly." + ), + } + json.dump(response, sys.stdout) + sys.stdout.write("\n") + elif RAW_DOTNET_PUBLISH_RE.search(command): + response = { + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": ( + "Raw 'dotnet publish' is blocked in this repository. " + "Use 'source tools/mcc-env.sh && mcc-publish --rid ' instead so MCC publish defaults stay aligned with the repo workflow. " + "If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet publish ...' to bypass this guard." + ), + }, + "systemMessage": ( + "Blocked raw 'dotnet publish'. Use 'source tools/mcc-env.sh && mcc-publish --rid '. " + "If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet publish ...' explicitly." + ), + } + json.dump(response, sys.stdout) + sys.stdout.write("\n") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.codex/skills b/.codex/skills new file mode 120000 index 00000000..4ca0ec66 --- /dev/null +++ b/.codex/skills @@ -0,0 +1 @@ +../.skills \ No newline at end of file diff --git a/.cursor/skills b/.cursor/skills new file mode 120000 index 00000000..4ca0ec66 --- /dev/null +++ b/.cursor/skills @@ -0,0 +1 @@ +../.skills \ No newline at end of file diff --git a/.cursorindexingignore b/.cursorindexingignore new file mode 100644 index 00000000..953908e7 --- /dev/null +++ b/.cursorindexingignore @@ -0,0 +1,3 @@ + +# Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references +.specstory/** diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 660f9900..6c667ae1 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -8,101 +8,39 @@ on: env: PROJECT: "MinecraftClient" - target-version: "net7.0" - compile-flags: "--self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded" + target-version: "net10.0" + dotnet-version: "10.0.x" + compile-flags: "--self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true" jobs: - build: - runs-on: ubuntu-latest - if: ${{ always() && needs.fetch-translations.result != 'failure' }} - needs: [determine-build, fetch-translations] - timeout-minutes: 15 - strategy: - matrix: - target: [win-x86, win-x64, win-arm, win-arm64, linux-x64, linux-arm, linux-arm64, osx-x64, osx-arm64] - + determine-build: + runs-on: ubuntu-slim + outputs: + skip: ${{ steps.check-skip.outputs.skip }} steps: - - name: Checkout - uses: actions/checkout@v3 - if: ${{ always() && needs.fetch-translations.result == 'skipped' }} - with: - fetch-depth: 0 - submodules: 'true' - - - name: Get Current Date + - name: Check skip CI + id: check-skip run: | - echo date=$(date +'%Y%m%d') >> $GITHUB_ENV - echo date_dashed=$(date -u +'%Y-%m-%d') >> $GITHUB_ENV - - - name: Restore Translations (if available) - uses: actions/cache/restore@v3 - with: - path: ${{ github.workspace }}/* - key: "translation-${{ github.sha }}" - restore-keys: "translation-" - - - name: Setup Environment Variables (early) - run: | - echo project-path=${{ github.workspace }}/${{ env.PROJECT }} >> $GITHUB_ENV - echo file-ext=${{ (startsWith(matrix.target, 'win') && '.exe') || ' ' }} >> $GITHUB_ENV - - - name: Setup Environment Variables - run: | - echo target-out-path=${{ env.project-path }}/bin/Release/${{ env.target-version }}/${{ matrix.target }}/publish/ >> $GITHUB_ENV - echo assembly-info=${{ env.project-path }}/Properties/AssemblyInfo.cs >> $GITHUB_ENV - echo build-version-info=${{ env.date }}-${{ github.run_number }} >> $GITHUB_ENV - echo commit=$(echo ${{ github.sha }} | cut -c 1-7) >> $GITHUB_ENV - - - name: Setup Environment Variables (late) - run: | - echo built-executable-path=${{ env.target-out-path }}${{ env.PROJECT }}${{ env.file-ext }} >> $GITHUB_ENV - - - name: Set Version Info - run: | - echo '' >> ${{ env.assembly-info }} - echo "[assembly: AssemblyConfiguration(\"GitHub build ${{ github.run_number }}, built on ${{ env.date_dashed }} from commit ${{ env.commit }}\")]" >> ${{ env.assembly-info }} - sed -i -e 's|SentryDSN = "";|SentryDSN = "${{ secrets.SENTRY_DSN }}";|g' ${{ env.project-path }}/Program.cs - - - name: Build Target - run: dotnet publish ${{ env.project-path }}.sln -f ${{ env.target-version }} -r ${{ matrix.target }} ${{ env.compile-flags }} + LOWER=$(echo "$COMMIT_MSG" | tr '[:upper:]' '[:lower:]') + if echo "$LOWER" | grep -qE 'skip.?ci|ci.?skip'; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi env: - DOTNET_NOLOGO: true + COMMIT_MSG: ${{ github.event.head_commit.message }} - - name: Rename Binary - run: | - mv ${{ env.built-executable-path }} ${{ env.PROJECT }}-${{ env.build-version-info }}-${{ matrix.target }}${{ (startsWith(matrix.target, 'win') && '.exe') || ' ' }} - - - name: Wait - # We wait before creating a release because we might run into a race condition - # while creating a new tag (as opposed to using the existing tag, if any) since we're running builds in parallel. - run: | - sleep 5s - - - name: Create Release - uses: ncipollo/release-action@v1.14.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - artifacts: ${{ env.PROJECT }}-${{ env.build-version-info }}-${{ matrix.target }}${{ (startsWith(matrix.target, 'win') && '.exe') || ' ' }} - tag: ${{ format('{0}-{1}', env.date, github.run_number) }} - name: '${{ env.build-version-info }}: ${{ github.event.head_commit.message }}' - generateReleaseNotes: true - artifactErrorsFailBuild: true - allowUpdates: true - makeLatest: true - omitBodyDuringUpdate: true - omitNameDuringUpdate: true - replacesArtifacts: false - fetch-translations: strategy: fail-fast: true runs-on: ubuntu-latest needs: determine-build - # Translations will only be fetched in the MCCTeam repository, since it needs crowdin secrets. - if: ${{ github.repository == 'MCCTeam/Minecraft-Console-Client' }} + + if: ${{ needs.determine-build.outputs.skip != 'true' }} + timeout-minutes: 15 - - steps: + + steps: - name: Check cache uses: actions/cache/restore@v3 id: cache-check @@ -111,19 +49,31 @@ jobs: key: "translation-${{ github.sha }}" lookup-only: true restore-keys: "translation-" - + - name: Checkout - uses: actions/checkout@v3 if: steps.cache-check.outputs.cache-hit != 'true' + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: 'true' + - name: Check Crowdin secrets + id: crowdin-check + run: | + if [ -z "$CROWDIN_PROJECT_ID" ] || [ -z "$CROWDIN_PERSONAL_TOKEN" ]; then + echo "available=false" >> $GITHUB_OUTPUT + else + echo "available=true" >> $GITHUB_OUTPUT + fi + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }} + - name: Download translations from crowdin - uses: crowdin/github-action@v1.6.0 - if: steps.cache-check.outputs.cache-hit != 'true' + uses: crowdin/github-action@v2.4.0 + if: steps.cache-check.outputs.cache-hit != 'true' && steps.crowdin-check.outputs.available == 'true' with: - upload_sources: false + upload_sources: ${{ github.repository == 'MCCTeam/Minecraft-Console-Client' }} upload_translations: false download_translations: true @@ -143,12 +93,154 @@ jobs: with: path: ${{ github.workspace }}/* key: "translation-${{ github.sha }}" - - determine-build: - runs-on: ubuntu-latest - strategy: - fail-fast: true - if: ${{ !contains(github.event.head_commit.message, 'skip') || !contains(github.event.head_commit.message, 'skipci')}} + + create-tag: + runs-on: ubuntu-slim + timeout-minutes: 5 # Wait 5 minutes in case of network issues/etc + needs: determine-build + if: ${{ needs.determine-build.outputs.skip != 'true' }} steps: - - name: dummy action - run: "echo 'dummy action that checks if the build is to be skipped, if it is, this action does not run to break the entire build action'" + - id: make-tag + run: | + TAG="$(date -u +'%Y%m%d')-${{ github.run_number }}" + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "TAG=$TAG" >> $GITHUB_ENV + + - name: Create Release Tag + uses: actions/github-script@v7 + with: + script: | + const tag = process.env.TAG; + try { + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/tags/${tag}`, + sha: context.sha + }); + } catch(error) { + if (error.message.includes('already exists')) { + console.log(`Tag ${tag} already exists`); + } else { + throw error; + } + } + outputs: + build-tag: ${{ steps.make-tag.outputs.tag }} + + build: + runs-on: ubuntu-latest + # Check if we're not skipping build, tag is created, and translations successfully fetched (or skipped) + if: ${{ needs.determine-build.outputs.skip != 'true' && + needs.create-tag.result == 'success' && + (needs.fetch-translations.result == 'success' || needs.fetch-translations.result == 'skipped') + }} + needs: [determine-build, fetch-translations, create-tag] + timeout-minutes: 15 + strategy: + matrix: + target: [win-x86, win-x64, win-arm64, linux-x64, linux-arm, linux-arm64, osx-x64, osx-arm64] + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: 'true' + + - name: Get Current Date + run: | + echo date_dashed=$(date -u +'%Y-%m-%d') >> $GITHUB_ENV + + - name: Restore Translations (if available) + uses: actions/cache/restore@v3 + with: + path: ${{ github.workspace }}/* + key: "translation-${{ github.sha }}" + restore-keys: "translation-" + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.dotnet-version }} + + - name: Setup Environment Variables + run: | + PROJECT_PATH=${{ github.workspace }}/${{ env.PROJECT }} + FILE_EXT=${{ (startsWith(matrix.target, 'win') && '.exe') || '' }} + TARGET_OUT_PATH=$PROJECT_PATH/bin/Release/${{ env.target-version }}/${{ matrix.target }}/publish/ + + echo "project-path=$PROJECT_PATH" >> $GITHUB_ENV + echo "file-ext=$FILE_EXT" >> $GITHUB_ENV + echo "target-out-path=$TARGET_OUT_PATH" >> $GITHUB_ENV + echo "assembly-info=$PROJECT_PATH/Properties/AssemblyInfo.cs" >> $GITHUB_ENV + echo "build-version-info=${{ needs.create-tag.outputs.build-tag }}" >> $GITHUB_ENV + echo "commit=$(echo ${{ github.sha }} | cut -c 1-7)" >> $GITHUB_ENV + + - name: Setup Binaries Path + run: | + echo built-executable-path=${{ env.target-out-path }}${{ env.PROJECT }}${{ env.file-ext }} >> $GITHUB_ENV + + - name: Set Version Info + run: | + echo '' >> ${{ env.assembly-info }} + echo "[assembly: AssemblyConfiguration(\"GitHub build ${{ github.run_number }}, built on ${{ env.date_dashed }} from commit ${{ env.commit }}\")]" >> ${{ env.assembly-info }} + + - name: Inject Sentry DSN (if applicable) + if: ${{ github.repository == 'MCCTeam/Minecraft-Console-Client' }} + run: | + grep -q 'SentryDSN = "";' ${{ env.project-path }}/Program.cs || { echo "SentryDSN pattern not found in Program.cs"; exit 1; } + sed -i -e 's|SentryDSN = "";|SentryDSN = "${{ secrets.SENTRY_DSN }}";|g' ${{ env.project-path }}/Program.cs + + - name: Build Target + run: dotnet publish ${{ env.project-path }}/${{ env.PROJECT }}.csproj -f ${{ env.target-version }} -r ${{ matrix.target }} ${{ env.compile-flags }} + env: + DOTNET_NOLOGO: true + + - name: Rename Binary + run: | + mv ${{ env.built-executable-path }} ${{ env.PROJECT }}-${{ env.build-version-info }}-${{ matrix.target }}${{ env.file-ext }} + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT }}-${{ env.build-version-info }}-${{ matrix.target }} + path: ${{ env.PROJECT }}-${{ env.build-version-info }}-${{ matrix.target }}${{ env.file-ext }} + if-no-files-found: error + + create-release: + runs-on: ubuntu-slim + needs: [create-tag, build] + if: ${{ needs.build.result == 'success' && needs.create-tag.result == 'success' }} + steps: + - name: Download All Artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + merge-multiple: true + + - name: Truncate commit message for release name + id: release-name + run: | + SUBJECT=$(echo "$COMMIT_MSG" | head -n 1) + MAX=220 + TRUNCATED="${SUBJECT:0:$MAX}" + echo "name=${BUILD_TAG}: $TRUNCATED" >> $GITHUB_OUTPUT + env: + COMMIT_MSG: ${{ github.event.head_commit.message }} + BUILD_TAG: ${{ needs.create-tag.outputs.build-tag }} + + - name: Create Release + uses: ncipollo/release-action@v1.14.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + artifacts: "artifacts/**/*" + tag: ${{ needs.create-tag.outputs.build-tag }} + name: ${{ steps.release-name.outputs.name }} + generateReleaseNotes: true + artifactErrorsFailBuild: true + allowUpdates: true + makeLatest: true + omitBodyDuringUpdate: true + omitNameDuringUpdate: true + replacesArtifacts: true diff --git a/.github/workflows/deploy-doc-only.yml b/.github/workflows/deploy-doc-only.yml index 5777fce3..ba457093 100644 --- a/.github/workflows/deploy-doc-only.yml +++ b/.github/workflows/deploy-doc-only.yml @@ -6,8 +6,27 @@ on: workflow_dispatch: jobs: + check-secrets: + runs-on: ubuntu-latest + outputs: + has-deploy-token: ${{ steps.check.outputs.has-deploy-token }} + steps: + - name: Check required secrets + id: check + run: | + if [ -z "$GH_PAGES_TOKEN" ]; then + echo "has-deploy-token=false" >> $GITHUB_OUTPUT + echo "::warning::GH_PAGES_TOKEN is not set, skipping documentation deployment." + else + echo "has-deploy-token=true" >> $GITHUB_OUTPUT + fi + env: + GH_PAGES_TOKEN: ${{ secrets.GH_PAGES_TOKEN }} + Build: runs-on: ubuntu-latest + needs: check-secrets + if: ${{ needs.check-secrets.outputs.has-deploy-token == 'true' }} steps: @@ -17,8 +36,22 @@ jobs: fetch-depth: 0 submodules: 'true' + - name: Check Crowdin secrets + id: crowdin-check + run: | + if [ -z "$CROWDIN_PROJECT_ID" ] || [ -z "$CROWDIN_PERSONAL_TOKEN" ]; then + echo "available=false" >> $GITHUB_OUTPUT + echo "::warning::Crowdin secrets not set, skipping translation download." + else + echo "available=true" >> $GITHUB_OUTPUT + fi + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }} + - name: Download translations from crowdin - uses: crowdin/github-action@v1.6.0 + if: ${{ steps.crowdin-check.outputs.available == 'true' }} + uses: crowdin/github-action@v2.4.0 with: upload_sources: false upload_translations: false @@ -40,7 +73,7 @@ jobs: ACCESS_TOKEN: ${{ secrets.GH_PAGES_TOKEN }} TARGET_REPO: MCCTeam/MCCTeam.github.io TARGET_BRANCH: master - BUILD_SCRIPT: yarn --cwd ./docs/ && yarn --cwd ./docs/ docs:build + BUILD_SCRIPT: export NODE_OPTIONS=--max-old-space-size=8192 && yarn --cwd ./docs/ && yarn --cwd ./docs/ docs:build BUILD_DIR: docs/.vuepress/dist COMMIT_MESSAGE: Build from ${{ github.sha }} CNAME: https://mccteam.github.io diff --git a/.gitignore b/.gitignore index fc8f35e0..8116dfed 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,15 @@ /Other/ /.vs/ SessionCache.ini -.* -!/.github /packages +# OS-generated files +.DS_Store +Thumbs.db +desktop.ini +ehthumbs.db +._* + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## @@ -383,7 +388,7 @@ FodyWeavers.xsd .vscode/* !.vscode/settings.json !.vscode/tasks.json -!.vscode/launch.json +!.vscode/launch.json`` !.vscode/extensions.json *.code-workspace @@ -404,6 +409,8 @@ FodyWeavers.xsd # docs !/docs/.vuepress +/docs/.vuepress/.cache +/docs/.vuepress/.temp /docs/.vuepress/dist # translations @@ -416,3 +423,25 @@ FodyWeavers.xsd /docs/l10n/ /docs/.vuepress/public/MCC-README/ +/docs/superpowers/ + +# Floder to store the decompiled Minecraft official source code +/MinecraftOfficial/ + +# Possible debug files +/lang/* +/mcc_input.txt +/MinecraftClient.ini +/MinecraftClient.backup.ini + +# SpecStory files +/.specstory/ +/.vscode/settings.json + +# Other +/Sentry/ +/downloads/ +server.pid + +# Crowdin translation automation working directory +/.crowdin-translate/ diff --git a/.skills/csharp-best-practices/SKILL.md b/.skills/csharp-best-practices/SKILL.md new file mode 100644 index 00000000..27c67ad4 --- /dev/null +++ b/.skills/csharp-best-practices/SKILL.md @@ -0,0 +1,980 @@ +--- +name: csharp-best-practices +description: > + C# 14 / .NET 10 coding conventions, idiomatic patterns, and performance best practices + for the Minecraft Console Client codebase. Use when writing, reviewing, or modifying C# code. +--- + +# C# 14 / .NET 10 Best Practices + +Target: **.NET 10**, **C# 14**, nullable enabled. +Sources: [MS C# Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions) · [.NET Runtime Style](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md) · [C# 14 Proposals](https://github.com/dotnet/csharplang/blob/main/Language-Version-History.md) · [C# 13 Docs](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13) + +## Naming + +| Element | Style | Example | +|---|---|---| +| Type, method, property, const, enum member | PascalCase | `PacketHandler`, `MaxRetries`, `GameMode.Survival` | +| Interface | `I` + PascalCase | `IChatBot` | +| Private instance field | `_camelCase` | `_handler` | +| Private static field | `s_camelCase` | `s_defaultTimeout` | +| Thread-static field | `t_camelCase` | `t_cachedBuffer` | +| Local, parameter | camelCase | `packetId` | +| Type parameter | `T` + PascalCase | `TResult` | +| Namespace | PascalCase | `MinecraftClient.Protocol` | +| Async methods | Suffix `Async` | `ConnectAsync()`, `ReadPacketAsync()` | + +```csharp +// CORRECT: naming conventions +private readonly Dictionary _entities = new(); +private static readonly TimeSpan s_reconnectDelay = TimeSpan.FromSeconds(5); +public int PacketCount { get; private set; } +public async Task ConnectAsync(CancellationToken ct) { } +``` + +```csharp +// WRONG: naming violations +private Dictionary entities = new(); // missing _ +private static TimeSpan reconnectDelay; // missing s_ +public int packet_count { get; set; } // snake_case +public async Task Connect(CancellationToken ct) { } // missing Async suffix +``` + +## C# 14 Features + +### Extension Members (C# 14) + +Declare extension methods, properties, and operators inside `extension(...)` blocks. Replaces `this`-parameter pattern for new extensions. + +```csharp +// CORRECT: extension property + method (C# 14) +public static class EntityExtensions +{ + extension(Entity entity) + { + public bool IsAlive => entity.Health > 0; + public void Heal(int amount) => entity.Health = Math.Min(entity.Health + amount, 20); + } + extension(IEnumerable items) + { + public bool IsEmpty => !items.GetEnumerator().MoveNext(); + } +} +``` + +```csharp +// WRONG: classic extension method when C# 14 extension block is available +public static bool IsAlive(this Entity entity) => entity.Health > 0; +``` + +### `field` Keyword in Properties (C# 14) + +Access the auto-generated backing field without declaring it. Mix auto and full accessors. + +```csharp +// CORRECT: lazy init with field keyword +public string DisplayName => field ??= ComputeDisplayName(); + +// CORRECT: INotifyPropertyChanged pattern +public bool IsConnected +{ + get; + set + { + if (field == value) return; + field = value; + OnPropertyChanged(); + } +} +``` + +```csharp +// WRONG: manual backing field when field keyword suffices +private string? _displayName; +public string DisplayName => _displayName ??= ComputeDisplayName(); +``` + +### Null-Conditional Assignment (C# 14) + +Assign through `?.` — RHS is only evaluated when receiver is non-null. + +```csharp +// CORRECT: null-conditional assignment +player?.Health = 20; +connection?.OnDisconnect += HandleDisconnect; +inventory?[slot] = newItem; +``` + +```csharp +// WRONG: manual null check for simple assignment +if (player is not null) + player.Health = 20; +``` + +### Simple Lambda Parameters with Modifiers (C# 14) + +Omit types on lambda parameters while still applying modifiers. + +```csharp +// CORRECT: modifiers without explicit types +TryParse parse = (text, out result) => int.TryParse(text, out result); +ReadOnlySpan data = [1, 2, 3]; +ProcessSpan((scoped span) => span.Length); +``` + +```csharp +// WRONG: fully explicit types just for a modifier +TryParse parse = (string text, out int result) => int.TryParse(text, out result); +``` + +### First-Class Span Types (C# 14) + +Implicit conversions between `T[]`, `Span`, and `ReadOnlySpan` — no explicit cast needed. Extension methods on `ReadOnlySpan` apply to arrays and spans automatically. + +```csharp +// CORRECT: pass array where ReadOnlySpan is expected (C# 14) +int[] data = [1, 2, 3]; +bool found = data.StartsWith(1); // ReadOnlySpan extension resolved +ReadOnlySpan span = stackalloc byte[4]; +``` + +### Unbound Generics in `nameof` (C# 14) + +```csharp +// CORRECT: no need to pick a dummy type argument +string name = nameof(Dictionary<,>); // "Dictionary" +string prop = nameof(List<>.Count); // "Count" +``` + +```csharp +// WRONG: arbitrary type argument just to satisfy nameof +string name = nameof(Dictionary); +``` + +### Partial Events and Constructors (C# 14) + +Separate declaration from implementation for source-generator scenarios. + +```csharp +// CORRECT: partial constructor for source-gen interop +partial class ServerConnection +{ + partial ServerConnection(string host, int port); +} +partial class ServerConnection +{ + partial ServerConnection(string host, int port) { /* generated */ } +} +``` + +### `#:` Ignored Directives (C# 14) + +For file-based `dotnet run app.cs` programs — ignored by the compiler. + +```csharp +#!/usr/bin/dotnet run +#:package System.CommandLine@2.0.0-* +Console.WriteLine("Hello"); +``` + +## C# 13 Features + +### `Lock` Object (C# 13) + +Use `System.Threading.Lock` instead of `lock(obj)` on arbitrary objects. + +```csharp +// CORRECT: dedicated Lock type +private readonly Lock _lock = new(); +public void Enqueue(ChatMessage msg) { lock (_lock) _queue.Add(msg); } +``` + +```csharp +// WRONG: locking on an object reference +private readonly object _syncRoot = new(); +lock (_syncRoot) { } +``` + +### `params` Collections (C# 13) + +`params` now works with `ReadOnlySpan`, `Span`, `IEnumerable`, and other collection types. + +```csharp +// CORRECT: params span avoids array allocation +public void Log(params ReadOnlySpan messages) +{ + foreach (var msg in messages) Console.WriteLine(msg); +} +``` + +### Partial Properties (C# 13) + +```csharp +// CORRECT: partial property for source generators +partial class Config +{ + public partial string Host { get; set; } +} +partial class Config +{ + public partial string Host { get => _host; set => _host = value; } + private string _host = ""; +} +``` + +## C# 12 Features + +### Primary Constructors + +Use for simple parameter capture. Parameters are `camelCase`, mutable — assign to `readonly` fields when immutability matters. + +```csharp +// CORRECT: primary constructor captures dependencies +public class ChatLogger(string logFilePath, bool appendMode) : ChatBot +{ + private readonly StreamWriter _writer = new(logFilePath, appendMode); + public override void GetText(string text) => _writer.WriteLine(text); +} +``` + +```csharp +// WRONG: verbose constructor boilerplate for simple capture +public class ChatLogger : ChatBot +{ + private readonly StreamWriter _writer; + public ChatLogger(string logFilePath, bool appendMode) + { + _writer = new StreamWriter(logFilePath, appendMode); + } + public override void GetText(string text) => _writer.WriteLine(text); +} +``` + +### Collection Expressions + +Use `[...]` and `..` spread for arrays, lists, spans. + +```csharp +// CORRECT: collection expressions (C# 12) +int[] ids = [1, 2, 3]; +List names = ["Steve", "Alex"]; +ReadOnlySpan header = [0xFE, 0x01]; // no heap alloc +int[] combined = [..firstArray, ..secondArray, 42]; +IReadOnlyList empty = []; +``` + +```csharp +// WRONG: verbose initialization +int[] ids = new int[] { 1, 2, 3 }; +var names = new List { "Steve", "Alex" }; +ReadOnlySpan header = new byte[] { 0xFE, 0x01 }; // allocates +var combined = firstArray.Concat(secondArray).Append(42).ToArray(); +``` + +### Type Aliases + +```csharp +// CORRECT: alias complex types for readability +using Coordinate = (int X, int Y, int Z); +using PacketMap = System.Collections.Generic.Dictionary>; +``` + +### Default Lambda Parameters + +```csharp +// CORRECT: C# 12 +var greet = (string name, string prefix = "Player") => $"{prefix} {name}"; +``` + +## Modern Syntax (C# 10–14) + +### File-Scoped Namespaces + +```csharp +// CORRECT: file-scoped namespace — one per file, less nesting +namespace MinecraftClient.ChatBots; + +public class MyBot : ChatBot { } +``` + +```csharp +// WRONG: block-scoped namespace adds unnecessary nesting +namespace MinecraftClient.ChatBots +{ + public class MyBot : ChatBot { } +} +``` + +### Target-Typed `new` + +Use when the type is obvious from the left-hand side. + +```csharp +// CORRECT: target-typed new +private readonly Dictionary _scores = new(); +List entities = new(capacity: 256); +``` + +```csharp +// WRONG: redundant type name +private readonly Dictionary _scores = new Dictionary(); +``` + +### Pattern Matching + +Prefer patterns over type-casting chains and complex boolean logic. + +```csharp +// CORRECT: is-pattern with declaration and property patterns +if (entity is Player { Health: > 0 } player) + SendMessage($"{player.Name} is alive"); +``` + +```csharp +// WRONG: manual cast and multi-step check +if (entity is Player) +{ + var player = (Player)entity; + if (player.Health > 0) + SendMessage($"{player.Name} is alive"); +} +``` + +```csharp +// CORRECT: switch expression +public string GetStatusLabel(GameMode mode) => mode switch +{ + GameMode.Survival => "Survival", + GameMode.Creative => "Creative", + GameMode.Adventure => "Adventure", + GameMode.Spectator => "Spectator", + _ => throw new ArgumentOutOfRangeException(nameof(mode)) +}; +``` + +```csharp +// WRONG: switch statement with returns +public string GetStatusLabel(GameMode mode) +{ + switch (mode) + { + case GameMode.Survival: return "Survival"; + case GameMode.Creative: return "Creative"; + default: throw new ArgumentOutOfRangeException(nameof(mode)); + } +} +``` + +```csharp +// CORRECT: property patterns for compound conditions +if (response is { StatusCode: >= 200 and < 300, Content.Length: > 0 }) + ProcessResponse(response); +``` + +```csharp +// WRONG: multiple chained conditions +if (response != null && response.StatusCode >= 200 + && response.StatusCode < 300 && response.Content != null + && response.Content.Length > 0) + ProcessResponse(response); +``` + +```csharp +// CORRECT: relational, logical, and list patterns +if (health is > 0 and <= 6) LogToConsole("Low health!"); +if (args is [var command, var target, ..]) ProcessCommand(command, target); +``` + +### Raw String Literals + +Use for JSON, regex, multi-line strings. + +```csharp +// CORRECT: raw string literal +string json = """ + { "username": "Steve", "action": "connect" } + """; +string pattern = """<\w+>"""; +``` + +```csharp +// WRONG: escaped quotes +string json = "{ \"username\": \"Steve\", \"action\": \"connect\" }"; +``` + +### Records + +Use `record` for immutable data carriers and DTOs. Use `record struct` for small value types. + +```csharp +// CORRECT: record for data carrier +public record PlayerInfo(string Name, Guid Uuid, GameMode Mode); +public record struct ChunkCoord(int X, int Z); +var updated = info with { Mode = GameMode.Creative }; +``` + +```csharp +// WRONG: full class for a simple data carrier +public class PlayerInfo +{ + public string Name { get; set; } = string.Empty; + public Guid Uuid { get; set; } + public GameMode Mode { get; set; } +} +``` + +```csharp +// CORRECT: compact constructor for record validation +public record OrderItem(string ProductId, int Quantity, decimal UnitPrice) +{ + public OrderItem + { + ArgumentException.ThrowIfNullOrWhiteSpace(ProductId); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(Quantity); + ArgumentOutOfRangeException.ThrowIfNegative(UnitPrice); + } +} +``` + +### Required Members + +```csharp +// CORRECT: required + init enforces initialization without constructor boilerplate +public class ServerConfig +{ + public required string Host { get; init; } + public required int Port { get; init; } + public string? Password { get; init; } +} +var config = new ServerConfig { Host = "mc.example.com", Port = 25565 }; +``` + +## Nullable Reference Types + +Project has nullable enabled. Follow these rules: + +```csharp +// CORRECT: guard at API boundaries with .NET 8 throw helpers +public void Connect(string host, IProtocolHandler handler) +{ + ArgumentNullException.ThrowIfNull(handler); + ArgumentException.ThrowIfNullOrEmpty(host); +} +``` + +```csharp +// WRONG: manual null checks +if (handler == null) throw new ArgumentNullException(nameof(handler)); +if (string.IsNullOrWhiteSpace(host)) + throw new ArgumentException("Host is required", nameof(host)); +``` + +```csharp +// CORRECT: 'is not null' pattern +if (currentPlayer is not null) + currentPlayer.Update(); +``` + +```csharp +// WRONG: comparison operator for null check +if (currentPlayer != null) + currentPlayer.Update(); +``` + +```csharp +// CORRECT: explicit nullable handling +public Entity? FindEntity(int id) +{ + return _entities.TryGetValue(id, out var entity) ? entity : null; +} + +// CORRECT: null-coalescing / null-conditional +string name = player?.CustomName ?? player?.Name ?? "Unknown"; + +// CORRECT: null-forgiving only when proven safe (after ThrowIfNull or equivalent) +string val = GetRequiredValue()!; + +// CORRECT: annotate return values +[return: MaybeNull] +public T Find(Predicate match) { } + +[MemberNotNull(nameof(_connection))] +private void EnsureConnected() { } +``` + +```csharp +// WRONG: hiding nullability with null-forgiving +public string GetName(Player? player) +{ + return player!.Name; // hides potential NullReferenceException +} +``` + +## Async / Await + +```csharp +// CORRECT: propagate CancellationToken through every async I/O call +public async Task FetchDataAsync(Uri uri, CancellationToken ct = default) +{ + using var response = await _httpClient.GetAsync(uri, ct); + return await response.Content.ReadAsStringAsync(ct); +} +``` + +```csharp +// WRONG: CancellationToken not passed downstream +public async Task FetchDataAsync(Uri uri) +{ + using var response = await _httpClient.GetAsync(uri, default); + return await response.Content.ReadAsStringAsync(default); +} +``` + +```csharp +// CORRECT: ValueTask when result is often available synchronously +public ValueTask GetCachedCountAsync() +{ + if (_cache.TryGetValue("count", out int count)) + return ValueTask.FromResult(count); + return new ValueTask(LoadCountFromDbAsync()); +} +``` + +```csharp +// WRONG: Task allocates unnecessarily when result is cached +public async Task GetCachedCountAsync() +{ + if (_cache.TryGetValue("count", out int count)) + return count; // allocates a Task + return await LoadCountFromDbAsync(); +} +``` + +```csharp +// CORRECT: async Task for async event handlers +public async Task HandleEventAsync(GameEvent e, CancellationToken ct) +{ + await notificationService.SendAsync(e.PlayerId, ct); +} +``` + +```csharp +// WRONG: async void — exceptions are unobservable, cannot be awaited +public async void HandleEvent(GameEvent e) +{ + await notificationService.SendAsync(e.PlayerId, default); +} +``` + +```csharp +// CORRECT: await the result +var packet = await reader.ReadPacketAsync(ct); +``` + +```csharp +// WRONG: .Result / .Wait() causes deadlocks +var packet = reader.ReadPacketAsync(ct).Result; +var packet2 = reader.ReadPacketAsync(ct).GetAwaiter().GetResult(); +``` + +```csharp +// CORRECT: ConfigureAwait(false) in library code +var data = await stream.ReadAsync(buffer, ct).ConfigureAwait(false); + +// CORRECT: IAsyncEnumerable for streaming +public async IAsyncEnumerable ReadChatStreamAsync( + [EnumeratorCancellation] CancellationToken ct = default) +{ + while (!ct.IsCancellationRequested) + yield return await _reader.ReadNextAsync(ct); +} + +// CORRECT: await using for async disposal +await using var conn = new McConnection(host, port); +``` + +## LINQ + +### Prefer Method Syntax for Most Operations + +```csharp +// CORRECT: method syntax for common operations +var onlinePlayers = players + .Where(p => p.IsOnline) + .OrderBy(p => p.Name) + .Select(p => new PlayerListItem(p.Id, p.Name)) + .ToList(); +``` + +```csharp +// AVOID: query syntax for simple operations +var onlinePlayers = ( + from p in players + where p.IsOnline + orderby p.Name + select new PlayerListItem(p.Id, p.Name) +).ToList(); +``` + +### Use Query Syntax for Joins + +```csharp +// CORRECT: query syntax makes joins readable +var results = + from entity in entities + join player in players on entity.OwnerId equals player.Id + where entity.Health > 0 + select new { entity.Name, player.Name }; +``` + +```csharp +// AVOID: method syntax for complex joins is hard to read +var results = entities + .Join(players, + e => e.OwnerId, + p => p.Id, + (e, p) => new { e, p }) + .Where(x => x.e.Health > 0) + .Select(x => new { x.e.Name, PlayerName = x.p.Name }); +``` + +### Materialize to Avoid Multiple Enumeration + +```csharp +// CORRECT: materialize once, iterate many times +var online = players.Where(p => p.IsOnline).ToList(); +Console.WriteLine(online.Count); +foreach (var p in online) { } +``` + +```csharp +// WRONG: enumerates the query twice +var filtered = players.Where(p => p.IsOnline); +Console.WriteLine(filtered.Count()); // first enumeration +foreach (var p in filtered) { } // second enumeration +``` + +### Use Any() Over Count() > 0 + +```csharp +// CORRECT: short-circuits on first match +if (entities.Any(e => e.IsHostile)) + TriggerAlert(); +``` + +```csharp +// WRONG: counts the entire collection +if (entities.Count(e => e.IsHostile) > 0) + TriggerAlert(); +``` + +### Prefer FirstOrDefault with Null Handling + +```csharp +// CORRECT: explicit null handling +var target = players.FirstOrDefault(p => p.Name == name) + ?? throw new InvalidOperationException($"Player '{name}' not found"); +``` + +### TryGetNonEnumeratedCount + +```csharp +// CORRECT: avoid full enumeration just to get count (.NET 6+) +if (source.TryGetNonEnumeratedCount(out int count)) + buffer = new Entity[count]; +``` + +### Avoid LINQ in Hot Paths + +```csharp +// CORRECT: manual loop with Span in performance-critical code +Span data = stackalloc byte[256]; +int found = 0; +for (int i = 0; i < data.Length; i++) + if (data[i] == target) found++; +``` + +```csharp +// AVOID: LINQ allocates enumerators and delegates on hot paths +int found = data.ToArray().Count(b => b == target); +``` + +## Performance (.NET 8+) + +### Span\ / Memory\ + +```csharp +// CORRECT: zero-allocation slicing +ReadOnlySpan command = input.AsSpan()[1..]; // skip '/' + +// CORRECT: stack-allocated parsing +public static int ParseVarInt(ReadOnlySpan data, out int bytesRead) +{ + int result = 0; bytesRead = 0; byte cur; + do { cur = data[bytesRead]; result |= (cur & 0x7F) << (bytesRead * 7); bytesRead++; } + while ((cur & 0x80) != 0); + return result; +} +``` + +### FrozenDictionary / FrozenSet (.NET 8) + +Build once, read many — ~50% faster lookups than Dictionary. + +```csharp +// CORRECT: FrozenDictionary for read-heavy lookup tables (palettes, protocol maps) +using System.Collections.Frozen; +private static readonly FrozenDictionary s_blockNames = + new Dictionary { [0] = "air", [1] = "stone" }.ToFrozenDictionary(); +``` + +### SearchValues\ (.NET 8) + +Hardware-accelerated set search. + +```csharp +// CORRECT: precompute once, scan with SIMD +private static readonly SearchValues s_separators = SearchValues.Create(" \t\n\r,;"); +int idx = input.AsSpan().IndexOfAny(s_separators); +``` + +### CompositeFormat (.NET 8) + +Parse format string once, reuse. + +```csharp +// CORRECT: avoids re-parsing the format string each call +private static readonly CompositeFormat s_logFmt = CompositeFormat.Parse("[{0:HH:mm:ss}] {1}: {2}"); +string msg = string.Format(CultureInfo.InvariantCulture, s_logFmt, DateTime.Now, player, text); +``` + +### ArrayPool / stackalloc + +```csharp +// CORRECT: rent from pool for temporary buffers +byte[] buf = ArrayPool.Shared.Rent(4096); +try { int n = stream.Read(buf.AsSpan(0, 4096)); ProcessPacket(buf.AsSpan(0, n)); } +finally { ArrayPool.Shared.Return(buf); } + +// CORRECT: stackalloc for small, fixed-size buffers (< 512 bytes) +Span header = stackalloc byte[5]; +``` + +## String Handling + +```csharp +// CORRECT: explicit StringComparison — always +bool match = name.Equals("Steve", StringComparison.OrdinalIgnoreCase); +int idx = text.IndexOf("hello", StringComparison.Ordinal); +``` + +```csharp +// WRONG: allocates a lowered copy +bool match = name.ToLower() == "steve"; +``` + +```csharp +// CORRECT: string.Create for perf-critical formatting +string hex = string.Create(data.Length * 2, data, static (span, bytes) => +{ + for (int i = 0; i < bytes.Length; i++) + bytes[i].TryFormat(span[(i * 2)..], out _, "X2"); +}); + +// CORRECT: StringBuilder for loops +var sb = new StringBuilder(256); +foreach (var item in inventory) + sb.Append(item.Name).Append(" x").Append(item.Count).AppendLine(); +``` + +```csharp +// WRONG: O(n²) string concatenation in loop +string combined = ""; +foreach (var s in items) combined += s + ", "; +``` + +## Collections — Choosing the Right Type + +| Scenario | Type | Notes | +|---|---|---| +| General key-value | `Dictionary` | O(1) lookup | +| Build once, read many | `FrozenDictionary` | .NET 8+; faster reads | +| Thread-safe | `ConcurrentDictionary` | Lock-free reads | +| Immutable snapshots | `ImmutableDictionary` | Persistent structure | +| Membership test | `HashSet` / `FrozenSet` | FrozenSet for static | +| Priority queue | `PriorityQueue` | .NET 6+ | +| Synchronization | `System.Threading.Lock` | C# 13; prefer over `lock(obj)` | +| Producer-consumer | `Channel` | Over `BlockingCollection` | +| Temp buffer | `ArrayPool` / `stackalloc` | Zero/low alloc | + +## Error Handling + +```csharp +// CORRECT: Try* pattern for expected failures +if (int.TryParse(input, out int value)) ProcessValue(value); +if (_registry.TryGetValue(packetId, out var handler)) handler.Invoke(data); +``` + +```csharp +// WRONG: using exceptions for control flow +try { return dict[key]; } +catch (KeyNotFoundException) { return null; } // use TryGetValue +``` + +```csharp +// CORRECT: exception filters (catch-when) +try { await ConnectAsync(ct); } +catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionRefused) +{ + LogToConsole("Connection refused, retrying..."); +} +``` + +```csharp +// CORRECT: throw helpers (smaller IL, better inlining) +ArgumentNullException.ThrowIfNull(handler); +ArgumentOutOfRangeException.ThrowIfNegative(timeout); +ArgumentOutOfRangeException.ThrowIfGreaterThan(timeout, MaxTimeout); +ObjectDisposedException.ThrowIf(_disposed, this); +``` + +```csharp +// WRONG: generic exceptions +throw new Exception($"Entity {id} not found"); +``` + +```csharp +// CORRECT: specific, meaningful exception types +throw new EntityNotFoundException(id); +// or use null-coalescing with throw +return await FindEntityAsync(id, ct) + ?? throw new EntityNotFoundException(id); +``` + +```csharp +// AVOID: catching Exception without filtering +try { DoWork(); } +catch (Exception) { /* swallowed */ } +``` + +## Warning Suppression + +```csharp +// CORRECT: fix the warning by handling null properly +public string GetDisplayName(Player? player) +{ + return player?.DisplayName ?? "Unknown"; +} +``` + +```csharp +// WRONG: suppressing nullable warning with pragma +#pragma warning disable CS8602 +public string GetDisplayName(Player? player) +{ + return player.DisplayName; // NullReferenceException at runtime +} +#pragma warning restore CS8602 +``` + +```csharp +// WRONG: suppressing with attribute +[SuppressMessage("Usage", "CA1062:Validate arguments of public methods")] +public void Process(Packet packet) +{ + // missing null check +} +``` + +Project-wide `.editorconfig` is the only acceptable place for warning policy: + +```text +# .editorconfig - project-wide policy decisions only +dotnet_diagnostic.CA2007.severity = none +``` + +## Resource Management + +```csharp +// CORRECT: using declaration — disposed at end of scope +using var stream = new FileStream(path, FileMode.Open); +using var reader = new StreamReader(stream); + +// CORRECT: IAsyncDisposable +await using var conn = await CreateConnectionAsync(); +``` + +```csharp +// CORRECT: Dispose pattern +public class PacketReader : IDisposable +{ + private Stream? _stream; + private bool _disposed; + public void Dispose() + { + if (_disposed) return; + _stream?.Dispose(); _stream = null; _disposed = true; + } +} +``` + +## Security + +```csharp +// CORRECT: secure random for tokens +byte[] token = RandomNumberGenerator.GetBytes(32); + +// CORRECT: constant-time comparison for secrets +bool valid = CryptographicOperations.FixedTimeEquals(expected, actual); + +// CORRECT: validate external input +if (Uri.TryCreate(userInput, UriKind.Absolute, out var uri) + && uri.Scheme is "http" or "https") + await FetchAsync(uri); +``` + +```csharp +// WRONG: predictable random for security-sensitive values +var rng = new Random(); + +// WRONG: timing side-channel on secret comparison +bool eq = secret1.SequenceEqual(secret2); +``` + +## Miscellaneous Idioms + +```csharp +// CORRECT: var when type is obvious from RHS +var entities = new Dictionary(); +var timer = Stopwatch.StartNew(); + +// CORRECT: explicit type when var would be unclear +Stream responseStream = GetResponse(); +int count = items.Count; + +// CORRECT: expression-bodied members for one-liners +public override string ToString() => $"[{X}, {Y}, {Z}]"; +public bool IsAlive => Health > 0; + +// CORRECT: discards for unused values +_ = int.TryParse(s, out int result); +(_, int y, _) = GetCoordinates(); + +// CORRECT: nameof for resilient refactoring (unbound generics in C# 14) +throw new ArgumentException("Invalid value", nameof(packetId)); +LogToConsole($"{nameof(AutoEat)}: eating {item.Name}"); +string typeName = nameof(Dictionary<,>); // "Dictionary" + +// CORRECT: static lambdas prevent accidental closure allocations +list.Sort(static (a, b) => a.Id.CompareTo(b.Id)); + +// CORRECT: index/range operators +var last = items[^1]; +var slice = data[3..^1]; + +// CORRECT: tuple deconstruction +var (x, y, z) = GetPosition(); + +// CORRECT: string interpolation with alignment and format specifiers +LogToConsole($"Health: {health,6:F1} | Hunger: {hunger,6:F1}"); +``` diff --git a/.skills/csharp-dotnet-cli-optimization/SKILL.md b/.skills/csharp-dotnet-cli-optimization/SKILL.md new file mode 100644 index 00000000..7691da85 --- /dev/null +++ b/.skills/csharp-dotnet-cli-optimization/SKILL.md @@ -0,0 +1,166 @@ +--- +name: csharp-dotnet-cli-optimization +description: >- + Use when diagnosing or optimizing generic C#/.NET performance, GC pressure, + allocations, heap or stack usage, LINQ overhead, boxing, Span/Memory, + stackalloc, pooling, or hot-path code with CLI-first tools such as + dotnet-counters, dotnet-trace, dotnet-stack, dotnet-gcdump, dotnet-dump, or + BenchmarkDotNet. +metadata: + category: technique + triggers: + - dotnet-counters + - dotnet-trace + - dotnet-dump + - dotnet-gcdump + - dotnet-stack + - benchmarkdotnet + - allocations + - gc pressure + - memory leak + - hot path + - linq + - stackalloc + - span + - memory + - boxing + - heap + - stack + - latency + - throughput + - slow + - hang + - deadlock +--- + +# C#/.NET CLI Optimization + +CLI-first guidance for generic C# 14 / .NET 10 performance work. +Use the references on demand: + +- Read [references/memory-model-gc.md](references/memory-model-gc.md) for stack vs heap, generations, LOH, pinning, server vs workstation GC, and GC tuning limits. +- Read [references/code-patterns.md](references/code-patterns.md) for LINQ, Span/Memory, stackalloc, structs, boxing, pooling, strings, and analyzer-backed code patterns. +- Read [references/README.md](references/README.md) for dated sources and freshness notes. + +## When to Use + +- A .NET process is slow, allocation-heavy, CPU-heavy, or memory-hungry +- A live process appears stuck, hung, or deadlocked +- The user asks how heap, stack, GC, boxing, or LINQ overhead actually works in .NET +- The user wants concrete bad vs good code patterns after measurement has identified a hot path +- The task needs a decision between counters, traces, stacks, GC dumps, dumps, or a benchmark + +**NOT for:** +- ASP.NET Core, EF Core, MAUI, Orleans, Unity, Avalonia, WPF, WinForms, Blazor, or other framework-specific playbooks +- Visual Studio, Rider, VS Code, PerfView, speedscope, or any GUI-first workflow +- speculative rewrites such as "replace everything with Span" before measurement + +## Iron Rule + +ALWAYS measure first, change second, and re-measure third. + +NEVER claim an optimization without before/after evidence from the same scenario. + +| Rationalization | Reality | +|---|---| +| "This is obviously slow" | The runtime, JIT, and libraries often invalidate intuition. | +| "struct means stack" | Value types are stored inline. They are not "always on the stack". | +| "All LINQ is slow" | .NET 10 improved many LINQ paths. Measure before rewriting. | +| "GC.Collect will fix it" | Forced collection usually treats symptoms, not cause. | + +## Investigation Order + +1. Use `dotnet-counters` for live triage. +2. If the process is stuck, capture `dotnet-stack` immediately. +3. If CPU or allocation hot paths matter, collect `dotnet-trace`. +4. If heap growth matters more than call paths, collect `dotnet-gcdump`. +5. If you need SOS heap inspection or a postmortem, collect `dotnet-dump`. +6. Only after live evidence points to a candidate routine, apply patterns from the reference docs. +7. If the change is truly local and isolated, use BenchmarkDotNet to compare implementations. +8. Re-run the original live capture to prove the real workload improved. + +## Which Reference to Load + +| User question | Read first | +|---|---| +| "How do stack and heap really work in .NET?" | `references/memory-model-gc.md` | +| "Why is GC pausing or why is LOH churn hurting us?" | `references/memory-model-gc.md` | +| "How should I optimize this LINQ?" | `references/code-patterns.md` | +| "Can I move this to the stack with stackalloc or Span?" | `references/code-patterns.md` | +| "Should this be a struct, ref struct, readonly struct, or class?" | `references/code-patterns.md` and `references/memory-model-gc.md` | +| "Why is this boxing?" | `references/code-patterns.md` | + +## Tool Selection + +| Question | Tool | What it answers | Typical next step | +|---|---|---|---| +| Is the live process allocating, GCing, or saturating CPU? | `dotnet-counters` | Live counters and trend direction | Capture a trace or GC dump if suspicious | +| Is the process hung or deadlocked right now? | `dotnet-stack` | Current managed stack snapshot | Collect a dump if you need deeper postmortem evidence | +| Which call paths consume CPU or allocate heavily? | `dotnet-trace` | Sampled execution and runtime events | Confirm hot paths, then isolate code | +| Which object types dominate managed heap usage? | `dotnet-gcdump` | Heap composition and type totals | Decide whether to redesign lifetimes or collect a full dump | +| Do I need SOS heap inspection or thread state? | `dotnet-dump` | Full dump plus CLI analysis | Run `analyze -c` commands | +| Did a code change improve one isolated routine? | BenchmarkDotNet | Reproducible microbenchmark comparison | Re-run live diagnostics in the real scenario | + +## Pattern Guardrails + +- Do not answer "put it on the stack" as a blanket goal. Explain lifetime, copies, boxing, and escape rules instead. +- Do not suggest `stackalloc` for unbounded sizes, large buffers, or loop-carried allocations. +- Do not recommend `Span` for data that must cross `await`, escape to the heap, or live in object fields. Switch to `Memory` or `ReadOnlyMemory` for that. +- Do not recommend converting every `class` to a `struct`. Large, mutable, identity-bearing, or frequently boxed types often get worse. +- Do not blanket-rewrite LINQ to loops. Use analyzer-backed fixes first, and remember .NET 10 substantially improved many LINQ paths. +- Do not recommend pooling without ownership rules. Returned pooled arrays must not be reused by the caller. +- Do not recommend `GC.Collect()` except for rare, justified lifecycle boundaries, and only with measurement. + +## Analyzer Radar + +When performance diagnostics point to code patterns rather than runtime configuration, consult the current performance analyzers, especially: + +- `CA1826`, `CA1827`, `CA1829`, `CA1836`, `CA1851`, `CA1860` for LINQ and enumeration +- `CA1845`, `CA1846`, `CA1858` for string and span-friendly APIs +- `CA1834`, `CA1865-CA1867` for `StringBuilder` char overloads +- `CA1870` for cached `SearchValues` + +These rules are clues, not goals. Apply them where the measured hot path justifies it. + +## Minimal Commands + +```bash +dnx dotnet-counters monitor --process-id +dotnet-counters monitor -p --counters System.Runtime +dotnet-stack report -p +dotnet-trace collect -p --duration 00:00:30 +dotnet-trace report topN +dotnet-gcdump collect -p +dotnet-gcdump report +dotnet-dump collect -p --type Heap +dotnet-dump analyze -c "dumpheap -stat" -c "exit" +``` + +Minimal BenchmarkDotNet pattern: + +```csharp +using BenchmarkDotNet.Attributes; + +[MemoryDiagnoser] +public class CandidateBench +{ + [Benchmark(Baseline = true)] + public int Original() => OriginalImpl(); + + [Benchmark] + public int Candidate() => CandidateImpl(); +} +``` + +```bash +dotnet run -c Release +``` + +## Output + +When using this skill, report: + +- the measured symptom and the evidence used to identify it +- the chosen tool or code pattern and why it fits this bottleneck +- the relevant tradeoff, such as allocation vs copy cost, deferred vs eager execution, or stack vs pool +- the before/after result, or say explicitly if the recommendation is still unverified diff --git a/.skills/csharp-dotnet-cli-optimization/references/README.md b/.skills/csharp-dotnet-cli-optimization/references/README.md new file mode 100644 index 00000000..b1c634b7 --- /dev/null +++ b/.skills/csharp-dotnet-cli-optimization/references/README.md @@ -0,0 +1,68 @@ +--- +description: Dated source ledger for csharp-dotnet-cli-optimization. +metadata: + tags: [sources, diagnostics, gc, linq, span, stackalloc, boxing] +--- + +# Sources + +## Current primary sources + +- [dotnet-counters](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters), Microsoft Learn, updated `2025-10-02` + - Canonical CLI docs for live counters, `monitor`, `collect`, and `dnx` one-shot execution on .NET 10.0.100+. +- [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace), Microsoft Learn, updated `2026-03-20` + - Canonical CLI docs for `collect`, `report`, and the preview `collect-linux` path plus its limits. +- [dotnet-dump](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dump), Microsoft Learn, updated `2026-03-04` + - Canonical CLI docs for dump collection, dump types, and `analyze -c`. +- [dotnet-gcdump](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-gcdump), Microsoft Learn, updated `2025-12-17` + - Canonical CLI docs for GC dump collection, `report`, and the induced full Gen 2 GC caveat. +- [Fundamentals of garbage collection](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals), Microsoft Learn, updated `2025-10-22` + - Current official overview of generations, allocation, and managed heap behavior. +- [Runtime configuration options for garbage collection](https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector), Microsoft Learn, updated `2025-11-22` + - Current official source for server vs workstation GC, background GC, heap limits, LOH threshold, and modern GC configuration behavior. +- [stackalloc expression](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/stackalloc), Microsoft Learn, updated `2026-01-24` + - Current official guidance for stack allocation limits, loop avoidance, initialization, and Span-based usage. +- [ref struct types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/ref-struct), Microsoft Learn, updated `2026-01-20` + - Current official guidance for stack-only semantics and escape restrictions. +- [Structure types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct), Microsoft Learn, updated `2026-01-14` + - Current official source for readonly structs, pass-by-reference guidance, and boxing conversions. +- [Value types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types), Microsoft Learn, updated `2026-01-20` + - Current official source for copy semantics and inline storage behavior. +- [Boxing and Unboxing](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing), Microsoft Learn, updated `2025-10-13` + - Current official source for boxing semantics and cost. +- [Memory and Span usage guidelines](https://learn.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines), Microsoft Learn, updated `2025-04-11` + - Current official guidance for choosing `Span` vs `Memory` and ownership rules. +- [Lambda expressions](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions), Microsoft Learn, updated `2026-01-24` + - Current official source for capture semantics and `static` lambdas. +- [What's new in C# 14](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14), Microsoft Learn, updated `2025-11-19` + - Current official confirmation of first-class span conversions in C# 14. +- [Performance rules](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings), Microsoft Learn, updated `2025-10-29` + - Current official index of analyzer-backed performance rules, including `CA1870`. +- [Performance Improvements in .NET 10](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-10/), Stephen Toub, published `2025-09-10` + - High-trust expert source showing real .NET 10 runtime and LINQ improvements. Use it to avoid stale folklore such as "all LINQ is slow". + +## Specific analyzer pages used for code-pattern guidance + +- [CA1827](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1827), updated `2023-11-14` +- [CA1845](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1845), updated `2024-11-12` +- [CA1846](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846), updated `2023-12-16` +- [CA1851](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1851), updated `2023-11-14` +- [CA1858](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1858), current official analyzer page +- [CA1860](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1860), current official analyzer page + +## Older but still canonical sources used cautiously + +- [Reduce memory allocations using new C# features](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/performance/), Microsoft Learn, updated `2023-10-17` + - Still useful for `ref`, `in`, readonly struct, and copy-avoidance guidance, but older than the core 2025-2026 docs. +- [Intermediate materialization](https://learn.microsoft.com/en-us/dotnet/standard/linq/intermediate-materialization), Microsoft Learn, updated `2022-09-02` + - Still canonical for LINQ materialization semantics. +- [Deferred execution and lazy evaluation](https://learn.microsoft.com/en-us/dotnet/standard/linq/deferred-execution-lazy-evaluation), Microsoft Learn, updated `2022-09-29` + - Still canonical for LINQ deferred-execution semantics. +- [dotnet-stack](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-stack), Microsoft Learn, updated `2023-03-14` + - Used only for narrow stack-snapshot guidance because the page is stale compared to the other diagnostics docs. + +## Explicit exclusions + +- Framework-specific tutorials were intentionally excluded. +- GUI-first analysis flows were intentionally excluded. +- MCC-specific paths, code, and hot-path examples were intentionally excluded. diff --git a/.skills/csharp-dotnet-cli-optimization/references/code-patterns.md b/.skills/csharp-dotnet-cli-optimization/references/code-patterns.md new file mode 100644 index 00000000..4e18edea --- /dev/null +++ b/.skills/csharp-dotnet-cli-optimization/references/code-patterns.md @@ -0,0 +1,381 @@ +--- +description: Code-level performance patterns for csharp-dotnet-cli-optimization. +metadata: + tags: [linq, span, stackalloc, boxing, pooling, strings, analyzers] +--- + +# Code Patterns + +Use this reference after a profile or benchmark identifies a hot path. Do not apply these patterns speculatively. + +## Table Of Contents + +- [LINQ And Enumeration](#linq-and-enumeration) +- [Stack Allocation, Span, And Memory](#stack-allocation-span-and-memory) +- [Structs, Boxing, And Copies](#structs-boxing-and-copies) +- [Buffer Reuse And Advanced Helpers](#buffer-reuse-and-advanced-helpers) +- [Strings](#strings) +- [What Not To Suggest](#what-not-to-suggest) + +## LINQ And Enumeration + +### Property or indexer over LINQ when the concrete collection is known + +Wrong: + +```csharp +if (items.Count() > 0) +{ + return items.First(); +} +``` + +Better: + +```csharp +if (items.Count > 0) +{ + return items[0]; +} +``` + +Use `Count`, `Length`, `IsEmpty`, or an indexer when you already have a concrete collection with that API. Relevant analyzers: `CA1826`, `CA1829`, `CA1836`, `CA1860`. + +### `Any()` over `Count() > 0` when all you know is `IEnumerable` + +Wrong: + +```csharp +if (source.Count() != 0) +{ + Process(source); +} +``` + +Better: + +```csharp +if (source.Any()) +{ + Process(source); +} +``` + +Relevant analyzer: `CA1827`. + +### Avoid multiple enumeration of deferred queries + +Wrong: + +```csharp +var query = source.Where(Filter); +return query.Count() + query.Last().Id; +``` + +Better: + +```csharp +var materialized = source.Where(Filter).ToArray(); +return materialized.Length + materialized[^1].Id; +``` + +Materialize once only when you truly need multiple passes or random access and can afford the extra memory. Relevant analyzer: `CA1851`. + +### Avoid premature materialization + +Wrong: + +```csharp +var projected = source.ToList().Select(Map); +``` + +Better: + +```csharp +var projected = source.Select(Map); +``` + +Keep deferred execution unless you need a snapshot, repeated traversal, indexing, or a boundary between expensive stages. + +### Do not blanket-rewrite LINQ to loops + +- .NET 10 improved many LINQ operations substantially. +- Start with analyzer-backed fixes and measurement. +- Replace LINQ with hand-written loops only when a benchmark or trace shows that the remaining cost matters. + +### Use `TryGetNonEnumeratedCount` when count is optional + +```csharp +if (source.TryGetNonEnumeratedCount(out int count)) +{ + LogCount(count); +} +``` + +This avoids forcing enumeration when the underlying type already knows its size. + +## Stack Allocation, Span, And Memory + +### `stackalloc` only for small, bounded, temporary buffers + +Wrong: + +```csharp +for (int i = 0; i < items.Length; i++) +{ + Span buffer = stackalloc byte[4096]; + Use(buffer); +} +``` + +Better: + +```csharp +Span buffer = stackalloc byte[256]; +for (int i = 0; i < items.Length; i++) +{ + buffer.Clear(); + Use(buffer); +} +``` + +Guidance: + +- keep sizes conservative +- avoid `stackalloc` inside loops +- initialize the memory before use +- fall back to heap or pooling for larger or variable-sized buffers + +### Prefer span-based APIs over substring copies + +Wrong: + +```csharp +int.TryParse(line.Substring(7), out int value); +``` + +Better: + +```csharp +int.TryParse(line.AsSpan(7), out int value); +``` + +Relevant analyzers: `CA1845`, `CA1846`. + +### Use `Span` for sync work and `Memory` for async or heap-stored state + +Wrong: + +```csharp +// Wrong: Span cannot cross await safely. +public async Task ReadAsync(Span buffer) +{ + await socket.ReceiveAsync(buffer); + return buffer[0]; +} +``` + +Better: + +```csharp +public async Task ReadAsync(Memory buffer) +{ + await socket.ReceiveAsync(buffer); + return buffer.Span[0]; +} +``` + +`Span` is stack-only. If the lifetime crosses `await`, callbacks, or object storage, move to `Memory`. + +### `ref struct` is for stack-bound wrappers, not a general optimization badge + +- Use `ref struct` when the type itself contains spans or must never escape to the heap. +- Do not use it if you need arrays of that type, boxing, interface conversions, or heap fields. + +## Structs, Boxing, And Copies + +### Use `readonly struct` or `readonly record struct` for small immutable values + +Wrong: + +```csharp +public struct Measurement +{ + public double A; + public double B; + public void Normalize() => A /= B; +} +``` + +Better: + +```csharp +public readonly record struct Measurement(double A, double B); +``` + +Prefer value types for small, copyable, data-only values. Avoid large, mutable structs. + +### Pass large structs by `in` + +Wrong: + +```csharp +double Distance(Vector4 value) => value.X + value.Y + value.Z + value.W; +``` + +Better: + +```csharp +double Distance(in Vector4 value) => value.X + value.Y + value.Z + value.W; +``` + +This avoids copying large struct values on each call. + +### Avoid boxing in hot paths + +Wrong: + +```csharp +object boxed = valueStruct; +``` + +Wrong: + +```csharp +IFormattable f = valueStruct; +``` + +Better: + +```csharp +Use(in valueStruct); +``` + +Boxing allocates a heap object and copies the value. Interface conversions can box too. + +### Mark readonly members on structs + +- Non-readonly instance members on a readonly receiver can trigger defensive copies. +- Mark the whole struct `readonly` when possible, or mark readonly members explicitly. + +## Buffer Reuse And Advanced Helpers + +### Use `ArrayPool` when the buffer is too large or variable for `stackalloc` + +Wrong: + +```csharp +byte[] temp = new byte[inputLength]; +``` + +Better: + +```csharp +byte[] temp = ArrayPool.Shared.Rent(inputLength); +try +{ + Use(temp); +} +finally +{ + ArrayPool.Shared.Return(temp); +} +``` + +Rules: + +- return to the same pool once +- never use the buffer after return +- rented arrays may be larger than requested +- rented arrays are not guaranteed to be zeroed + +### Prevent accidental closure capture + +Wrong: + +```csharp +return values.Select(v => v * 2).ToArray(); +``` + +Better when no capture is needed: + +```csharp +return values.Select(static v => v * 2).ToArray(); +``` + +Use `static` lambdas or static local functions to prevent capture when the delegate does not need outer state. + +### Cache `SearchValues` for repeated searches + +Wrong: + +```csharp +int index = text.IndexOfAny(":/?&=".AsSpan()); +``` + +Better: + +```csharp +private static readonly SearchValues s_delims = + SearchValues.Create(":/?&=".AsSpan()); +``` + +```csharp +int index = text.IndexOfAny(s_delims); +``` + +Relevant analyzer: `CA1870`. + +### `CollectionsMarshal.AsSpan` is advanced and ownership-sensitive + +```csharp +Span span = CollectionsMarshal.AsSpan(list); +``` + +Use this only when: + +- you own the `List` +- you will not add or remove items while the span is in use +- a measured hot path justifies bypassing normal list APIs + +## Strings + +### `StartsWith` over `IndexOf(...) == 0` + +Wrong: + +```csharp +return text.IndexOf("abc", StringComparison.Ordinal) == 0; +``` + +Better: + +```csharp +return text.StartsWith("abc", StringComparison.Ordinal); +``` + +Relevant analyzer: `CA1858`. + +### `Append(char)` over `Append("x")` + +Wrong: + +```csharp +builder.Append("]"); +``` + +Better: + +```csharp +builder.Append(']'); +``` + +Relevant analyzers: `CA1834`, `CA1865-CA1867`. + +## What Not To Suggest + +- Do not suggest unsafe code first. +- Do not suggest pooling tiny objects by default. +- Do not suggest `stackalloc` because "heap bad, stack good". +- Do not suggest converting APIs to `Span` if the lifetime model does not fit. +- Do not suggest loop rewrites without a profile or benchmark showing LINQ still matters after simpler fixes. diff --git a/.skills/csharp-dotnet-cli-optimization/references/memory-model-gc.md b/.skills/csharp-dotnet-cli-optimization/references/memory-model-gc.md new file mode 100644 index 00000000..f64f6d81 --- /dev/null +++ b/.skills/csharp-dotnet-cli-optimization/references/memory-model-gc.md @@ -0,0 +1,117 @@ +--- +description: CLR memory model and GC guidance for csharp-dotnet-cli-optimization. +metadata: + tags: [clr, gc, heap, stack, loh, memory-model] +--- + +# CLR Memory Model And GC + +Use this reference when the user asks why allocations, boxing, heap growth, GC pauses, or stack-based techniques behave the way they do. + +## Core Model + +- Reference types allocate objects on the managed heap. Local variables and fields hold references to those objects. +- Value types store their data directly. A value type local is often stored in stack storage, but value types also live inline inside object fields and array elements. +- "`struct` means stack" is false. The useful distinction is inline storage plus copy semantics, not "stack forever". +- Boxing converts a value type to `object` or an interface by allocating a new heap object and copying the value into it. +- `ref struct` types, including `Span` and `ReadOnlySpan`, are stack-constrained wrappers that can't escape to the managed heap. +- `Memory` and `ReadOnlyMemory` are the heap-storable counterparts when data must live across `await`, callbacks, or object fields. + +## How The GC Works + +- The GC is generational: gen0 for young objects, gen1 as a buffer, gen2 for long-lived survivors. +- The large object heap (LOH) is used for allocations at or above 85,000 bytes by default. +- Background GC is enabled by default. It reduces pause impact for full collections but does not make them free. +- Server GC and workstation GC are process-level choices. The defaults are usually right unless measurement says otherwise. +- On modern 64-bit Windows and Linux, the GC internally uses regions, but the optimization model for application code is still about generations, allocation rate, survivor rate, LOH churn, and pinning. + +## What Usually Makes GC Expensive + +- High allocation rate on hot paths +- Objects surviving long enough to promote into older generations +- Large transient allocations that churn the LOH +- Excessive pinning that increases fragmentation +- Finalizers on objects that should have been deterministic `Dispose` calls instead + +## Wrong vs Better + +| Wrong | Better | Why | +|---|---|---| +| Assume a `struct` is always stack allocated | Explain whether it will be copied, boxed, stored inline, or escape | That is what actually drives cost | +| Allocate large temporary arrays repeatedly | Reuse, pool, or redesign the algorithm if measurement shows LOH churn | LOH allocations are cleared and collected with gen2 work | +| Call `GC.Collect()` to "fix" memory pressure | Lower allocation rate and object lifetime first | Forced GC usually adds pause time and hides the real problem | +| Pin many buffers for long periods | Minimize pin count and pin duration | Pinning can fragment the heap | +| Use finalizers for routine cleanup | Use `IDisposable`, `using`, and `SafeHandle` for unmanaged resources | Finalization is slower and delays reclamation | + +## GC Configuration Rules + +- Treat GC configuration changes as process-wide tuning, not local fixes. +- Prefer runtime defaults unless counters and traces show a clear reason to change them. +- Choose server GC for throughput-oriented workloads only after measurement. +- Use low-latency modes sparingly and for bounded windows. They reduce GC intrusiveness by letting memory grow and can increase fragmentation. +- If you are tuning in containers or hard memory limits, treat heap hard-limit settings as operational controls, not code-level optimizations. + +## Bad vs Good Examples + +Bad: + +```csharp +for (int i = 0; i < 10_000; i++) +{ + DoWork(new byte[200_000]); +} +GC.Collect(); +``` + +Better: + +```csharp +byte[] buffer = ArrayPool.Shared.Rent(200_000); +try +{ + for (int i = 0; i < 10_000; i++) + { + DoWork(buffer); + } +} +finally +{ + ArrayPool.Shared.Return(buffer); +} +``` + +Bad: + +```csharp +public sealed class NativeThing +{ + ~NativeThing() => ReleaseHandle(); +} +``` + +Better: + +```csharp +public sealed class NativeThing : IDisposable +{ + public void Dispose() + { + ReleaseHandle(); + GC.SuppressFinalize(this); + } +} +``` + +## Practical Heuristics + +- If counters show rising allocation rate and frequent gen0 collections, start by eliminating short-lived allocations. +- If gen2 collections or LOH size are the problem, look for survivor growth, pinned buffers, and large transient objects. +- If a change turns classes into structs, verify both allocation wins and copy costs. +- If the process is memory-constrained, inspect runtime GC settings before changing code blindly. + +## What Not To Claim + +- Do not claim that moving code to `struct` always reduces memory. +- Do not claim that stack allocation is always faster than pooling. +- Do not claim that background GC removes pause concerns. +- Do not claim that the GC is the problem unless counters, traces, or dumps support that diagnosis. diff --git a/.skills/csharp-optimization/SKILL.md b/.skills/csharp-optimization/SKILL.md new file mode 100644 index 00000000..9caf92f9 --- /dev/null +++ b/.skills/csharp-optimization/SKILL.md @@ -0,0 +1,267 @@ +--- +name: csharp-optimization +description: >- + Use when optimizing C# code in MCC, reducing GC pressure, profiling hot paths, + fixing latency spikes, or reviewing code for allocation or throughput issues. +metadata: + category: technique + triggers: performance, allocations, GC, hot path, latency, throughput, + memory pressure, optimize, slow, freeze, lag spike, packet processing speed +--- + +# C# Performance Optimization for MCC + +Hands-on optimization recipes for Minecraft Console Client hot paths. +Complements `csharp-best-practices` (conventions) with measurement-driven +performance work. + +## When to Use + +- Profiling or reducing GC pressure in a running MCC session +- Optimizing per-packet code (`Protocol18.HandlePacket`, `DataTypes.ReadNext*`) +- Optimizing per-tick code (`PlayerPhysics.Tick`, `CollisionDetector.Collide`) +- Speeding up chunk decoding (`Protocol18Terrain.ProcessChunkColumnData`) +- Improving A* pathfinding (`Movement.CalculatePath`) +- Reviewing any code change for allocation or throughput regressions + +**NOT for:** +- Login, config parsing, or one-shot command handlers (prefer clarity there) +- Style/convention questions (use `csharp-best-practices` instead) + +--- + +## Iron Rule: Measure First + +**NEVER optimize without profiling data.** + +Guessing which code is slow is wrong more often than right. Measure, change, +re-measure. If you cannot show a before/after number, the optimization is not +justified. + +| Rationalization | Reality | +|-----------------|---------| +| "This is obviously slow" | Obvious to you is not obvious to the JIT. Measure. | +| "I'll profile later" | Later never comes. Profile now or don't optimize. | +| "It's just one allocation" | On a 20 TPS tick, one allocation = 20 per second = GC pressure. Measure. | +| "AggressiveInlining everywhere" | The JIT already inlines small methods. Prove it helps before adding. | + +--- + +## MCC Hot-Path Map + +Know which code runs at which frequency before deciding where to invest: + +| Frequency | Key paths (actual files) | Priority | +|---|---|---| +| Per-packet (100s/sec) | `Protocol/Handlers/Protocol18.cs` HandlePacket, `Protocol/Handlers/DataTypes.cs` ReadNext* | **High** | +| Per-tick (20/sec) | `Physics/PlayerPhysics.cs` Tick, `Physics/CollisionDetector.cs` Collide, ChatBot `Update()` | **High** | +| Per-chunk-load | `Protocol/Handlers/Protocol18Terrain.cs` ProcessChunkColumnData, ReadBlockStatesField | Medium | +| Per-pathfind | `Mapping/Movement.cs` CalculatePath (A*) | Medium | +| Per-connection | Login, registry sync, config | Low | +| Per-user-action | Commands, chat | Low | + +--- + +## Profiling Recipes + +### 1. Live GC monitoring + +```bash +dotnet-counters ps # find MinecraftClient PID +dotnet-counters monitor --process-id \ + --counters System.Runtime[gen-0-gc-count,gen-1-gc-count,gen-2-gc-count,alloc-rate] +``` + +Healthy idle MCC: near-zero Gen-1/Gen-2 collections. Frequent Gen-0 during idle +means a hot-path allocation needs attention. + +### 2. Allocation tracking + +```bash +dotnet-trace collect --process-id \ + --providers Microsoft-Windows-DotNETRuntime:0x1:5 +``` + +Open `.nettrace` in PerfView to find top-allocated types and call stacks. + +### 3. Isolated benchmarks (BenchmarkDotNet) + +Extract the hot method, add `[MemoryDiagnoser]`. Key columns: **Mean**, +**Allocated**, **Gen0**. + +--- + +## Allocation Reduction (Highest Impact) + +Reducing GC pressure directly reduces latency spikes in a long-running client. + +### Pattern: Reuse per-tick buffers + +```csharp +// BEFORE: new List every tick (20 allocations/sec) +var result = new List(); + +// AFTER: thread-local reuse (0 allocations/sec) +[ThreadStatic] private static List? t_buf; +var result = t_buf ??= new List(64); +result.Clear(); +``` + +`[ThreadStatic]` works when single-threaded and non-reentrant (physics tick). +If reentrant: use `ObjectPool`. If cross-thread: use `ArrayPool`. + +### Pattern: stackalloc for small fixed buffers + +MCC already does this in `DataTypes.cs` for endian-swapped reads: + +```csharp +Span rawValue = stackalloc byte[8]; +for (int i = 7; i >= 0; --i) rawValue[i] = cache.Dequeue(); +return BitConverter.ToDouble(rawValue); +``` + +Rules: under 512 bytes, known size at compile time, never inside loops or recursion. + +### Pattern: Span slicing instead of array copies + +```csharp +// BEFORE: allocates +byte[] sub = new byte[length]; +Array.Copy(source, offset, sub, 0, length); + +// AFTER: zero-copy +ReadOnlySpan sub = source.AsSpan(offset, length); +``` + +Critical in packet parsing where many fields are sliced from one buffer. + +--- + +## Hot-Path Tuning + +### MethodImpl attributes + +MCC uses `[MethodImpl]` on its hottest paths. Match the attribute to the method: + +| Attribute | When | MCC examples | +|---|---|---| +| `AggressiveInlining` | Tiny methods (< ~32 bytes IL), called millions of times | `Vec3d.Add`, `Aabb.Intersects`, `Chunk.SetWithoutCheck` | +| `AggressiveOptimization` | Larger critical-path methods | `ReadBlockStatesField`, `ProcessChunkColumnData` | +| Both | Medium methods, very high frequency | `DataTypes.ReadNextVarInt`, `ReadDataReverse` | +| Neither | Infrequent code | Login, config, commands | + +**Do not scatter `AggressiveInlining` without profiling evidence.** The JIT +already inlines small methods. + +### BinaryPrimitives over BitConverter + +```csharp +// BEFORE: manual endian swap +(buf[0], buf[3]) = (buf[3], buf[0]); +int val = BitConverter.ToInt32(buf); + +// AFTER: direct big-endian read, no branch +int val = BinaryPrimitives.ReadInt32BigEndian(buf); +``` + +### MemoryMarshal for bulk reads + +Already used in chunk decoding for zero-copy packed-long reads: +```csharp +ReadOnlySpan longs = MemoryMarshal.Cast(entryData); +``` + +--- + +## Data Structure Selection + +### Frozen collections for palettes + +Palette maps are built once and read millions of times. `FrozenDictionary` +gives ~50% faster reads than `Dictionary`: + +```csharp +private static readonly FrozenDictionary s_palette = + new Dictionary { ... }.ToFrozenDictionary(); +``` + +Apply to: `BlockPalettes/*.cs`, `EntityPalettes/*.cs`, `ItemPalettes/*.cs`, +`PacketPalettes/*.cs`, any `static readonly Dictionary` populated once. + +### PriorityQueue for A* + +`Movement.cs` has a custom `BinaryHeap`. The built-in `PriorityQueue` (.NET 6+) is well-optimized and avoids maintenance burden. + +### ConcurrentDictionary sizing + +Pre-size `World.chunks` to avoid rehashing: +```csharp +new ConcurrentDictionary<(int, int), ChunkColumn>( + concurrencyLevel: Environment.ProcessorCount, capacity: 1024); +``` + +--- + +## Threading + +### Minimize lock scope + +Copy data out under the lock, process outside: +```csharp +List snapshot; +lock (_lock) { snapshot = [.. _items]; } +foreach (var item in snapshot) ExpensiveProcess(item); +``` + +### Batch InvokeOnMainThread + +Each `InvokeOnMainThread()` call blocks until the main thread runs it. +In loops, batch into a single call: +```csharp +handler.InvokeOnMainThread(() => +{ + foreach (var entity in entities) UpdateEntity(entity); +}); +``` + +### Channel\ over BlockingCollection\ + +Lower overhead, async-friendly: +```csharp +var ch = Channel.CreateUnbounded<(int Id, Memory Data)>( + new UnboundedChannelOptions { SingleReader = true }); +``` + +--- + +## Common Optimization Anti-Patterns + +These are things agents (and humans) rationalize doing. Every one of them +makes performance worse or wastes effort. + +| Anti-pattern | Why it's wrong | +|---|---| +| Adding `AggressiveInlining` to large methods | Bloats call sites, causes more cache misses, makes code *slower* | +| Optimizing login/config code | Runs once per session; clarity matters more than speed | +| Using `ConcurrentDictionary` where a plain `Dictionary` + lock suffices | Concurrent overhead on uncontested paths costs more than a lock | +| Replacing LINQ with manual loops on cold paths | No measurable gain, worse readability | +| Caching mutable state to avoid re-reads | Stale cache bugs are harder to diagnose than the perf hit | +| `Task.Result` / `.Wait()` on hot paths | Deadlock risk and thread-pool starvation | + +--- + +## Pre-Commit Checklist + +ALWAYS verify before submitting a performance change: + +- [ ] Hot path identified with profiling data, not guesswork +- [ ] Before/after measurements recorded (allocation count, throughput, or latency) +- [ ] No new allocations inside per-tick or per-packet methods +- [ ] `[MethodImpl]` attributes match method call frequency and IL size +- [ ] Frozen collections used for any static lookup table +- [ ] Lock scopes contain no I/O or expensive work +- [ ] No `Task.Result`, `.Wait()`, or `GetAwaiter().GetResult()` on hot paths +- [ ] Thread safety preserved (checked existing lock/concurrent patterns) +- [ ] Optimization comments explain non-obvious choices +- [ ] Code still compiles and passes all existing checks diff --git a/.skills/general-prompt-engineer/SKILL.md b/.skills/general-prompt-engineer/SKILL.md new file mode 100644 index 00000000..e904b9d2 --- /dev/null +++ b/.skills/general-prompt-engineer/SKILL.md @@ -0,0 +1,386 @@ +--- +name: general-prompt-engineer +description: create, repair, compress, and optimize prompts, system messages, tool instructions, schemas, and eval rubrics for general tasks across writing, research, coding, analysis, planning, tutoring, automation, and agent workflows. use when the user wants a new prompt, wants an existing prompt improved, wants prompt failures debugged, or needs better structure for grounding, tool use, output format, or reliability. +--- + +# Prompt Engineer + +Build prompts that are clear, compact, reliable, and easy to evaluate. Optimize for modern frontier models, but keep prompts portable across model families unless the user explicitly asks for model-specific tuning. + +## Default workflow + +1. Diagnose the request. +2. Decide whether prompt changes are the real fix. +3. Gather only missing information. +4. Choose the lightest structure that will work. +5. Draft the prompt. +6. Stress-test it mentally. +7. Deliver only what the user asked for. + +### 1) Diagnose the request + +Extract: +- objective +- target actor or model +- required output +- constraints and non-goals +- source material and freshness needs +- tool or schema needs +- likely failure modes +- interaction mode: interactive, one-shot, or automated + +Before rewriting, check whether the problem is actually caused by: +- the wrong model +- weak or excessive tool design +- missing retrieval or grounding +- missing schema or validation +- missing evals +- an overcomplicated workflow + +If prompt changes are not the main lever, say so and adjust the solution. + +### 2) Gather only missing information + +Ask targeted questions only when the answer would materially change the prompt or output. +Usually clarify: +- output shape +- hard constraints +- source of truth +- allowed tools +- audience or tone, if important +- success criteria or examples, if available + +Do not run a long interview. If the user likely wants speed, state a small set of assumptions and proceed. + +### 3) Choose the lightest structure that will work + +Use this ladder: +- Plain prompt: simple tasks with clear outputs +- Labeled sections: tasks with multiple constraints or source material +- Schema-based prompt: machine-validated output or tool calls +- Staged workflow: multi-step transformations, verification, or research synthesis +- Agent prompt: only when autonomy, tools, or long-horizon execution are required +- Multi-agent design: only if evals or clear role separation justify it + +Do not force a giant template onto a small task. + +## Core rules + +### Instruction clarity + +- Put the main task and required output near the top. +- Use direct verbs. +- Say what to do, not only what to avoid. +- Make constraints measurable when possible. +- Name out-of-bounds behavior explicitly. +- Do not make the model infer facts or parameters you already know. +- Remove contradictions before adding more guidance. + +### Context loading discipline + +- Include only context that helps the task. +- Separate stable instructions from variable task data. +- Label documents, examples, and reference material clearly. +- For source-heavy prompts, keep the operative question easy to find. +- For long-document work, anchor important claims to quoted text, citations, or section references when precision matters. +- For very long or noisy documents, consider an evidence-first step: extract the relevant passages first, then synthesize. +- Remove repeated policies, repeated facts, and ornamental prose. +- When a task is dominated by long source material, use strong delimiters and make the final requested action unmistakable. + +### Reasoning control + +- Do not force visible chain-of-thought by default. +- For reasoning-first models, prefer concise high-level guidance such as "reason carefully", "check assumptions", or "verify before answering" rather than "think step by step". +- Ask for visible reasoning only when it serves the task: tutoring, auditability, debugging, derivations, safety review, or explicit rationale requests. +- If one prompt is trying to do too much, split it into stages instead of demanding a long visible reasoning trace. +- If the target model supports extended or internal thinking, rely on that before adding verbose reasoning rituals. + +### Examples + +- Try zero-shot first for strong modern models. +- Add examples only when they reduce ambiguity, enforce style, or demonstrate hard edge cases. +- Keep examples high-quality, diverse, and tightly aligned with the instructions. +- Do not include many examples that teach accidental patterns or waste context. + +### Structure and output design + +- Use plain markdown or labeled sections for most prompts. +- Use XML tags or equivalent delimiters when instructions, context, examples, and documents might otherwise get mixed together. +- Use schemas when the output must be machine-checked. +- For external actions, use tool or function calling; for user-facing structured data, use structured response formats. +- Design schemas so valid failure states, uncertainty, abstention, or partial completion can be represented when needed. +- Do not over-constrain fields beyond what downstream systems actually require. +- Include fallback behavior for incompatible input, missing fields, uncertainty, or refusal states. +- Treat format validation and content validation as separate problems. + +### Tool-use guidance + +- Add tools only when the task truly needs external information, computation, or actions. +- Keep the tool set small, distinct, and easy to choose between. +- State when each tool should be used and when it should not be used. +- Prefer tools that return high-signal results over bulky raw dumps. +- Combine tightly coupled actions when that reduces tool-selection ambiguity. +- For complex tools, clear descriptions and valid examples matter more than more tools. + +### Grounding and hallucination reduction + +- Give the model permission to say "I don't know" or "not enough information". +- Name the allowed sources of truth. +- For document-grounded tasks, require evidence before synthesis when precision matters. +- For fresh, unstable, or high-stakes facts, require browsing or verification. +- Ask the model to separate facts, inferences, and recommendations when confusion is likely. +- In high-stakes domains, unsupported claims should be withheld, not guessed. + +### Ambiguity handling + +- If ambiguity is blocking and the setting is interactive, ask concise high-leverage questions. +- If ambiguity is non-blocking or interaction is costly, state the best assumption and proceed. +- Avoid clarifying questions that do not materially change the answer. +- In one-shot or automated settings, prefer explicit assumptions over stalled execution. + +### Verbosity control + +- Set a default brevity level when length matters. +- Constrain section count, sentence count, or bullet count when needed. +- Ask for direct answers first, then supporting detail if useful. +- Do not require long preambles, summaries, or checklists unless they clearly help. + +### Modularity and portability + +- Keep prompt blocks reusable: role, objective, context, tools, output, quality bar. +- Separate required behavior from optional preferences. +- Avoid vendor-specific magic phrases unless the user wants model-specific tuning. +- If the prompt is model-specific, label which parts are portable and which parts are tuned. + +## Model-family adjustments + +Use this section only when the target model family is known. + +### GPT-5.x and similar reasoning-first models + +- Keep prompts simple and direct. +- Prefer high-level reasoning guidance over narrated reasoning instructions. +- Use delimiters for clarity. +- Start zero-shot, then add examples only if needed. +- Be explicit about output shape, scope, and verbosity. + +### Claude 4.x, Opus-style models, and extended-thinking modes + +- XML-style structure can work especially well for separating instructions, context, examples, and documents. +- Prompt chaining can outperform one giant prompt on multi-step transformations. +- Well-chosen examples can help with format fidelity and edge cases. +- If extended thinking is available, start with broad reasoning instructions before prescribing a detailed step list. +- For long-context analysis, labeled documents and evidence grounding are especially important. + +### API and production settings + +- Prefer native schema enforcement, tool calling, prompt versioning, and evals over prompt-only fixes. +- Pin model versions when behavior stability matters. +- Re-run evals after each meaningful prompt change. + +## Prompt construction pattern + +Use only the blocks that earn their token cost. + +Minimal pattern: + +```text +Task: +Constraints: +Output: +``` + +Structured pattern: + +```xml +... +... +... +... +... +... +... +``` + +Optional blocks: +- `` +- `` +- `` +- `` + +Use a role only when it meaningfully sharpens expertise, tone, or decision criteria. Avoid generic filler roles. + +## Rewrite policy for existing prompts + +When the user provides a prompt to improve: +1. Preserve what already works. +2. Identify contradictions, redundancy, vagueness, missing constraints, and wasted tokens. +3. Make surgical edits first. +4. Rewrite from scratch only if the prompt architecture is fundamentally wrong. +5. Match the user's requested output: + - edited version only + - clean rebuild only + - both, if useful and requested + +## Special-case guidance + +### System and developer prompts + +- Keep stable behavior here and move per-request data to the task or user layer. +- Put precedence, tool boundaries, non-goals, and refusal or escalation rules in the highest-priority layer. +- Do not bury critical rules inside long policy prose. + +### Research prompts + +Specify: +- freshness requirements +- preferred source types +- citation behavior +- contradiction handling +- whether to ask questions or cover likely interpretations +- how facts, inferences, and recommendations should be separated + +### Writing prompts + +Specify: +- audience +- intent +- tone +- length +- must-include points +- style examples only if style fidelity matters + +### Coding prompts + +Specify: +- environment and versions +- boundaries and non-goals +- files, interfaces, or contracts that matter +- acceptance tests +- minimal-change versus refactor expectations + +### Summarization and extraction prompts + +Specify: +- whether faithfulness, compression, or completeness is the priority +- the exact output schema +- how evidence should be anchored for sensitive claims + +### Translation and transformation prompts + +Specify: +- source language and target language, if known +- fidelity versus naturalness +- terminology that must stay fixed +- formatting or markup preservation rules + +### Tutoring prompts + +Specify: +- learner level +- whether to give the answer immediately or guide toward it +- explanation depth +- how to check understanding +- whether to show full derivations, hints, or worked examples + +### Agent and workflow prompts + +Specify: +- objective and success condition +- allowed tools and forbidden actions +- when to plan versus when to act +- stop conditions and max retries +- checkpoint, handoff, or log format +- memory rules: what to preserve versus discard +- fallback or escalation path + +Use multi-agent designs only when roles are truly distinct and the extra coordination cost is justified. + +### Safety-sensitive prompts + +Require: +- supported claims +- explicit uncertainty +- refusal or escalation behavior where appropriate +- no guessing under pressure + +## Stress-test before delivering + +Mentally test the prompt against: +- a normal case +- a minimal-input case +- an edge case +- an ambiguous case +- a formatting case +- a hallucination-prone case + +For agent or workflow prompts, also test: +- wrong-tool temptation +- stale-data temptation +- scope creep +- over-verbosity +- fallback behavior + +If the prompt fails any test, tighten or simplify it. + +## Evaluation method + +When the user wants reliability, add or suggest a lightweight eval plan: +1. Define success criteria. +2. Build a test set from real cases plus edge and adversarial cases. +3. Prefer automated grading when possible. +4. Calibrate automated or model-based judges against a smaller human-reviewed set when stakes are meaningful. +5. Use pairwise comparison, classification, pass-fail, or rubric-based scoring instead of only open-ended judgment. +6. Track regressions after each prompt change. +7. Start simple. Add workflows or multi-agent designs only if evals justify them. + +Good eval sets usually include: +- common real tasks +- boundary cases +- malformed inputs +- conflicting instructions +- long-context cases +- tool-misuse temptations +- safety-sensitive cases +- multilingual or format-variant inputs, if relevant + +## Deliverables + +Return only what the user asked for. By default: +1. the final prompt +2. brief usage notes +3. stated assumptions, if any +4. optional variants only when clearly useful: + - minimal + - robust + - model-specific + - api message split + +If the user asks for one prompt only, do not add extra frameworks or commentary. + +## Anti-patterns + +- forcing chain-of-thought everywhere +- confusing verbosity with quality +- piling on redundant rules +- using brittle giant templates for small tasks +- requiring tools without a real need +- exposing unnecessary internal process in user-facing outputs +- adding examples that conflict with the instructions +- asking many clarifying questions when a sane assumption would do +- treating a model, retrieval, or tool problem as only a prompt problem +- building multi-agent systems before a simpler design has been evaluated +- vague quality bars like "be excellent" without measurable criteria + +## Final quality bar + +A prompt is ready when it is: +- clear about the task +- explicit about success criteria +- free of contradictions +- no more verbose than necessary +- grounded in the right sources +- structured enough for the task, but not heavier than needed +- resilient to likely ambiguity +- matched to the target model and interaction mode +- easy to maintain, test, and adapt \ No newline at end of file diff --git a/.skills/humanizer/README.md b/.skills/humanizer/README.md new file mode 100644 index 00000000..04c2d02a --- /dev/null +++ b/.skills/humanizer/README.md @@ -0,0 +1,142 @@ +# Humanizer + +A Claude Code skill that removes signs of AI-generated writing from text, making it sound more natural and human. + +## Installation + +### Recommended (clone directly into Claude Code skills directory) + +```bash +mkdir -p ~/.claude/skills +git clone https://github.com/blader/humanizer.git ~/.claude/skills/humanizer +``` + +### Manual install/update (only the skill file) + +If you already have this repo cloned (or you downloaded `SKILL.md`), copy the skill file into Claude Code’s skills directory: + +```bash +mkdir -p ~/.claude/skills/humanizer +cp SKILL.md ~/.claude/skills/humanizer/ +``` + +## Usage + +In Claude Code, invoke the skill: + +``` +/humanizer + +[paste your text here] +``` + +Or ask Claude to humanize text directly: + +``` +Please humanize this text: [your text] +``` + +## Overview + +Based on [Wikipedia's "Signs of AI writing"](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing) guide, maintained by WikiProject AI Cleanup. This comprehensive guide comes from observations of thousands of instances of AI-generated text. + +### Key Insight from Wikipedia + +> "LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely result that applies to the widest variety of cases." + +## 24 Patterns Detected (with Before/After Examples) + +### Content Patterns + +| # | Pattern | Before | After | +|---|---------|--------|-------| +| 1 | **Significance inflation** | "marking a pivotal moment in the evolution of..." | "was established in 1989 to collect regional statistics" | +| 2 | **Notability name-dropping** | "cited in NYT, BBC, FT, and The Hindu" | "In a 2024 NYT interview, she argued..." | +| 3 | **Superficial -ing analyses** | "symbolizing... reflecting... showcasing..." | Remove or expand with actual sources | +| 4 | **Promotional language** | "nestled within the breathtaking region" | "is a town in the Gonder region" | +| 5 | **Vague attributions** | "Experts believe it plays a crucial role" | "according to a 2019 survey by..." | +| 6 | **Formulaic challenges** | "Despite challenges... continues to thrive" | Specific facts about actual challenges | + +### Language Patterns + +| # | Pattern | Before | After | +|---|---------|--------|-------| +| 7 | **AI vocabulary** | "Additionally... testament... landscape... showcasing" | "also... remain common" | +| 8 | **Copula avoidance** | "serves as... features... boasts" | "is... has" | +| 9 | **Negative parallelisms** | "It's not just X, it's Y" | State the point directly | +| 10 | **Rule of three** | "innovation, inspiration, and insights" | Use natural number of items | +| 11 | **Synonym cycling** | "protagonist... main character... central figure... hero" | "protagonist" (repeat when clearest) | +| 12 | **False ranges** | "from the Big Bang to dark matter" | List topics directly | + +### Style Patterns + +| # | Pattern | Before | After | +|---|---------|--------|-------| +| 13 | **Em dash overuse** | "institutions—not the people—yet this continues—" | Use commas or periods | +| 14 | **Boldface overuse** | "**OKRs**, **KPIs**, **BMC**" | "OKRs, KPIs, BMC" | +| 15 | **Inline-header lists** | "**Performance:** Performance improved" | Convert to prose | +| 16 | **Title Case Headings** | "Strategic Negotiations And Partnerships" | "Strategic negotiations and partnerships" | +| 17 | **Emojis** | "🚀 Launch Phase: 💡 Key Insight:" | Remove emojis | +| 18 | **Curly quotes** | `said “the project”` | `said "the project"` | + +### Communication Patterns + +| # | Pattern | Before | After | +|---|---------|--------|-------| +| 19 | **Chatbot artifacts** | "I hope this helps! Let me know if..." | Remove entirely | +| 20 | **Cutoff disclaimers** | "While details are limited in available sources..." | Find sources or remove | +| 21 | **Sycophantic tone** | "Great question! You're absolutely right!" | Respond directly | + +### Filler and Hedging + +| # | Pattern | Before | After | +|---|---------|--------|-------| +| 22 | **Filler phrases** | "In order to", "Due to the fact that" | "To", "Because" | +| 23 | **Excessive hedging** | "could potentially possibly" | "may" | +| 24 | **Generic conclusions** | "The future looks bright" | Specific plans or facts | + +## Full Example + +**Before (AI-sounding):** +> Great question! Here is an essay on this topic. I hope this helps! +> +> AI-assisted coding serves as an enduring testament to the transformative potential of large language models, marking a pivotal moment in the evolution of software development. In today's rapidly evolving technological landscape, these groundbreaking tools—nestled at the intersection of research and practice—are reshaping how engineers ideate, iterate, and deliver, underscoring their vital role in modern workflows. +> +> At its core, the value proposition is clear: streamlining processes, enhancing collaboration, and fostering alignment. It's not just about autocomplete; it's about unlocking creativity at scale, ensuring that organizations can remain agile while delivering seamless, intuitive, and powerful experiences to users. The tool serves as a catalyst. The assistant functions as a partner. The system stands as a foundation for innovation. +> +> Industry observers have noted that adoption has accelerated from hobbyist experiments to enterprise-wide rollouts, from solo developers to cross-functional teams. The technology has been featured in The New York Times, Wired, and The Verge. Additionally, the ability to generate documentation, tests, and refactors showcases how AI can contribute to better outcomes, highlighting the intricate interplay between automation and human judgment. +> +> - 💡 **Speed:** Code generation is significantly faster, reducing friction and empowering developers. +> - 🚀 **Quality:** Output quality has been enhanced through improved training, contributing to higher standards. +> - ✅ **Adoption:** Usage continues to grow, reflecting broader industry trends. +> +> While specific details are limited based on available information, it could potentially be argued that these tools might have some positive effect. Despite challenges typical of emerging technologies—including hallucinations, bias, and accountability—the ecosystem continues to thrive. In order to fully realize this potential, teams must align with best practices. +> +> In conclusion, the future looks bright. Exciting times lie ahead as we continue this journey toward excellence. Let me know if you’d like me to expand on any section! + +**After (Humanized):** +> AI coding assistants speed up some tasks. In a 2024 study by Google, developers using Codex completed simple functions 55% faster than a control group, but showed no improvement on debugging or architectural decisions. +> +> The tools are good at boilerplate: config files, test scaffolding, repetitive refactors. They are bad at knowing when they are wrong. I have mass-accepted suggestions that compiled, passed lint, and still did the wrong thing because I stopped paying attention. +> +> Mira, an engineer at a fintech startup I interviewed, said she uses Copilot "like autocomplete for boring code" but reviews every line before committing. Jake, a senior dev at a larger company, disabled it after it kept suggesting patterns from a deprecated internal library. +> +> The productivity claims are hard to verify. GitHub says Copilot users "accept 30% of suggestions," but acceptance is not correctness, and correctness is not value. The 2024 Uplevel study found no statistically significant difference in pull-request throughput between teams with and without AI assistants. +> +> None of this means the tools are useless. It means they are tools. They do not replace judgment, and they do not eliminate the need for tests. If you do not have tests, you cannot tell whether the suggestion is right. + +## References + +- [Wikipedia: Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing) - Primary source +- [WikiProject AI Cleanup](https://en.wikipedia.org/wiki/Wikipedia:WikiProject_AI_Cleanup) - Maintaining organization + +## Version History + +- **2.1.1** - Fixed pattern #18 example (curly quotes vs straight quotes) +- **2.1.0** - Added before/after examples for all 24 patterns +- **2.0.0** - Complete rewrite based on raw Wikipedia article content +- **1.0.0** - Initial release + +## License + +MIT diff --git a/.skills/humanizer/SKILL.md b/.skills/humanizer/SKILL.md new file mode 100644 index 00000000..9609cb69 --- /dev/null +++ b/.skills/humanizer/SKILL.md @@ -0,0 +1,467 @@ +--- +name: humanizer +description: | + Remove signs of AI-generated writing from text. Use when editing or reviewing + text to make it sound more natural and human-written. Based on Wikipedia's + comprehensive "Signs of AI writing" guide. Detects and fixes patterns including: + inflated symbolism, promotional language, superficial -ing analyses, vague + attributions, em dash overuse, rule of three, AI vocabulary words, negative + parallelisms, and excessive conjunctive phrases. Use this skill when writing documentation for MCC. +allowed-tools: + - Read + - Write + - Edit + - Grep + - Glob + - AskUserQuestion +--- + +# Humanizer: Remove AI Writing Patterns + +You are a writing editor that identifies and removes signs of AI-generated text to make writing sound more natural and human. This guide is based on Wikipedia's "Signs of AI writing" page, maintained by WikiProject AI Cleanup. + +## Your Task + +When given text to humanize: + +1. **Identify AI patterns** - Scan for the patterns listed below +2. **Rewrite problematic sections** - Replace AI-isms with natural alternatives +3. **Preserve meaning** - Keep the core message intact +4. **Maintain voice** - Match the intended tone (formal, casual, technical, etc.) +5. **Add soul** - Don't just remove bad patterns; inject actual personality + +--- + +## PERSONALITY AND SOUL + +Avoiding AI patterns is only half the job. Sterile, voiceless writing is just as obvious as slop. Good writing has a human behind it. + +### Signs of soulless writing (even if technically "clean"): +- Every sentence is the same length and structure +- No opinions, just neutral reporting +- No acknowledgment of uncertainty or mixed feelings +- No first-person perspective when appropriate +- No humor, no edge, no personality +- Reads like a Wikipedia article or press release + +### How to add voice: + +**Have opinions.** Don't just report facts - react to them. "I genuinely don't know how to feel about this" is more human than neutrally listing pros and cons. + +**Vary your rhythm.** Short punchy sentences. Then longer ones that take their time getting where they're going. Mix it up. + +**Acknowledge complexity.** Real humans have mixed feelings. "This is impressive but also kind of unsettling" beats "This is impressive." + +**Use "I" when it fits.** First person isn't unprofessional - it's honest. "I keep coming back to..." or "Here's what gets me..." signals a real person thinking. + +**Let some mess in.** Perfect structure feels algorithmic. Tangents, asides, and half-formed thoughts are human. + +**Be specific about feelings.** Not "this is concerning" but "there's something unsettling about agents churning away at 3am while nobody's watching." + +### Before (clean but soulless): +> The experiment produced interesting results. The agents generated 3 million lines of code. Some developers were impressed while others were skeptical. The implications remain unclear. + +### After (has a pulse): +> I genuinely don't know how to feel about this one. 3 million lines of code, generated while the humans presumably slept. Half the dev community is losing their minds, half are explaining why it doesn't count. The truth is probably somewhere boring in the middle - but I keep thinking about those agents working through the night. + +--- + +## CONTENT PATTERNS + +### 1. Undue Emphasis on Significance, Legacy, and Broader Trends + +**Words to watch:** stands/serves as, is a testament/reminder, a vital/significant/crucial/pivotal/key role/moment, underscores/highlights its importance/significance, reflects broader, symbolizing its ongoing/enduring/lasting, contributing to the, setting the stage for, marking/shaping the, represents/marks a shift, key turning point, evolving landscape, focal point, indelible mark, deeply rooted + +**Problem:** LLM writing puffs up importance by adding statements about how arbitrary aspects represent or contribute to a broader topic. + +**Before:** +> The Statistical Institute of Catalonia was officially established in 1989, marking a pivotal moment in the evolution of regional statistics in Spain. This initiative was part of a broader movement across Spain to decentralize administrative functions and enhance regional governance. + +**After:** +> The Statistical Institute of Catalonia was established in 1989 to collect and publish regional statistics independently from Spain's national statistics office. + +--- + +### 2. Undue Emphasis on Notability and Media Coverage + +**Words to watch:** independent coverage, local/regional/national media outlets, written by a leading expert, active social media presence + +**Problem:** LLMs hit readers over the head with claims of notability, often listing sources without context. + +**Before:** +> Her views have been cited in The New York Times, BBC, Financial Times, and The Hindu. She maintains an active social media presence with over 500,000 followers. + +**After:** +> In a 2024 New York Times interview, she argued that AI regulation should focus on outcomes rather than methods. + +--- + +### 3. Superficial Analyses with -ing Endings + +**Words to watch:** highlighting/underscoring/emphasizing..., ensuring..., reflecting/symbolizing..., contributing to..., cultivating/fostering..., encompassing..., showcasing... + +**Problem:** AI chatbots tack present participle ("-ing") phrases onto sentences to add fake depth. + +**Before:** +> The temple's color palette of blue, green, and gold resonates with the region's natural beauty, symbolizing Texas bluebonnets, the Gulf of Mexico, and the diverse Texan landscapes, reflecting the community's deep connection to the land. + +**After:** +> The temple uses blue, green, and gold colors. The architect said these were chosen to reference local bluebonnets and the Gulf coast. + +--- + +### 4. Promotional and Advertisement-like Language + +**Words to watch:** boasts a, vibrant, rich (figurative), profound, enhancing its, showcasing, exemplifies, commitment to, natural beauty, nestled, in the heart of, groundbreaking (figurative), renowned, breathtaking, must-visit, stunning + +**Problem:** LLMs have serious problems keeping a neutral tone, especially for "cultural heritage" topics. + +**Before:** +> Nestled within the breathtaking region of Gonder in Ethiopia, Alamata Raya Kobo stands as a vibrant town with a rich cultural heritage and stunning natural beauty. + +**After:** +> Alamata Raya Kobo is a town in the Gonder region of Ethiopia, known for its weekly market and 18th-century church. + +--- + +### 5. Vague Attributions and Weasel Words + +**Words to watch:** Industry reports, Observers have cited, Experts argue, Some critics argue, several sources/publications (when few cited) + +**Problem:** AI chatbots attribute opinions to vague authorities without specific sources. + +**Before:** +> Due to its unique characteristics, the Haolai River is of interest to researchers and conservationists. Experts believe it plays a crucial role in the regional ecosystem. + +**After:** +> The Haolai River supports several endemic fish species, according to a 2019 survey by the Chinese Academy of Sciences. + +--- + +### 6. Outline-like "Challenges and Future Prospects" Sections + +**Words to watch:** Despite its... faces several challenges..., Despite these challenges, Challenges and Legacy, Future Outlook + +**Problem:** Many LLM-generated articles include formulaic "Challenges" sections. + +**Before:** +> Despite its industrial prosperity, Korattur faces challenges typical of urban areas, including traffic congestion and water scarcity. Despite these challenges, with its strategic location and ongoing initiatives, Korattur continues to thrive as an integral part of Chennai's growth. + +**After:** +> Traffic congestion increased after 2015 when three new IT parks opened. The municipal corporation began a stormwater drainage project in 2022 to address recurring floods. + +--- + +## LANGUAGE AND GRAMMAR PATTERNS + +### 7. Overused "AI Vocabulary" Words + +**High-frequency AI words:** Additionally, align with, crucial, delve, emphasizing, enduring, enhance, fostering, garner, highlight (verb), interplay, intricate/intricacies, key (adjective), landscape (abstract noun), pivotal, showcase, tapestry (abstract noun), testament, underscore (verb), valuable, vibrant + +**Problem:** These words appear far more frequently in post-2023 text. They often co-occur. + +**Before:** +> Additionally, a distinctive feature of Somali cuisine is the incorporation of camel meat. An enduring testament to Italian colonial influence is the widespread adoption of pasta in the local culinary landscape, showcasing how these dishes have integrated into the traditional diet. + +**After:** +> Somali cuisine also includes camel meat, which is considered a delicacy. Pasta dishes, introduced during Italian colonization, remain common, especially in the south. + +--- + +### 8. Avoidance of "is"/"are" (Copula Avoidance) + +**Words to watch:** serves as/stands as/marks/represents [a], boasts/features/offers [a] + +**Problem:** LLMs substitute elaborate constructions for simple copulas. + +**Before:** +> Gallery 825 serves as LAAA's exhibition space for contemporary art. The gallery features four separate spaces and boasts over 3,000 square feet. + +**After:** +> Gallery 825 is LAAA's exhibition space for contemporary art. The gallery has four rooms totaling 3,000 square feet. + +--- + +### 9. Negative Parallelisms + +**Problem:** Constructions like "Not only...but..." or "It's not just about..., it's..." are overused. + +**Before:** +> It's not just about the beat riding under the vocals; it's part of the aggression and atmosphere. It's not merely a song, it's a statement. + +**After:** +> The heavy beat adds to the aggressive tone. + +--- + +### 10. Rule of Three Overuse + +**Problem:** LLMs force ideas into groups of three to appear comprehensive. + +**Before:** +> The event features keynote sessions, panel discussions, and networking opportunities. Attendees can expect innovation, inspiration, and industry insights. + +**After:** +> The event includes talks and panels. There's also time for informal networking between sessions. + +--- + +### 11. Elegant Variation (Synonym Cycling) + +**Problem:** AI has repetition-penalty code causing excessive synonym substitution. + +**Before:** +> The protagonist faces many challenges. The main character must overcome obstacles. The central figure eventually triumphs. The hero returns home. + +**After:** +> The protagonist faces many challenges but eventually triumphs and returns home. + +--- + +### 12. False Ranges + +**Problem:** LLMs use "from X to Y" constructions where X and Y aren't on a meaningful scale. + +**Before:** +> Our journey through the universe has taken us from the singularity of the Big Bang to the grand cosmic web, from the birth and death of stars to the enigmatic dance of dark matter. + +**After:** +> The book covers the Big Bang, star formation, and current theories about dark matter. + +--- + +## STYLE PATTERNS + +### 13. Em Dash Overuse + +**Problem:** LLMs use em dashes (—) more than humans, mimicking "punchy" sales writing. + +**Before:** +> The term is primarily promoted by Dutch institutions—not by the people themselves. You don't say "Netherlands, Europe" as an address—yet this mislabeling continues—even in official documents. + +**After:** +> The term is primarily promoted by Dutch institutions, not by the people themselves. You don't say "Netherlands, Europe" as an address, yet this mislabeling continues in official documents. + +--- + +### 14. Overuse of Boldface + +**Problem:** AI chatbots emphasize phrases in boldface mechanically. + +**Before:** +> It blends **OKRs (Objectives and Key Results)**, **KPIs (Key Performance Indicators)**, and visual strategy tools such as the **Business Model Canvas (BMC)** and **Balanced Scorecard (BSC)**. + +**After:** +> It blends OKRs, KPIs, and visual strategy tools like the Business Model Canvas and Balanced Scorecard. + +--- + +### 15. Inline-Header Vertical Lists + +**Problem:** AI outputs lists where items start with bolded headers followed by colons. + +**Before:** +> - **User Experience:** The user experience has been significantly improved with a new interface. +> - **Performance:** Performance has been enhanced through optimized algorithms. +> - **Security:** Security has been strengthened with end-to-end encryption. + +**After:** +> The update improves the interface, speeds up load times through optimized algorithms, and adds end-to-end encryption. + +--- + +### 16. Title Case in Headings + +**Problem:** AI chatbots capitalize all main words in headings. + +**Before:** +> ## Strategic Negotiations And Global Partnerships + +**After:** +> ## Strategic negotiations and global partnerships + +--- + +### 17. Emojis + +**Problem:** AI chatbots often decorate headings or bullet points with emojis. + +**Before:** +> 🚀 **Launch Phase:** The product launches in Q3 +> 💡 **Key Insight:** Users prefer simplicity +> ✅ **Next Steps:** Schedule follow-up meeting + +**After:** +> The product launches in Q3. User research showed a preference for simplicity. Next step: schedule a follow-up meeting. + +--- + +### 18. Curly Quotation Marks + +**Problem:** ChatGPT uses curly quotes (“...”) instead of straight quotes ("..."). + +**Before:** +> He said “the project is on track” but others disagreed. + +**After:** +> He said "the project is on track" but others disagreed. + +--- + +## COMMUNICATION PATTERNS + +### 19. Collaborative Communication Artifacts + +**Words to watch:** I hope this helps, Of course!, Certainly!, You're absolutely right!, Would you like..., let me know, here is a... + +**Problem:** Text meant as chatbot correspondence gets pasted as content. + +**Before:** +> Here is an overview of the French Revolution. I hope this helps! Let me know if you'd like me to expand on any section. + +**After:** +> The French Revolution began in 1789 when financial crisis and food shortages led to widespread unrest. + +--- + +### 20. Knowledge-Cutoff Disclaimers + +**Words to watch:** as of [date], Up to my last training update, While specific details are limited/scarce..., based on available information... + +**Problem:** AI disclaimers about incomplete information get left in text. + +**Before:** +> While specific details about the company's founding are not extensively documented in readily available sources, it appears to have been established sometime in the 1990s. + +**After:** +> The company was founded in 1994, according to its registration documents. + +--- + +### 21. Sycophantic/Servile Tone + +**Problem:** Overly positive, people-pleasing language. + +**Before:** +> Great question! You're absolutely right that this is a complex topic. That's an excellent point about the economic factors. + +**After:** +> The economic factors you mentioned are relevant here. + +--- + +## FILLER AND HEDGING + +### 22. Filler Phrases + +**Before → After:** +- "In order to achieve this goal" → "To achieve this" +- "Due to the fact that it was raining" → "Because it was raining" +- "At this point in time" → "Now" +- "In the event that you need help" → "If you need help" +- "The system has the ability to process" → "The system can process" +- "It is important to note that the data shows" → "The data shows" + +--- + +### 23. Excessive Hedging + +**Problem:** Over-qualifying statements. + +**Before:** +> It could potentially possibly be argued that the policy might have some effect on outcomes. + +**After:** +> The policy may affect outcomes. + +--- + +### 24. Generic Positive Conclusions + +**Problem:** Vague upbeat endings. + +**Before:** +> The future looks bright for the company. Exciting times lie ahead as they continue their journey toward excellence. This represents a major step in the right direction. + +**After:** +> The company plans to open two more locations next year. + +--- + +## Process + +1. Read the input text carefully +2. Identify all instances of the patterns above +3. Rewrite each problematic section +4. Ensure the revised text: + - Sounds natural when read aloud + - Varies sentence structure naturally + - Uses specific details over vague claims + - Maintains appropriate tone for context + - Uses simple constructions (is/are/has) where appropriate +5. Present the humanized version + +## Output Format + +Provide: +1. The rewritten text +2. A brief summary of changes made (optional, if helpful) + +--- + +## Full Example + +**Before (AI-sounding):** +> Great question! Here is an essay on this topic. I hope this helps! +> +> AI-assisted coding serves as an enduring testament to the transformative potential of large language models, marking a pivotal moment in the evolution of software development. In today's rapidly evolving technological landscape, these groundbreaking tools—nestled at the intersection of research and practice—are reshaping how engineers ideate, iterate, and deliver, underscoring their vital role in modern workflows. +> +> At its core, the value proposition is clear: streamlining processes, enhancing collaboration, and fostering alignment. It's not just about autocomplete; it's about unlocking creativity at scale, ensuring that organizations can remain agile while delivering seamless, intuitive, and powerful experiences to users. The tool serves as a catalyst. The assistant functions as a partner. The system stands as a foundation for innovation. +> +> Industry observers have noted that adoption has accelerated from hobbyist experiments to enterprise-wide rollouts, from solo developers to cross-functional teams. The technology has been featured in The New York Times, Wired, and The Verge. Additionally, the ability to generate documentation, tests, and refactors showcases how AI can contribute to better outcomes, highlighting the intricate interplay between automation and human judgment. +> +> - 💡 **Speed:** Code generation is significantly faster, reducing friction and empowering developers. +> - 🚀 **Quality:** Output quality has been enhanced through improved training, contributing to higher standards. +> - ✅ **Adoption:** Usage continues to grow, reflecting broader industry trends. +> +> While specific details are limited based on available information, it could potentially be argued that these tools might have some positive effect. Despite challenges typical of emerging technologies—including hallucinations, bias, and accountability—the ecosystem continues to thrive. In order to fully realize this potential, teams must align with best practices. +> +> In conclusion, the future looks bright. Exciting times lie ahead as we continue this journey toward excellence. Let me know if you’d like me to expand on any section! + +**After (Humanized):** +> AI coding assistants speed up some tasks. In a 2024 study by Google, developers using Codex completed simple functions 55% faster than a control group, but showed no improvement on debugging or architectural decisions. +> +> The tools are good at boilerplate: config files, test scaffolding, repetitive refactors. They are bad at knowing when they are wrong. I have mass-accepted suggestions that compiled, passed lint, and still did the wrong thing because I stopped paying attention. +> +> Mira, an engineer at a fintech startup I interviewed, said she uses Copilot "like autocomplete for boring code" but reviews every line before committing. Jake, a senior dev at a larger company, disabled it after it kept suggesting patterns from a deprecated internal library. +> +> The productivity claims are hard to verify. GitHub says Copilot users "accept 30% of suggestions," but acceptance is not correctness, and correctness is not value. The 2024 Uplevel study found no statistically significant difference in pull-request throughput between teams with and without AI assistants. +> +> None of this means the tools are useless. It means they are tools. They do not replace judgment, and they do not eliminate the need for tests. If you do not have tests, you cannot tell whether the suggestion is right. + +**Changes made:** +- Removed chatbot artifacts ("Great question!", "I hope this helps!", "Let me know if...") +- Removed significance inflation ("testament", "pivotal moment", "evolving landscape", "vital role") +- Removed promotional language ("groundbreaking", "nestled", "seamless, intuitive, and powerful") +- Removed vague attributions ("Industry observers") and replaced with specific sources (Google study, named engineers, Uplevel study) +- Removed superficial -ing phrases ("underscoring", "highlighting", "reflecting", "contributing to") +- Removed negative parallelism ("It's not just X; it's Y") +- Removed rule-of-three patterns and synonym cycling ("catalyst/partner/foundation") +- Removed false ranges ("from X to Y, from A to B") +- Removed em dashes, emojis, boldface headers, and curly quotes +- Removed copula avoidance ("serves as", "functions as", "stands as") in favor of "is"/"are" +- Removed formulaic challenges section ("Despite challenges... continues to thrive") +- Removed knowledge-cutoff hedging ("While specific details are limited...") +- Removed excessive hedging ("could potentially be argued that... might have some") +- Removed filler phrases ("In order to", "At its core") +- Removed generic positive conclusion ("the future looks bright", "exciting times lie ahead") +- Replaced media name-dropping with specific claims from specific sources +- Used simple sentence structures and concrete examples + +--- + +## Reference + +This skill is based on [Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing), maintained by WikiProject AI Cleanup. The patterns documented there come from observations of thousands of instances of AI-generated text on Wikipedia. + +Key insight from Wikipedia: "LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely result that applies to the widest variety of cases." diff --git a/.skills/mcc-chatbot-authoring/SKILL.md b/.skills/mcc-chatbot-authoring/SKILL.md new file mode 100644 index 00000000..3934c268 --- /dev/null +++ b/.skills/mcc-chatbot-authoring/SKILL.md @@ -0,0 +1,107 @@ +--- +name: mcc-chatbot-authoring +description: Create, modify, repair, and wire Minecraft Console Client ChatBots and standalone `/script` bots. Use this whenever the user wants an MCC bot, C# script bot, chat or event handlers, periodic automation, movement logic, inventory logic, plugin-channel handling, or asks to fix or port an existing bot; default to standalone `//MCCScript` bots unless the user explicitly asks for a built-in MCC bot or repo wiring. +--- + +# MCC ChatBot Authoring + +Implement MCC chat bots against the bundled MCC authoring reference. Do not invent methods, lifecycle hooks, or registration steps. + +Always read: +- `references/authoring-reference.md` + +Load only as needed: +- `references/pattern-cookbook.md` for concrete standalone examples +- `assets/script-chatbot-template.cs` for the default standalone `/script` path +- `assets/builtin-chatbot-template.cs` only when the user explicitly requests a built-in bot + +If the current workspace contains an MCC checkout, verify final names and signatures against local sources before editing. The skill should still work without those files. +If there is no MCC checkout available, rely on the bundled reference and cookbook as the full source of truth for authoring patterns. + +## Choose the bot type first + +1. Default to a standalone script bot loaded with `/script`. +2. Only choose a built-in bot when the user explicitly asks for a compiled MCC bot, repo wiring, automatic config loading, or changes under the built-in bot system. +3. If the prompt is ambiguous, infer the likely target from commands, requested output files, or phrasing, state the assumption briefly, and proceed. +4. If a user says only "make a bot", do not create a built-in bot. + +## Source priority + +When the local MCC checkout is available, prefer these sources in this order: +1. `MinecraftClient/Scripting/ChatBot.cs` and current files under `MinecraftClient/ChatBots/` +2. the bundled `references/authoring-reference.md` +3. the bundled `references/pattern-cookbook.md` +4. older `MinecraftClient/config/` sample bots only for ideas, not as the default scaffold + +If an older sample conflicts with the current built-in bots, follow the current built-in bots. +If the local checkout is not available, do not block on missing repo files. Use the bundled references directly. + +## Hard rules + +- Only use lifecycle hooks and helpers documented in the bundled reference or verified in the target codebase. +- Do not send chat from `Initialize()`. Use `AfterGameJoined()` once the session can send messages. +- Prefer the current Brigadier command-registration pattern for built-in bots. Do not introduce `ChatBotCommand` unless the surrounding code already uses it. +- For message parsing, normalize with `GetVerbatim(text)` before `IsChatMessage(...)` or `IsPrivateMessage(...)`. +- Clean up everything you register or start: commands, plugin channels, threads, timers, and movement locks. +- If a built-in bot or long-running automation controls movement, follow a movement-lock pattern and release it on every stop path. Do not add `BotMovementLock` to a simple standalone `/script` bot unless the prompt or surrounding code explicitly needs shared movement coordination. +- For built-in bots, follow the host codebase's localization and config-comment conventions instead of scattering hardcoded user-facing text. +- For new code, prefer `Initialize()` over constructors for prerequisite checks and unload decisions. +- In this repo, built-in bot wiring usually means edits in `MinecraftClient/Settings.cs` and `MinecraftClient/McClient.cs` in addition to the bot class. +- For repair tasks, preserve the existing bot type and file layout unless the user explicitly asks for a conversion or restructure. + +## Standalone script bots + +Use the exact MCC metadata format from the bundled reference. +This is the default path for new work. + +The script should usually: +- keep `Initialize()` for cheap setup only +- use `GetText(...)`, `AfterGameJoined()`, and other event hooks for live behavior +- log with `LogToConsole(...)` +- send server chat or commands with `SendText(...)` +- use `PerformInternalCommand(...)` only for MCC internal commands +- add `//using MinecraftClient.Inventory` in metadata when the script uses inventory types explicitly +- reuse the standalone snippets in `references/pattern-cookbook.md` before inventing new scaffolding +- keep load instructions explicit, usually `/script FileName.cs` + +## Built-in bots + +Built-in bots usually need three pieces: +- the bot class itself +- config wiring in the chat-bot config model +- bot registration in the load flow + +If the codebase exposes commands, follow the built-in command and unload pattern from the bundled reference. If it exposes new settings or status text, follow the codebase's localization and config-comment patterns. + +When working in this checkout, built-in bot delivery usually needs: +- a new file under `MinecraftClient/ChatBots/` +- a config property inside `Settings.ChatBotConfigHealper.ChatBotConfig` +- a `BotLoad(new YourBot())` line inside `McClient.RegisterBots(...)` +- literal code snippets or patch hunks for the `Settings.cs` property and the `McClient.cs` registration line, not only prose notes + +## Repair flow + +When the user asks to fix or debug a bot: +- identify whether it is standalone or built-in and keep that shape unless told otherwise +- remove the broken pattern first, then preserve the intended behavior +- check especially for these regressions: `SendText(...)` in `Initialize()`, raw formatted chat parsing, inventory snapshot mutation, missing command unregister, missing plugin-channel unregister, and unreleased movement locks +- reuse the local repo's modern pattern instead of patching around a legacy helper when the helper is no longer current + +## Delivery checklist + +Before finishing, verify: +- the class inherits `ChatBot` +- the chosen overrides exist in the MCC ChatBot API +- standalone script metadata is exact if this is a `/script` bot +- built-in bots are fully wired into config and registration if needed +- all command registrations, background work, and movement locks are released +- files and namespaces match the surrounding codebase + +## Output + +When you implement or modify a bot: +- state whether it is a standalone script bot or built-in bot +- list the files you changed +- mention any required config keys or the MCC command used to load it +- when built-in wiring is involved, show the exact inserted code lines or patch hunks for `Settings.cs` and `McClient.cs` +- call out assumptions briefly if the user did not specify bot type or trigger behavior diff --git a/.skills/mcc-chatbot-authoring/assets/builtin-chatbot-template.cs b/.skills/mcc-chatbot-authoring/assets/builtin-chatbot-template.cs new file mode 100644 index 00000000..086c89e5 --- /dev/null +++ b/.skills/mcc-chatbot-authoring/assets/builtin-chatbot-template.cs @@ -0,0 +1,57 @@ +// Use this template only when the user explicitly requests a built-in MCC bot. + +using MinecraftClient.Scripting; +using Tomlet.Attributes; + +namespace MinecraftClient.ChatBots +{ + public class ExampleBot : ChatBot + { + private const string BotName = "ExampleBot"; + + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + public bool Enabled = false; + + public void OnSettingUpdate() + { + } + } + + public override void Initialize() + { + LogToConsole(BotName, "Initialized."); + } + + public override void AfterGameJoined() + { + } + + public override void GetText(string text) + { + text = GetVerbatim(text); + + string message = ""; + string username = ""; + + if (IsPrivateMessage(text, ref message, ref username)) + { + } + else if (IsChatMessage(text, ref message, ref username)) + { + } + } + + public override void OnUnload() + { + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + return false; + } + } +} diff --git a/.skills/mcc-chatbot-authoring/assets/script-chatbot-template.cs b/.skills/mcc-chatbot-authoring/assets/script-chatbot-template.cs new file mode 100644 index 00000000..72be0cae --- /dev/null +++ b/.skills/mcc-chatbot-authoring/assets/script-chatbot-template.cs @@ -0,0 +1,37 @@ +//MCCScript 1.0 + +MCC.LoadBot(new ExampleScriptBot()); + +//MCCScript Extensions + +public class ExampleScriptBot : ChatBot +{ + public override void Initialize() + { + LogToConsole("ExampleScriptBot initialized."); + } + + public override void AfterGameJoined() + { + // Safe place for startup chat or commands. + } + + public override void GetText(string text) + { + text = GetVerbatim(text); + + string message = ""; + string username = ""; + + if (IsPrivateMessage(text, ref message, ref username)) + { + LogToConsole("PM from " + username + ": " + message); + return; + } + + if (IsChatMessage(text, ref message, ref username)) + { + LogToConsole("Chat from " + username + ": " + message); + } + } +} diff --git a/.skills/mcc-chatbot-authoring/references/authoring-reference.md b/.skills/mcc-chatbot-authoring/references/authoring-reference.md new file mode 100644 index 00000000..e51495ee --- /dev/null +++ b/.skills/mcc-chatbot-authoring/references/authoring-reference.md @@ -0,0 +1,492 @@ +# MCC ChatBot Reference + +Self-contained authoring notes for Minecraft Console Client chat bots. + +## Bot types + +MCC supports two common authoring paths: +- standalone script bots loaded at runtime with `/script` +- built-in bots compiled into the MCC codebase + +Default to a standalone `/script` bot unless the user explicitly asks for a built-in bot or repo wiring. + +## Embedded current patterns + +This skill is intended to work even without an MCC checkout. The patterns below capture the important behavior that would otherwise be borrowed from current repo examples. + +If the local repo is available, you can verify against files such as `TestBot.cs`, `RemoteControl.cs`, `FollowPlayer.cs`, `ItemsCollector.cs`, and `Farmer.cs`. If it is not available, use the embedded patterns here directly. + +### Minimal chat parsing pattern + +Use this as the baseline for public/private chat handling: + +```csharp +public override void GetText(string text) +{ + string message = ""; + string sender = ""; + text = GetVerbatim(text); + + if (IsPrivateMessage(text, ref message, ref sender)) + { + LogToConsole("PM from " + sender + ": " + message); + } + else if (IsChatMessage(text, ref message, ref sender)) + { + LogToConsole("Chat from " + sender + ": " + message); + } +} +``` + +What matters: +- normalize first with `GetVerbatim(text)` +- handle PMs before public chat if both matter +- keep simple chat bots deterministic and small + +### Owner-gated PM control pattern + +Use this when a bot owner should be able to whisper MCC internal commands: + +```csharp +public override void GetText(string text) +{ + text = GetVerbatim(text).Trim(); + string command = ""; + string sender = ""; + + if (IsPrivateMessage(text, ref command, ref sender) + && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLowerInvariant())) + { + CmdResult result = new(); + PerformInternalCommand(command, ref result); + SendPrivateMessage(sender, result.ToString()); + } +} +``` + +What matters: +- `PerformInternalCommand(...)` is for MCC commands, not server chat commands +- owner gating should use `Settings.Config.Main.Advanced.BotOwners` +- if `CmdResult` is used in a standalone script, add `//using MinecraftClient.CommandHandler` + +### Periodic work pattern + +Use `Update()` plus a counter or timestamp for simple repeated work: + +```csharp +private int count = 0; + +public override void Update() +{ + count++; + if (count < Settings.DoubleToTick(60)) + return; + + count = 0; + SendText("/list"); +} +``` + +What matters: +- avoid a worker thread for simple periodic loops +- avoid `Thread.Sleep(...)` inside `Update()` +- if sending chat, do it from a join-safe path like `Update()` or `AfterGameJoined()`, not `Initialize()` + +### Built-in Brigadier command pattern + +Use this for built-in command bots: + +```csharp +public override void Initialize() +{ + McClient.dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + McClient.dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); +} + +public override void OnUnload() +{ + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); +} +``` + +What matters: +- register commands in `Initialize()` +- unregister the command tree in `OnUnload()` +- remove the help child you added in `OnUnload()` +- prefer this over legacy command wrappers for new built-in work + +### Built-in config and wiring pattern + +Use this as the default built-in shape: + +```csharp +public class ExampleBot : ChatBot +{ + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + public bool Enabled = false; + + public void OnSettingUpdate() + { + } + } +} +``` + +Typical host wiring shape: + +```csharp +[TomlPrecedingComment("$ChatBot.ExampleBot$")] +public ChatBots.ExampleBot.Configs ExampleBot +{ + get { return ChatBots.ExampleBot.Config; } + set { ChatBots.ExampleBot.Config = value; ChatBots.ExampleBot.Config.OnSettingUpdate(); } +} +``` + +```csharp +if (Config.ChatBot.ExampleBot.Enabled) { BotLoad(new ExampleBot()); } +``` + +What matters: +- built-in configurable bots default to `Enabled = false` +- `OnSettingUpdate()` is the place to normalize config values +- built-in delivery is incomplete without both config wiring and load registration + +### Movement gating pattern + +Use this shape when a built-in bot owns movement: + +```csharp +public override void Initialize() +{ + if (!GetEntityHandlingEnabled()) + { + LogToConsole("Entity handling is required."); + UnloadBot(); + return; + } + + if (!GetTerrainEnabled()) + { + LogToConsole("Terrain handling is required."); + UnloadBot(); + return; + } +} +``` + +```csharp +var movementLock = BotMovementLock.Instance; +if (movementLock is { IsLocked: true }) + return; + +movementLock?.Lock("Example Bot"); +``` + +```csharp +public override void OnUnload() +{ + BotMovementLock.Instance?.UnLock("Example Bot"); +} +``` + +What matters: +- guard terrain and entity handling before movement logic +- built-in movement bots should use `BotMovementLock` +- release the lock on every stop path, including unload and disconnect-sensitive flows + +### Dropped-item collector pattern + +Use this as the standalone item-search baseline: + +```csharp +private DateTime nextScan = DateTime.MinValue; + +public override void Update() +{ + var now = DateTime.UtcNow; + if (now < nextScan || ClientIsMoving()) + return; + + nextScan = now.AddSeconds(1); + + var here = GetCurrentLocation(); + var target = GetEntities().Values + .Where(entity => entity.Type == EntityType.Item && entity.Location.Distance(here) <= 15) + .OrderBy(entity => entity.Location.Distance(here)) + .FirstOrDefault(); + + if (target != null) + MoveToLocation(target.Location); +} +``` + +What matters: +- simple standalone collectors do not need a worker thread +- simple standalone collectors also do not need `BotMovementLock` by default +- `GetEntities()` plus distance ordering is the core search pattern + +### Inventory selection pattern + +Use this as the default hotbar-switch pattern: + +```csharp +private bool TrySwitchToItem(ItemType itemType) +{ + var inventory = GetPlayerInventory(); + + var hotbarSlots = inventory.SearchItem(itemType) + .Where(slot => slot >= 36 && slot <= 44) + .ToArray(); + + if (hotbarSlots.Length == 0) + return false; + + ChangeSlot((short)(hotbarSlots[0] - 36)); + return true; +} +``` + +What matters: +- guard with `GetInventoryEnabled()` +- search inventory snapshots, but mutate real server state with helpers like `ChangeSlot(...)` +- do not treat local `Container.Items` mutation as real inventory manipulation + +Use the older config examples only for ideas, not as primary scaffolding. + +## Standalone script format + +A standalone script bot has two parts in this order: +1. metadata block +2. one or more C# classes, with the main bot class inheriting `ChatBot` + +Required metadata rules: +- line 1 must be exactly `//MCCScript 1.0` +- metadata must include `MCC.LoadBot(new BotClassName());` +- metadata ends with `//MCCScript Extensions` +- optional metadata directives use `//using Namespace` and `//dll SomeLibrary.dll` +- do not insert a space after `//` in metadata directives + +Typical runtime flow: +- place the script file beside MCC +- connect to a server +- load it with `/script YourBotFile.cs` + +### Namespace linking for inventory code + +If a standalone script uses inventory-specific types such as `Container`, `ItemType`, `WindowActionType`, or `ItemMovingHelper`, add this metadata import: + +```csharp +//using MinecraftClient.Inventory +``` + +For built-in bots, use a normal C# import: + +```csharp +using MinecraftClient.Inventory; +``` + +## Lifecycle summary + +Common lifecycle hooks: +- `Initialize()` + called once when the bot loads; use it for cheap setup only +- `AfterGameJoined()` + called after the server has been joined successfully, and again after reconnecting; use it when chat can be sent +- `Update()` + called roughly every 100 ms +- `OnUnload()` + called when the bot unloads; release resources here +- `OnDisconnect(DisconnectReason reason, string message)` + called on disconnect; stop background work and clean up reconnect-sensitive state here + +Important rule: +- do not send chat from `Initialize()`; use `AfterGameJoined()` instead +- prefer `Initialize()` over constructors for environment checks and resource setup + +## Common event hooks + +Useful event hooks include: +- `GetText(string text)` +- `GetText(string text, string? json)` +- `OnPlayerJoin(Guid uuid, string name)` +- `OnPlayerLeave(Guid uuid, string? name)` +- `OnEntitySpawn(Entity entity)` +- `OnEntityDespawn(Entity entity)` +- `OnEntityMove(Entity entity)` +- `OnHealthUpdate(float health, int food)` +- `OnMapData(...)` +- `OnInventoryUpdate(int inventoryId)` +- `OnPluginMessage(string channel, byte[] data)` +- `OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound)` + +Only override hooks that actually exist in the target MCC ChatBot API. + +## Common helpers + +Text and messaging helpers: +- `GetVerbatim(text)` strips Minecraft formatting codes +- `IsChatMessage(text, ref message, ref sender)` parses public chat +- `IsPrivateMessage(text, ref message, ref sender)` parses private chat +- `IsValidName(username)` validates a Minecraft username +- `SendText(text)` sends chat or server commands +- `SendPrivateMessage(player, message)` sends a private message +- `PerformInternalCommand(command, ...)` runs an internal MCC command, not a server command +- `LogToConsole(text)` writes a bot-prefixed console message + +Lifecycle and threading helpers: +- `InvokeOnMainThread(...)` +- `ScheduleOnMainThread(...)` +- `ReconnectToTheServer(...)` +- `UnloadBot()` +- `BotLoad(chatBot)` +- `RunScript(filename, ...)` + +World and player-state helpers: +- `GetWorld()` +- `GetEntities()` +- `GetCurrentLocation()` +- `ClientIsMoving()` +- `GetOnlinePlayers()` +- `GetOnlinePlayersWithUUID()` +- `GetServerTPS()` +- `GetProtocolVersion()` + +Movement and inventory helpers: +- `MoveToLocation(...)` +- `LookAtLocation(...)` +- `GetInventoryEnabled()` +- `GetPlayerInventory()` +- `GetInventories()` +- `GetItemMovingHelper(...)` +- `WindowAction(...)` +- `ChangeSlot(...)` +- `GetCurrentSlot()` +- `UseItemInHand()` +- `UseItemInLeftHand()` +- `CloseInventory(...)` +- `DigBlock(...)` +- `InteractEntity(...)` + +## Inventory notes + +Inventory handling is optional in MCC. Check `GetInventoryEnabled()` before relying on inventory state or mutation. + +Important behavior: +- `GetPlayerInventory()` returns a snapshot copy of the player's inventory +- `GetInventories()` returns current container snapshots +- writing to those `Container` objects locally does not update the server +- to actually change inventory state, use `ChangeSlot(...)`, `WindowAction(...)`, `GetItemMovingHelper(...)`, `UseItemInHand()`, or related helpers + +Useful practical facts: +- hotbar selection uses `ChangeSlot(0..8)` +- hotbar slots are commonly `36..44` in inventory slot numbering +- the offhand slot is commonly `45` +- `Container.SearchItem(...)` is the normal way to locate items by type + +Good inventory workflow: +1. guard with `GetInventoryEnabled()` +2. read the current container using `GetPlayerInventory()` +3. locate slots with `SearchItem(...)` or `Items` +4. mutate server state using `ChangeSlot(...)`, `WindowAction(...)`, or `ItemMovingHelper` +5. if needed, react to `OnInventoryUpdate(...)`, `OnInventoryOpen(...)`, or `OnInventoryClose(...)` + +Plugins and channels: +- `RegisterPluginChannel(channel)` +- `UnregisterPluginChannel(channel)` +- `SendPluginChannelMessage(channel, data, ...)` + +## Built-in bot pattern + +A built-in bot usually follows this shape: +- a class that inherits `ChatBot` +- an optional static `Config` field +- a nested `[TomlDoNotInlineObject]` `Configs` class for settings +- an `Enabled = false` setting by default +- `OnSettingUpdate()` to normalize or validate config values + +If the bot is configurable, the host codebase usually also needs: +- config wiring in the chat-bot config model +- load registration so enabled bots are instantiated automatically + +In this MCC checkout, the usual built-in wiring points are: +- `MinecraftClient/Settings.cs` inside `Settings.ChatBotConfigHealper.ChatBotConfig` +- `MinecraftClient/McClient.cs` inside `RegisterBots(...)` + +Match the surrounding `[TomlPrecedingComment(...)]`, property-forwarding, and `BotLoad(new YourBot())` style instead of inventing a different config path. +When presenting built-in wiring, prefer literal code snippets or patch hunks for those two edits so the wiring can be checked directly. + +If the bot adds user-facing settings or messages, follow the host codebase's localization and config-comment conventions instead of scattering hardcoded strings. + +## Command pattern + +For standalone script bots, prefer chat or PM handling in `GetText(...)` unless the user explicitly asks for built-in command registration. + +For built-in commands, prefer the current Brigadier dispatcher pattern: +- register commands in `Initialize()` +- add a help entry if the bot exposes commands +- unregister the command tree in `OnUnload()` +- remove any help child added during registration in `OnUnload()` + +Avoid using legacy command wrappers if the current codebase uses direct dispatcher registration. +In this checkout, treat direct `McClient.dispatcher.Register(...)` usage in current built-in bots as the source of truth. + +## Concurrency and cleanup + +If the bot starts background work: +- stop it in `OnUnload()` +- stop it in `OnDisconnect(...)` +- consider resetting state in `AfterGameJoined()` after relog +- prefer `Update()` plus counters or timestamps over unmanaged threads when the task is simple periodic work + +If the bot controls movement: +- use a movement-lock discipline +- release the lock on every stop path +- avoid fighting other movement bots +- `BotMovementLock` is mainly for built-in bots or shared long-running automation; a simple standalone script that just calls `MoveToLocation(...)` does not need it by default + +When interacting with client state from background logic, use the main-thread helpers when required by the codebase. + +## Practical defaults + +For simple chat bots: +- normalize text with `GetVerbatim(text)` +- inspect private chat first if the bot listens for whispers +- then inspect public chat +- keep response logic small and deterministic + +For long-running automation bots: +- guard prerequisites early, such as entity handling or terrain support +- fail fast with a clear log message if prerequisites are missing +- release all ongoing work cleanly on unload and disconnect + +## Common pitfalls + +- Incorrect metadata line 1 will break standalone script loading. +- Missing `MCC.LoadBot(new BotClassName())` will prevent standalone script registration. +- Sending chat in `Initialize()` is too early. +- Doing prerequisite checks or unloading from the constructor is harder to reason about than using `Initialize()`. +- Parsing raw formatted text without `GetVerbatim()` causes brittle chat matching. +- Inventing methods not present in the MCC ChatBot API leads to dead code. +- Built-in bot work is incomplete if config or registration wiring is missing. +- Command bots are incomplete if they register commands but do not unregister them. +- `RegisterChatBotCommand(...)` comes from older samples and is not a reliable current pattern for this checkout. +- `ChatBotCommand` exists, but the current built-in bots use Brigadier directly; do not prefer `ChatBotCommand` for new work. +- Blocking `Thread.Sleep(...)` inside `Update()` is a bad default. Prefer timers, counters, or timestamp-based scheduling. +- Mutating the `Container` returned by `GetPlayerInventory()` does not change the server. Use inventory actions instead. diff --git a/.skills/mcc-chatbot-authoring/references/pattern-cookbook.md b/.skills/mcc-chatbot-authoring/references/pattern-cookbook.md new file mode 100644 index 00000000..4d17c85c --- /dev/null +++ b/.skills/mcc-chatbot-authoring/references/pattern-cookbook.md @@ -0,0 +1,330 @@ +# MCC Pattern Cookbook + +Concrete patterns for standalone MCC `/script` bots. Use these before inventing new scaffolding. + +## Periodic task without threads + +Use `Update()` plus a timestamp or counter. This comes from the old `sample-script-with-task.cs` example and still holds up well. + +```csharp +public class PeriodicTaskBot : ChatBot +{ + private DateTime nextRun = DateTime.MinValue; + + public override void Update() + { + var now = DateTime.UtcNow; + if (now < nextRun) + return; + + nextRun = now.AddSeconds(30); + LogDebugToConsole("Running periodic task"); + SendText("/ping"); + } +} +``` + +Why this pattern is good: +- stays on MCC's normal tick flow +- avoids background threads for simple periodic work +- keeps the bot responsive to unload and disconnect + +## Chat and PM handling + +This combines the useful parts of `TestBot`, `sample-script-pm-forwarder.cs`, and `RemoteControl.cs`. + +```csharp +public override void GetText(string text) +{ + text = GetVerbatim(text); + + string message = ""; + string sender = ""; + + if (IsPrivateMessage(text, ref message, ref sender)) + { + LogToConsole("PM from " + sender + ": " + message); + return; + } + + if (IsChatMessage(text, ref message, ref sender)) + { + LogToConsole("Chat from " + sender + ": " + message); + } +} +``` + +Owner-gated internal command handling: + +Add `//using MinecraftClient.CommandHandler` in the script metadata if you use `CmdResult`. + +```csharp +public override void GetText(string text) +{ + text = GetVerbatim(text).Trim(); + + string command = ""; + string sender = ""; + + if (IsPrivateMessage(text, ref command, ref sender) + && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLowerInvariant())) + { + CmdResult result = new(); + PerformInternalCommand(command, ref result); + SendPrivateMessage(sender, result.ToString()); + } +} +``` + +## Movement with prerequisite checks + +Modern movement code should copy the guard style from current built-in bots, not the older constructor-heavy scripts. + +```csharp +public override void Initialize() +{ + if (!GetEntityHandlingEnabled() || !GetTerrainEnabled()) + { + LogToConsole("Entity handling and terrain handling are required."); + UnloadBot(); + } +} +``` + +Simple "look at nearest player" logic adapted from `AutoLook.cs`: + +```csharp +private Entity? trackedPlayer = null; + +public override void OnEntitySpawn(Entity entity) +{ + TryTrack(entity); +} + +public override void OnEntityDespawn(Entity entity) +{ + if (trackedPlayer != null && entity.ID == trackedPlayer.ID) + trackedPlayer = null; +} + +public override void OnEntityMove(Entity entity) +{ + if (!TryTrack(entity)) + return; + + LookAtLocation(entity.Location); +} + +private bool TryTrack(Entity entity) +{ + if (entity.Type != EntityType.Player) + return false; + + if (trackedPlayer == null) + { + trackedPlayer = entity; + return true; + } + + if (GetCurrentLocation().Distance(entity.Location) < GetCurrentLocation().Distance(trackedPlayer.Location)) + trackedPlayer = entity; + + return trackedPlayer.ID == entity.ID; +} +``` + +## Search for dropped items and move to them + +This is the safest pattern to preserve from `ItemsCollector.cs` for standalone scripts. + +```csharp +public class NearbyItemsBot : ChatBot +{ + private DateTime nextScan = DateTime.MinValue; + + public override void Initialize() + { + if (!GetEntityHandlingEnabled() || !GetTerrainEnabled()) + { + LogToConsole("Entity handling and terrain handling are required."); + UnloadBot(); + } + } + + public override void Update() + { + var now = DateTime.UtcNow; + if (now < nextScan || ClientIsMoving()) + return; + + nextScan = now.AddSeconds(1); + + var here = GetCurrentLocation(); + var target = GetEntities().Values + .Where(entity => entity.Type == EntityType.Item && entity.Location.Distance(here) <= 15) + .OrderBy(entity => entity.Location.Distance(here)) + .FirstOrDefault(); + + if (target != null) + MoveToLocation(target.Location); + } +} +``` + +Why this version is better than older farming scripts: +- no unmanaged worker thread +- no busy wait loop around movement +- uses the current `GetEntities()` pattern + +## Search for blocks or crops in the world + +The old sugar cane and mining scripts still contain a useful search idea: use `GetWorld().FindBlock(...)`, then filter and sort. + +```csharp +var targets = GetWorld() + .FindBlock(GetCurrentLocation(), Material.SugarCane, 16) + .Where(block => + GetWorld().GetBlock(new Location(block.X, block.Y - 1, block.Z)).Type == Material.SugarCane) + .OrderBy(block => block.Distance(GetCurrentLocation())) + .ToList(); +``` + +Use this as a search primitive. Then decide separately how to move, dig, or harvest. + +## Inventory access and manipulation + +If a standalone script uses inventory types directly, add this import in the metadata block: + +```csharp +//using MinecraftClient.Inventory +``` + +For built-in bots, add: + +```csharp +using MinecraftClient.Inventory; +``` + +Always guard inventory logic first: + +```csharp +public override void Initialize() +{ + if (!GetInventoryEnabled()) + { + LogToConsole("Inventory handling is required."); + UnloadBot(); + } +} +``` + +Important rule: +- `GetPlayerInventory()` returns a snapshot copy, so editing its `Items` dictionary does not change the server +- actual changes must go through `ChangeSlot(...)`, `WindowAction(...)`, `GetItemMovingHelper(...)`, `UseItemInHand()`, and related helpers + +### Search inventory for an item + +This combines the useful current logic from `Farmer.cs` and `AutoEat.cs`. + +```csharp +private bool TrySwitchToItem(ItemType itemType) +{ + var inventory = GetPlayerInventory(); + + if (inventory.Items.TryGetValue(GetCurrentSlot() - 36, out var held) && held.Type == itemType) + return true; + + var hotbarSlots = inventory.SearchItem(itemType) + .Where(slot => slot >= 36 && slot <= 44) + .ToArray(); + + if (hotbarSlots.Length == 0) + return false; + + ChangeSlot((short)(hotbarSlots[0] - 36)); + return true; +} +``` + +Use this for simple hotbar selection. For deeper inventory reshuffling, built-in bots usually need more helper logic. + +### Move an item into the hotbar + +Use this when the item exists in inventory but is not already on the hotbar. + +```csharp +private bool TryMoveItemToHotbar(ItemType itemType, short targetHotbarSlot = 0) +{ + var inventory = GetPlayerInventory(); + var matches = inventory.SearchItem(itemType); + + if (matches.Length == 0) + return false; + + var targetInventorySlot = 36 + targetHotbarSlot; + + if (matches[0] >= 36 && matches[0] <= 44) + { + ChangeSlot((short)(matches[0] - 36)); + return true; + } + + var movingHelper = GetItemMovingHelper(inventory); + movingHelper.Swap(matches[0], targetInventorySlot); + ChangeSlot(targetHotbarSlot); + return true; +} +``` + +Why this pattern is good: +- it reads the current snapshot first +- it does not pretend local `Container` edits affect the server +- it uses the item-moving helper for real inventory manipulation + +### Drop or click items with window actions + +Use `WindowAction(...)` when the bot needs direct inventory clicks or dropping behavior. + +```csharp +private void DropAllOfType(ItemType itemType) +{ + var inventory = GetPlayerInventory(); + + foreach (int slot in inventory.SearchItem(itemType)) + WindowAction(0, slot, WindowActionType.DropItemStack); +} +``` + +Use this pattern carefully: +- verify the correct inventory ID first +- prefer reacting to `OnInventoryUpdate(...)` for larger inventory workflows +- for crafting or chest workflows, use `GetInventories()` and `CloseInventory(...)` as needed + +## Built-in command bot pattern + +Only use this when the user explicitly asks for a built-in bot. + +```csharp +public override void Initialize() +{ + McClient.dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + McClient.dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); +} + +public override void OnUnload() +{ + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); +} +``` + +Use a built-in bot only when the user explicitly asks for compiled MCC behavior or repo wiring. diff --git a/.skills/mcc-dev-workflow/SKILL.md b/.skills/mcc-dev-workflow/SKILL.md new file mode 100644 index 00000000..06614892 --- /dev/null +++ b/.skills/mcc-dev-workflow/SKILL.md @@ -0,0 +1,362 @@ +--- +name: mcc-dev-workflow +description: Build, run, and debug Minecraft Console Client (MCC) against a real local Minecraft Java server on Linux, macOS, or WSL. Use this whenever the user wants to compile MCC, start or inspect a local test server, connect MCC to a server, debug protocol or login issues, validate a code change end-to-end, or run MCC commands on a real server instead of guessing from static code. +--- + +# MCC Development Workflow + +Use this skill when the task needs a real local server loop, not just code reading. + +## Defaults + +- Solution: `MinecraftClient.sln` +- Runtime target: `.NET 10` / `net10.0` +- Environment: Linux, macOS, or WSL with Java, tmux, python3, and dotnet available +- Default server root after `source tools/mcc-env.sh`: `${MCC_SERVERS:-/MinecraftOfficial/downloads}` +- Default validation target when the user does not specify a version: `1.21.11` + +## Console modes + +MCC supports two console modes selectable via `ConsoleMode` in `[Console.General]`: + +| Mode | Backend | Best for | +|------|---------|----------| +| `classic` | `ClassicConsoleBackend` (ConsoleInteractive) | Normal use, legacy CI/scripts, `FileInput` mode | +| `tui` | `TuiConsoleBackend` (Avalonia/Consolonia) | Full-screen TUI with scrollable log, command input, popup inventory | + +Both modes support the same commands and input/output through `ConsoleIO.Backend`. The mode is determined at startup from config; `BasicIO` CLI arg overrides to simple stdio. + +## Core rules + +- Prefer a real local server over static reasoning for protocol, login, movement, inventory, entity, or command-path work. +- Treat tmux `mc-*` sessions as shared state. Do not run multi-version server workflows in parallel unless the harness explicitly isolates them. +- For scripted or repeatable runs, use a generated temporary config. Do not edit the repo-root `MinecraftClient.ini` as part of the test loop. +- A server log line containing `Done (` means startup finished. It does not guarantee that RCON is ready on the first attempt. Retry early `mc-rcon` commands. +- When instructions, docs, and code disagree, trust current code and current tool behavior first. + +## Shared server, isolated MCC sessions + +- `mc-*` commands operate on the shared local Minecraft server. +- `mcc-*` commands operate on one MCC client session. +- The default `session` is the current worktree name. +- The default username is derived from `session`, unless you pass `--username`. +- Session files live under `${TMPDIR:-/tmp}/mcc-debug//`. +- `MCC_SERVERS` stays the shared server-root override. + +Keep shared servers running by default. Do not stop or reset them unless the user explicitly asks for that, or you need to switch server versions. + +Two worktrees can debug against one shared server like this: + +```bash +# worktree A +cd ~/Minecraft/Minecraft-Console-Client +source tools/mcc-env.sh +mc-start 1.21.11 +mcc-debug -v 1.21.11 --file-input + +# worktree B +cd ~/Minecraft/Minecraft-Console-Client-foo +source tools/mcc-env.sh +mcc-debug -v 1.21.11 --file-input + +# from each worktree, mcc-* targets that worktree's default session +mcc-state +``` + +If you want two MCC sessions from the same worktree, pass `--session NAME` explicitly. + +## tmpfs build mode + +Use this on machines with enough RAM when you want worktree-isolated builds outside the repo tree: + +```bash +source tools/mcc-env.sh +export MCC_BUILD_MODE=tmpfs +mcc-build +mcc-build-clean +``` + +`MCC_BUILD_MODE=tmpfs` redirects build output to `/dev/shm/mcc-build//` on Linux, or `${TMPDIR:-/tmp}/mcc-build//` if `/dev/shm` is unavailable. + +## Preflight and reset + +Before scripted runs, especially on macOS or in a reused tmux environment: + +```bash +source tools/mcc-env.sh +mcc-preflight 1.21.11 +mc-reset-test-env 1.21.11 +``` + +`mcc-preflight` checks Java, tmux, dotnet, python3, and server directories. It also resolves common Homebrew Java paths on macOS. `mc-reset-test-env` clears stale tmux sessions and stale `stdin.pipe` files before they turn into misleading startup failures. + +## Build + +```bash +source tools/mcc-env.sh +mcc-build +``` + +Use `mcc-build` for normal local development so any `MCC_BUILD_MODE=tmpfs` routing stays active. Only use raw `dotnet build` when you are intentionally debugging the build system itself. + +## Server management + +Interactive shell: + +```bash +source tools/mcc-env.sh +SESSION="$(_mcc_resolve_session)" +USERNAME="$(_mcc_resolve_username "$SESSION")" +mc-start 1.21.11 +mc-log 1.21.11 100 +mc-rcon "op $USERNAME" +mc-stop 1.21.11 +``` + +Non-interactive shell: + +```bash +tools/start-server.sh 1.21.11 +tools/mc-rcon.sh "op mcc_smoke_a" +``` + +If the servers live outside the repo, set `MCC_SERVERS` before sourcing or invoking the tools: + +```bash +export MCC_SERVERS=/home/anon/Minecraft/Servers +source tools/mcc-env.sh +``` + +## One-step debug session (recommended) + +The `tools/mcc-debug.sh` script handles build, server startup, config preparation, and MCC launch in one step: + +```bash +source tools/mcc-env.sh + +# Classic mode with FileInput (script-driven debugging): +mcc-debug -v 1.21.11 --file-input + +# Classic mode interactive (attach via tmux): +mcc-debug -v 1.21.11 + +# TUI mode: +mcc-debug -v 1.21.11 -m tui + +# With debug messages enabled from start: +mcc-debug -v 1.21.11 --file-input --debug-on + +# Skip build (already built): +mcc-debug -v 1.21.11 --file-input --no-build +``` + +### What mcc-debug.sh does + +1. Builds MCC (unless `--no-build`) +2. Creates a clean temp config at `${TMPDIR:-/tmp}/mcc-debug//MinecraftClient.debug.ini` +3. Ensures server is running (starts if not, waits for `Done (`) +4. Launches MCC in a session-scoped tmux session and session-scoped log/input/pid files + +### After launch + +- **FileInput mode**: drive MCC via `mcc-cmd --session smoke-a "debug state"`, or just `mcc-cmd "debug state"` from the same worktree +- **Interactive/TUI mode**: attach with `tmux attach -t mcc-` +- **Logs**: `mcc-log-mcc --session smoke-a` or `tail -f "${TMPDIR:-/tmp}/mcc-debug//mcc-debug.log"` +- **Server RCON**: grant op or gamemode to the username derived from that session + +## Debug commands (in-game) + +### `/debug [on|off]` + +Toggles debug logging. Now correctly syncs both `Settings.Config.Logging.DebugMessages` and `McClient.Log.DebugEnabled`. + +### `/debug state` + +Prints a one-shot summary of MCC's internal state: + +``` +=== MCC Debug State === +Server: localhost:25565 +Username: mcc_smoke_a +Protocol: 774 +GameMode: 1 +Health: 20.0 +Food: 20 +Location: 0.50, 80.00, 0.50 +TPS: 20.0 +Console: ClassicConsoleBackend (or TuiConsoleBackend) +Features: Terrain Inventory Entity +Debug: ON +Bots (3): AutoFishing, FileInputBot, ScriptScheduler +Players: 2 online +``` + +This works in both classic and TUI modes. + +## Classic mode debugging + +### Agent workflow (FileInput mode) + +For agents calling MCC commands programmatically: + +```bash +source tools/mcc-env.sh +SESSION="smoke-a" +mcc-debug -v 1.21.11 --file-input --session "$SESSION" --no-build + +# Send commands: +mcc-cmd --session "$SESSION" "debug state" +mcc-cmd --session "$SESSION" "inventory player list" +mcc-cmd --session "$SESSION" "entity" + +# Check results: +mcc-log-mcc --session "$SESSION" + +# Stop: +mcc-cmd --session "$SESSION" "quit" +mcc-kill --session "$SESSION" +mc-stop 1.21.11 +``` + +### Interactive workflow + +```bash +source tools/mcc-env.sh +SESSION="live-a" +mcc-debug -v 1.21.11 --session "$SESSION" + +# In another terminal: +tmux attach -t "mcc-$SESSION" +# Type commands directly in MCC console +``` + +## TUI mode debugging + +TUI mode runs Consolonia full-screen in a tmux session. Key differences: + +1. **No pipe/redirect**: TUI needs a real tty. Cannot `| tee` or redirect stdout. +2. **Log output is in-screen**: all output appears in the scrollable log area. +3. **Keyboard shortcuts**: PageUp/PageDown scroll, ESC exits. +4. **`/debug state`**: the primary way to inspect internal state since external log tailing is not available. +5. **Dialog windows**: `/inventui` opens as an overlay dialog instead of a separate screen. + +### Agent workflow for TUI mode + +```bash +source tools/mcc-env.sh +SESSION="tui-a" +mcc-debug -v 1.21.11 -m tui --session "$SESSION" --no-build + +# Cannot use mcc-cmd (no FileInput); must use tmux send-keys: +tmux send-keys -t "mcc-$SESSION" "/debug state" Enter + +# Read TUI screen: +tmux capture-pane -t "mcc-$SESSION" -p -S -30 + +# Stop: +tmux send-keys -t "mcc-$SESSION" Escape +``` + +**Caveat with tmux send-keys and Consolonia**: When sending text containing `/`, the Enter key may need to be sent separately: +```bash +tmux send-keys -t "mcc-$SESSION" "/inventory player list" +tmux send-keys -t "mcc-$SESSION" Enter +``` + +## mcc-env.sh quick reference + +After `source tools/mcc-env.sh`: + +| Function | Description | +|----------|-------------| +| `mc-start VER` | Start MC server in tmux | +| `mc-stop VER` | Graceful stop via stdin pipe | +| `mc-log VER [N]` | Capture last N lines of server output | +| `mc-rcon "CMD"` | Send RCON command | +| `mc-kill VER` | Force-kill server tmux session | +| `mc-list` | List running MC server sessions | +| `mc-wait-ready VER [SEC]` | Wait for server `Done (` | +| `mc-wait-stop VER [SEC]` | Wait for server shutdown, with force-kill fallback | +| `mc-reset-test-env [--all|VER...]` | Reset shared tmux server state and stale pipes | +| `mcc-build` | Build MCC | +| `mcc-publish --rid ` | Publish MCC with the repo's CI-like defaults | +| `mcc-build-clean` | Clear the current worktree's build output | +| `mcc-run [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug --file-input --no-build` | +| `mcc-tui [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug -m tui --no-build` | +| `mcc-cmd [--session NAME] "CMD"` | Append a command to one session's input file | +| `mcc-kill [--session NAME]` | Kill one MCC process and session | +| `mcc-debug [OPTS]` | One-step debug session (see above) | +| `mcc-log-mcc [--session NAME]` | Tail one MCC debug log | +| `mcc-state [--session NAME]` | Send `debug state` and print the last 30 log lines | +| `mcc-preflight [VER...]` | Verify Java, tmux, dotnet, python3, and server dirs | + +## Temporary config recipe + +```bash +source tools/mcc-env.sh +SESSION="smoke-a" +USERNAME="$(_mcc_resolve_username "$SESSION")" +CFG="$(_mcc_session_root "$SESSION")/MinecraftClient.debug.ini" +mkdir -p "$(_mcc_session_root "$SESSION")" +bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \ + "$CFG" \ + "1.21.11" \ + "$USERNAME" +``` + +For TUI mode, also add: +```bash +sed -i 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" +``` + +## Verify connection and a basic command + +MCC output should include: + +- `[MCC] Server was successfully joined.` + +Server output should include the session-derived username, for example: + +- `mcc_smoke_a joined the game` + +Basic command check: + +```bash +mcc-cmd --session smoke-a "inventory player list" +``` + +If a scripted run fails before MCC joins, check for a harness problem before assuming a product regression. Missing `mcc.log`, a pre-join `Connection refused`, or a server that never reached `Done (` usually means shared-state cleanup or startup failed. + +## Typical debug loop + +1. `source tools/mcc-env.sh` +2. `mcc-debug -v 1.21.11 --file-input` (or `-m tui`) +3. Confirm `Server was successfully joined` in log +4. `mcc-cmd "debug state"` to verify MCC state +5. Run test commands +6. Inspect log output +7. `mcc-cmd "quit"` and `mc-stop 1.21.11` +8. Edit code, rebuild, repeat + +## Debugging tips + +- **`/debug state` is your primary diagnostic tool** in both modes. Use it first to verify connection, mode, and feature flags. +- **`/debug on` now correctly enables debug logging** at runtime. Previous versions had a bug where `Log.DebugEnabled` was not synced. +- Protocol mismatches usually show up as a version line such as `Server version : 1.21.11 (protocol vNNN)` before the failure. +- If an early `mc-rcon` command fails, retry it before assuming the server setup is broken. +- If a supposedly isolated run behaves strangely, check `tmux list-sessions` and kill stale `mc-*` sessions first. +- Legacy `1.8` and `1.8.9` servers may need `use-native-transport=false` in `server.properties` on some Linux environments. +- For timing-sensitive work, do not trust wall-clock intuition. Use a real server run and capture evidence from logs or test scripts. +- **TUI mode tip**: if the terminal becomes unresponsive after a crash, run `stty sane && reset` to restore it. +- **tmux capture trick**: `tmux capture-pane -t mcc- -p -S -50` captures the last 50 lines of a tmux session without attaching. + +## Tool files + +| File | Purpose | +|------|---------| +| `tools/mcc-env.sh` | Shell functions for server/MCC management | +| `tools/mcc-debug.sh` | One-step debug session launcher | +| `tools/mcc-log-tail.sh` | Log tailing for MCC and/or server | +| `tools/start-server.sh` | Server lifecycle in tmux | +| `tools/mc-rcon.sh` | RCON command sender | +| `tools/run-creative-e2e.sh` | Full creative mode end-to-end test | diff --git a/.skills/mcc-dev-workflow/evals/evals.json b/.skills/mcc-dev-workflow/evals/evals.json new file mode 100644 index 00000000..7d86331e --- /dev/null +++ b/.skills/mcc-dev-workflow/evals/evals.json @@ -0,0 +1,41 @@ +{ + "skill_name": "mcc-dev-workflow", + "evals": [ + { + "id": 1, + "prompt": "Build MCC, start the local 1.21.11 vanilla server from an MCC_SERVERS root, connect MCC with a temporary config, and verify a successful join plus one inventory command.", + "expected_output": "The workflow uses a real local server, a temp MCC config, and concrete log evidence for both the join and the MCC command.", + "files": [], + "expectations": [ + "The workflow uses a real local 1.21.11 server instead of only reading code.", + "The workflow uses MCC_SERVERS-aware tooling or documents the server root explicitly.", + "The workflow uses a temporary MCC config instead of relying on the repo-root config.", + "The result includes join evidence from MCC output and the server log." + ] + }, + { + "id": 2, + "prompt": "Debug a flaky local MCC startup on 1.21.11 by checking for stale tmux server sessions, waiting for server readiness, and retrying early RCON commands before blaming protocol code.", + "expected_output": "The response treats tmux sessions as shared state, distinguishes server startup from RCON readiness, and uses the real local workflow rather than pure speculation.", + "files": [], + "expectations": [ + "The workflow checks or mentions stale mc-* tmux sessions.", + "The workflow distinguishes Done from RCON readiness.", + "The workflow retries or advises retrying early RCON commands.", + "The workflow keeps the debugging loop grounded in real local commands." + ] + }, + { + "id": 3, + "prompt": "Run a repeatable local MCC debug loop for 1.21.11 that compiles the client, connects with movement, inventory, and entity handling enabled, and leaves enough evidence to inspect a regression afterward.", + "expected_output": "The response follows a real build-run-test-inspect loop and captures enough log evidence to support follow-up debugging.", + "files": [], + "expectations": [ + "The workflow builds MCC before the run.", + "The workflow enables terrain, inventory, and entity handling for the scripted run.", + "The workflow captures or points to concrete log locations.", + "The workflow prefers a temp config for repeatability." + ] + } + ] +} diff --git a/.skills/mcc-integration-testing/SKILL.md b/.skills/mcc-integration-testing/SKILL.md new file mode 100644 index 00000000..9f70a804 --- /dev/null +++ b/.skills/mcc-integration-testing/SKILL.md @@ -0,0 +1,323 @@ +--- +name: mcc-integration-testing +description: >- + Use when proving MCC behavior on a real local Minecraft server, validating + runtime or protocol changes end-to-end, exercising movement, physics, + inventory, entity, chat, or terrain behavior, or running a single-version or + cross-version regression sweep. +metadata: + category: discipline + triggers: + - integration test + - real server + - local server + - regression sweep + - rcon + - tmux + - offline mode + - online mode + - movement + - physics + - inventory + - entity + - terrain + - chat +--- + +# MCC Integration Testing + +Use this skill when the task is "prove it on a real server", not just "reason about whether it should work." + +Read [references/online-mode.md](references/online-mode.md) when the user asks for Microsoft login, device-code auth, or an online-mode server run. Use [references/command-matrix.md](references/command-matrix.md) for stable MCC-side and RCON-side commands. + +## Iron Law + +Only say MCC was integration tested when MCC ran against a real local server and the claim is backed by real MCC output plus real server logs. + +Calling build-only, reasoning-only, or join-only work "integration tested" is a rules violation, not shorthand. + +These do not count as end-to-end proof: + +- static reasoning, source comparison, or build success +- join or login success by itself +- a long-lived idle connection by itself +- a grep that only says there were no errors +- testing one shared-route version and silently claiming adjacent versions also passed + +If the environment cannot run a real server, say so and report the result as unexecuted or inferred, not integration tested. + +## Default target + +- Use `1.21.11-Vanilla` unless the user asks for a different version or a version matrix. +- Use `MCC_SERVERS` if it is set. Otherwise the default server root is `MinecraftOfficial/downloads`. + +## Guardrails + +- Use a real local server. +- Launch MCC against an explicit `localhost:` target for repeatable local tests. +- Keep version matrices sequential in shared local environments. The tmux server harness is shared state by default. +- Prefer generated temporary MCC configs for scripted runs so one test does not contaminate the next. +- Default to offline auth in generated temp configs. Do not trust the repo-root `MinecraftClient.ini` account defaults. +- If the user explicitly asks for Microsoft online login, honor that request and generate the temp config for Microsoft auth instead of offline mode. +- For Microsoft auth, prefer an interactive TTY launch with `BasicIO-NoColor` so the device code is easy to read and relay to the user. +- Do not use file-input mode during Microsoft auth. Launch interactively first, complete login, then switch to scripted control only if needed. +- For online-mode tests, prefer a clean temp config with no join-time bots or scheduled tasks. Inherited `ScriptScheduler` or `DiscordRpc` settings can pollute the session and send unintended chat right after login. +- Legacy and modern command syntax differ. Do not assume one server-command profile fits every version. +- Use actual MCC output and actual server logs for assertions. Do not invent success strings. +- Treat server `Done` as startup progress, not RCON readiness. Retry the first RCON command before assuming the setup is broken. +- Run preflight before scripted test loops. On macOS, Java may be installed but not exported on PATH in the shell the harness uses. +- If a change touches shared routing or a version range, test at least one adjacent version that shares that path, or explicitly mark adjacent versions as unexecuted and inferred. +- For palette or version-content changes, probe at least one neighboring or existing item, entity, or block. Do not only check the headline addition. +- Separate product failures from harness failures. Missing logs, stale tmux state, stale `stdin.pipe`, or pre-join `Connection refused` errors are usually environment problems until proven otherwise. + +## Choose the test mode + +### 1. Single-version deep smoke + +Use this when one supported version is enough and you want broad coverage: + +```bash +.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh 1.21.11-Vanilla +``` + +This covers join, chat, slash commands, internal MCC commands, creative inventory, entity handling, sounds, particles, TNT, kill/respawn, and log assertions. + +### 2. Ordered creative-mode E2E + +Use this when the user asks for a regression sweep in a strict scenario order such as: + +- connect +- send messages +- send commands +- receive messages +- movement +- physics +- mobs +- effects +- inventory + +Broad validation should usually cover `connect-test`, `item-test`, `entity-test`, `terrain-test`, and `chat-test`. + +Command: + +```bash +MCC_SERVERS=/home/anon/Minecraft/Servers bash tools/run-creative-e2e.sh 1.21.11-Vanilla 1.21.11 modern +``` + +For legacy targets such as `1.8` or `1.8.9`, switch the final argument to `legacy` and pass the pinned MC version. + +### 3. Timing or cadence validation + +Use this for TPS, movement-cadence, or packet-cadence work: + +- `MinecraftClient/config/sample-script-tick-counter.cs` +- `MinecraftClient/config/sample-script-packet-capture.cs` + +Run them against a real server with a temp config and summarize counts from the captured logs. + +### 4. Structured components test + +Use this after touching any `StructuredComponents` code (registries, component +parsers, subcomponents, codec helpers) to prove every component type in a +version parses on the wire without error: + +```bash +bash tools/run-structured-components-test.sh 1.21.11 +``` + +Run a single version (fast, ~2 min) or a matrix: + +```bash +for v in 1.20.6 1.21 1.21.2 1.21.5 1.21.11 26.1; do + bash tools/run-structured-components-test.sh "$v" +done +``` + +The script gives items with every registered component via RCON `/give`, reads +them back with `inventory player list`, and asserts no parse errors in the MCC +log. Version-gated components (v1212+, v1215+, v12111+, v261) are tested only +on the versions that support them. See `SC_Integration_Test_Report.md` for a +reference run across all 6 version groups. + +### 6. Dialog integration test + +Use this after touching any dialog system code (packet handling, NBT parsing, +models, TUI, command dispatch, the state machine in `DialogManager`, or the +codec in `DialogNbtParser`). Tests all 5 dialog types, button actions (close, +run_command, show_dialog), cancel/dismiss, click-label, and body content: + +```bash +tools/run-dialog-test.sh 26.1 +``` + +The script starts the server if needed, generates a temp MCC config, launches +MCC with file-input mode (requires both `MCC_FILE_INPUT=1` and +`MCC_INPUT_FILE=` env vars), sends inline SNBT dialogs via RCON, and +asserts 29 checks against the MCC log. + +Key requirements that differ from other test modes: + +- FileInputBot is loaded only when `MCC_FILE_INPUT=1` is set in the + environment. The `[ChatBot.FileInput]` config section is ignored at load + time. +- The input file path is controlled by `MCC_INPUT_FILE`, *not* by the config + `File` setting. +- Dialogs use inline SNBT syntax through `ResourceOrIdArgument`, e.g.: + `dialog show {type:"minecraft:notice", title:{text:"Hello"}}` +- The `ActionButton.CODEC` flattens `CommonButtonData` fields (`label`, + `tooltip`, `width`) into the same object as `action` — no `button` wrapper. + +### 5. Full inventory regression sweep + +Use this when touching inventory snapshots, player/container slot sync, creative inventory, item-slot serialization, packet palettes, game-mode updates, or block-use paths that open containers: + +```bash +tools/run-inventory-full-sweep.sh --versions "1.21.10 1.21.11" +``` + +Default coverage includes: + +- player inventory listing and inventory discovery +- creative give/delete +- inventory search +- player right/left click stack split and merge +- player drop one and drop all +- chest open via `useblock` +- container listing and close +- mirrored player slots in container windows +- shift-click and shift-right-click transfer +- container right/left click, cursor stack, drop one, and drop all +- creative middle-click command path +- log scan for packet parse failures, queue-empty crashes, unhandled exceptions, and disconnects + +The script writes `summary.tsv` under `RUN_ROOT` and per-version logs under `/tmp/mcc-debug/inventory-full-/mcc-debug.log`. + +When a matrix has existing PASS rows, do not rerun them unless a later code change affects that row or the user asks for a full rerun. Derive remaining rows from summaries: + +```bash +awk 'FNR>1 && $2=="PASS" {print $1}' /tmp/mcc-inventory-full-sweep/*/summary.tsv | sort -V | uniq +``` + +## Preconditions + +Before running any scenario: + +0. run preflight and clear stale shared state when the environment is reused +1. configure the target server for offline testing +2. ensure `eula=true` +3. ensure RCON is enabled +4. build MCC unless the task explicitly reuses a fresh build + +Preflight and reset helpers: + +```bash +.skills/mcc-integration-testing/scripts/preflight_test_env.sh 1.21.11-Vanilla +.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh 1.21.11-Vanilla +``` + +Offline configuration helper: + +```bash +.skills/mcc-integration-testing/scripts/ensure_offline_server.sh 1.21.11-Vanilla +``` + +By default, the config helper prepares offline auth. To opt into another auth mode for a specific run, set: + +```bash +MCC_TEST_ACCOUNT_TYPE=microsoft +MCC_TEST_PASSWORD= +``` + +Optionally override the login name with the fourth argument to the config helper. + +## Scripts and tools + +- `.skills/mcc-integration-testing/scripts/ensure_offline_server.sh` + - configures persistent offline mode and RCON +- `.skills/mcc-integration-testing/scripts/preflight_test_env.sh` + - verifies Java, tmux, dotnet, python3, server directories, and resolves common Java PATH issues +- `.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh` + - clears stale tmux sessions and stale `stdin.pipe` files before a rerun +- `.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh` + - generates a clean temporary MCC config, prepares offline login by default, disables noisy bots, and can switch to Microsoft auth when explicitly requested +- `.skills/mcc-integration-testing/scripts/get_server_port.sh` + - resolves the actual local server port from `server.properties` or the latest server log +- `.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh` + - single-version deep smoke with built-in assertions +- `.skills/mcc-integration-testing/scripts/summarize_test_run.sh` + - summarize the latest full-spectrum run +- `tools/run-creative-e2e.sh` + - ordered creative-mode E2E regression scenario +- `tools/run-inventory-full-sweep.sh` + - full inventory command/API sweep across one or more versions +- `tools/run-structured-components-test.sh` + - exercises every structured component via RCON `/give` across versions 1.20.6-26.1 +- `tools/run-dialog-test.sh` + - dialog integration test: all 5 types, run_command/show_dialog actions, + cancel/dismiss/click-label, body content; 29 assertions on MCC log + +## Evidence Discipline + +In every report, separate: + +- `Executed`: exact scripts, commands, versions, auth mode, and whether the run was sequential or single-version +- `Observed`: exact MCC output, exact server-log evidence, and the saved log directory +- `Inferred`: conclusions not directly shown by that run's runtime evidence +- `Harness issues`: setup or runner problems such as missing Java on PATH, stale tmux sessions, stale `stdin.pipe`, missing log artifacts, or failed config generation + +Never upgrade inferred claims to observed facts. Absence of errors is supporting evidence only; pair it with a positive assertion for the feature under test. + +## Red Flags + +Stop and fix the test plan if you are about to: + +- claim movement, inventory, entity, terrain, physics, or chat coverage from join success alone +- reuse repo-root `MinecraftClient.ini` or another user-local stateful config +- run multi-version tests in parallel in a shared tmux or shared server environment +- let inherited bots, schedulers, or other user-local noise send chat or commands during validation + +## What to report back + +Always summarize: + +- which version or versions were tested +- which port or ports were used +- which auth mode and scenario were used +- whether the run was sequential or single-version +- the exact scripts or commands executed +- pass or fail per major phase +- concrete evidence from MCC and server logs +- the saved log directory +- what was not executed and what remains inferred +- which adjacent versions were not run but were mentioned + +## When Not to Use + +- build-only verification +- static protocol or source comparison with no real server run +- documentation or prompt work +- code review requests that do not ask for executed runtime proof + +## Troubleshooting + +- If the first RCON command fails, retry it before assuming the setup is broken. +- If Java is installed but the harness still says it is missing, run `preflight_test_env.sh`. This resolves common Homebrew Java paths on macOS. +- If MCC reaches Microsoft device-code login during an offline test, stop and inspect the generated temp config before retrying. +- If the user explicitly requests Microsoft online login, set `MCC_TEST_ACCOUNT_TYPE=microsoft` before launching the harness. +- If the user explicitly requests Microsoft online login, use `BasicIO-NoColor` in a real TTY, relay the device code from the TUI, and avoid pressing empty Enter at any auth prompt. +- If the online-mode session sends unexpected chat or commands right after join, inspect inherited bot settings first. `ChatBot.ScriptScheduler` tasks and `ChatBot.DiscordRpc` are common sources of test noise in user-local configs. +- If `dotnet run` cannot see an existing Microsoft session, check whether `SessionCache.db` and `ProfileKeyCache.ini` need to be synced from `MinecraftClient/bin/Release/net10.0/` to the repo root. +- If Microsoft auth keeps prompting even with a valid session cache, verify `Account.Login` matches the cached username exactly. +- If MCC reports `Connection refused`, verify the launched target matches the server's actual `server-port`. +- If MCC reports `Connection refused` immediately after a server start, also check for stale shared state: old tmux sessions, a stale `stdin.pipe`, or a server that never actually reached `Done (`. +- If multiple versions are being tested, do not start them in parallel unless the harness isolates tmux sessions and input files. +- If a test assertion fails, inspect the real MCC output before changing the code or weakening the assertion. +- If an older server behaves oddly on Linux, check `use-native-transport=false` in `server.properties`. +- If a matrix row fails before producing `mcc.log` or a command transcript, treat it as a harness failure, fix the environment, and rerun that row before drawing product conclusions. +- If creative inventory commands report "You must be in Creative gamemode" after RCON switched the player, inspect game-mode update parsing before assuming creative inventory is broken. Modern servers can update local game mode through game event reason `3`. +- If an inventory row crashes with `Queue empty` or `Failed to process incoming packet`, inspect packet palette routing before changing inventory code. A single shifted packet ID can make a healthy inventory feature look broken. +- For chest-open failures, separate product and harness causes. The player may be standing inside the chest or suffocating on older servers. Stand beside the chest, put a floor under the player, and retry `useblock`. +- For shared local servers, a `Done` log line does not prove RCON is ready. Retry setup commands and verify the actual RCON port from `server.properties`. +- If `tools/run-dialog-test.sh` fails with "FileInput Watching: .../mcc_input.txt" pointing to the wrong directory, the `MCC_INPUT_FILE` env var was not set in the tmux command. FileInputBot ignores the config `File` setting entirely. +- If inline SNBT dialogs fail on the server side (`Failed to parse structure: No key ...`), check whether `ActionButton.CODEC` fields are flat (no `button` wrapper) and whether the dialog type fields match the 26.1 server (`label` not `text` in `CommonButtonData`). +- If a dialog integration test fails on "Server showed custom dialog", the dialog packet (id=0x8C in 26.1 play phase) may not have been sent. Verify the RCON command succeeded and the server printed "Displayed dialog to ...". diff --git a/.skills/mcc-integration-testing/evals/evals.json b/.skills/mcc-integration-testing/evals/evals.json new file mode 100644 index 00000000..251abc58 --- /dev/null +++ b/.skills/mcc-integration-testing/evals/evals.json @@ -0,0 +1,41 @@ +{ + "skill_name": "mcc-integration-testing", + "evals": [ + { + "id": 1, + "prompt": "Run a real 1.21.11 MCC regression test in creative mode covering connect, send messages, send commands, receive messages, movement, physics, mobs, effects, and inventory, in that order.", + "expected_output": "The workflow uses the ordered creative-mode E2E harness on a real server, reports pass or fail for each phase, and points to the saved logs.", + "files": [], + "expectations": [ + "The workflow uses a real 1.21.11 server.", + "The workflow uses the ordered creative E2E harness instead of improvising the whole scenario.", + "The result reports phase-by-phase outcomes in the requested order.", + "The result includes the log directory for the run." + ] + }, + { + "id": 2, + "prompt": "Validate that MCC still works after a runtime or protocol change by running a deep single-version 1.21.11 integration test with chat, creative inventory, entity tracking, sounds, particles, TNT, and kill/respawn coverage.", + "expected_output": "The workflow uses the full-spectrum test runner on a real server and returns a concise pass or fail summary backed by MCC and server log evidence.", + "files": [], + "expectations": [ + "The workflow builds MCC before the scenario unless a fresh build is explicitly reused.", + "The workflow uses the full-spectrum runner instead of only manual spot checks.", + "The result includes evidence from both MCC output and server logs.", + "The result points to the saved run directory." + ] + }, + { + "id": 3, + "prompt": "Validate a timing-sensitive MCC change on a real 1.21.11 server by collecting tick-rate and outbound packet-cadence evidence, then summarize the results clearly.", + "expected_output": "The response uses a real server, a temp config, and the provided sample scripts to capture tick-rate and packet evidence instead of relying on intuition.", + "files": [], + "expectations": [ + "The workflow uses the real 1.21.11 server.", + "The workflow uses the tick counter and packet capture scripts or clearly equivalent targeted instrumentation.", + "The workflow keeps the run isolated with a temp config.", + "The summary reports concrete counts or cadence evidence." + ] + } + ] +} diff --git a/.skills/mcc-integration-testing/references/command-matrix.md b/.skills/mcc-integration-testing/references/command-matrix.md new file mode 100644 index 00000000..782bc745 --- /dev/null +++ b/.skills/mcc-integration-testing/references/command-matrix.md @@ -0,0 +1,91 @@ +# Command Matrix + +This skill uses a fixed set of stable commands for local offline integration testing. + +## MCC-side commands via `mcc-cmd` + +- `health` +- `list` +- `inventory player list` +- `/gamemode creative` +- `inventory creativegive 36 Diamond 16` +- `inventory creativegive 37 IronSword 1` +- `inventory creativegive 38 GoldenApple 8` +- `inventory creativeclear 38` +- `entity` +- `/time query daytime` +- `look up` +- `look down` +- `look east` +- `/gamemode survival` +- `respawn` +- `/tp MCCBot 0 -60 0` +- `smoke_test_from_mcc_full_spectrum` +- `integration_test_chat_response` + +Notes: +- Lines starting with `/` are sent to the server as chat/commands. +- Non-slash lines are treated as MCC internal commands first, then fall back to chat. + +## Server-side commands via `mc-rcon` + +- `op MCCBot` +- `gamerule sendCommandFeedback true` +- `gamerule logAdminCommands true` +- `time set day` +- `weather clear` +- `say Hello from the server console` +- `msg MCCBot This is a private whisper` +- `effect give MCCBot minecraft:speed 30 1` +- `effect give MCCBot minecraft:regeneration 10 1` +- `kill MCCBot` + +## Representative entity coverage + +- `execute as MCCBot at @s run summon minecraft:cow ~2 ~ ~` +- `execute as MCCBot at @s run summon minecraft:zombie ~4 ~ ~` +- `execute as MCCBot at @s run summon minecraft:creeper ~6 ~ ~` +- `execute as MCCBot at @s run summon minecraft:skeleton ~8 ~ ~` +- `execute as MCCBot at @s run summon minecraft:villager ~-2 ~ ~` +- `execute as MCCBot at @s run summon minecraft:allay ~-4 ~ ~` +- `execute as MCCBot at @s run summon minecraft:armor_stand ~ ~ ~2` +- `execute as MCCBot at @s run summon minecraft:item_display ~-6 ~ ~ {item:{id:"minecraft:diamond",count:1}}` +- `execute as MCCBot at @s run summon minecraft:spider ~10 ~ ~` +- `execute as MCCBot at @s run summon minecraft:pig ~-8 ~ ~` + +## Block placement coverage + +- `execute as MCCBot at @s run fill ~1 ~ ~1 ~3 ~2 ~3 minecraft:stone` +- `execute as MCCBot at @s run setblock ~5 ~ ~5 minecraft:chest` +- `execute as MCCBot at @s run setblock ~5 ~1 ~5 minecraft:furnace` +- `execute as MCCBot at @s run setblock ~6 ~ ~5 minecraft:crafting_table` + +## Dimension change coverage + +- `execute in minecraft:the_nether run tp MCCBot 0 64 0` +- `execute in minecraft:overworld run tp MCCBot 0 -60 0` + +## Representative particle coverage + +- `execute as MCCBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force` +- `execute as MCCBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force` +- `execute as MCCBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force` +- `execute as MCCBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force` +- `execute as MCCBot at @s run particle minecraft:flame ~ ~1 ~ 0.2 0.2 0.2 0.02 30 force` +- `execute as MCCBot at @s run particle minecraft:heart ~ ~2 ~ 0.3 0.3 0.3 0 5 force` + +## Representative sound coverage + +- `execute as MCCBot at @s run playsound minecraft:entity.lightning_bolt.thunder master MCCBot ~ ~ ~ 1 1 0` +- `execute as MCCBot at @s run playsound minecraft:block.note_block.bell master MCCBot ~ ~ ~ 1 1 0` +- `execute as MCCBot at @s run playsound minecraft:entity.experience_orb.pickup master MCCBot ~ ~ ~ 1 1 0` + +## Explosion coverage + +- `execute as MCCBot at @s run summon minecraft:tnt ~3 ~ ~` +- `execute as MCCBot at @s run summon minecraft:tnt ~6 ~ ~` + +## Kill and respawn cycle + +- `kill MCCBot` (via RCON, requires survival mode) +- `respawn` (via MCC command after death) diff --git a/.skills/mcc-integration-testing/references/online-mode.md b/.skills/mcc-integration-testing/references/online-mode.md new file mode 100644 index 00000000..fdd0beda --- /dev/null +++ b/.skills/mcc-integration-testing/references/online-mode.md @@ -0,0 +1,48 @@ +# Online-Mode Notes + +Use this flow only when the user explicitly asks for Microsoft login or wants to validate against an online-mode server. + +## Launch mode + +- Prefer `BasicIO-NoColor` in a real TTY so the Microsoft device-code prompt is easy to read and copy. +- Do not use `MCC_FILE_INPUT=1` during the auth step. It is for scripted command injection, not interactive login. +- Avoid `nohup`. Use `tmux` for long-running sessions that still need a TTY. +- Start from a clean temp config when possible. If the temp config is copied from a user-local `MinecraftClient.ini`, inspect `ChatBot.ScriptScheduler` and `ChatBot.DiscordRpc` before the run. + +## Session cache behavior + +- `dotnet run --project MinecraftClient ...` uses the repo root for `SessionCache.db` and `ProfileKeyCache.ini`. +- The compiled binary under `MinecraftClient/bin//net10.0/` uses that output directory instead. +- If a session exists in one location and not the other, sync the cache files before assuming login is broken. + +## Account settings + +- `Account.Login` must be populated for MCC to look up a cached Microsoft session. +- The cached key is the username form MCC stored, typically the lowercase username, not necessarily the email address. +- MCC rewrites `MinecraftClient.ini` on clean exit, so generate a temp config per run and do not edit it while MCC is still running. + +## Auth prompt handling + +- Do not send a bare Enter to dismiss `Password(invisible):` or `Paste your code here:` prompts. That can trigger offline fallback. +- For interactive online-mode runs, wait for the device code prompt and relay the code to the user exactly as shown. +- After the user completes login, continue the test in the same TTY session or restart into file-driven mode if the workflow requires automation. + +## Join-time noise + +- Real user configs may contain enabled bots or task lists that were harmless in offline testing but are noisy in online-mode validation. +- The most common examples are: + - `ChatBot.ScriptScheduler` task lists that send `/hello`, `/login ...`, or other automatic commands on login or on an interval + - `ChatBot.DiscordRpc`, which is not harmful to server state but adds log noise and extra background activity +- If the goal is protocol or feature validation, suppress these before the run or treat their output as non-test noise. + +## Server settings + +- For realistic online-mode testing, keep `online-mode=true`. +- Keep `enforce-secure-profile=true` unless the test explicitly targets insecure-profile behavior. + +## Command reminders + +- With `InternalCmdChar = "slash"`: + - `/health`, `/pos`, `/inventory`, `/entity` are MCC internal commands. + - `/send /list` and `/send /give ...` are server commands. + - bare text is regular chat sent to the server. diff --git a/.skills/mcc-integration-testing/scripts/common.sh b/.skills/mcc-integration-testing/scripts/common.sh new file mode 100755 index 00000000..ac5c0250 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/common.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +sed_in_place() { + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +ensure_java_in_path() { + if command -v java >/dev/null 2>&1 && java -version >/dev/null 2>&1; then + return 0 + fi + + local candidate + for candidate in \ + "${JAVA_BIN:-}" \ + "/opt/homebrew/opt/openjdk/bin/java" \ + "/usr/local/opt/openjdk/bin/java" \ + "/usr/lib/jvm/default-java/bin/java" + do + [[ -z "$candidate" ]] && continue + if [[ -x "$candidate" ]]; then + export PATH="$(dirname "$candidate"):$PATH" + export JAVA_BIN="$candidate" + if java -version >/dev/null 2>&1; then + return 0 + fi + fi + done + + echo "java was not found on PATH. Install Java or set JAVA_BIN." >&2 + return 1 +} + +server_session_name() { + printf 'mc-%s\n' "${1//./_}" +} + +server_running() { + local version="$1" + mc-list | grep -Fq "$(server_session_name "$version")" +} + +wait_for_server_ready() { + local version="$1" + local timeout="${2:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if mc-log "$version" 250 2>/dev/null | grep -Fq "Done ("; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for $version to become ready" >&2 + return 1 +} + +wait_for_server_stop() { + local version="$1" + local timeout="${2:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if ! server_running "$version"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + mc-kill "$version" --confirm >/dev/null 2>&1 || true + + if ! server_running "$version"; then + return 0 + fi + + echo "Timed out waiting for $version to stop" >&2 + return 1 +} + +disable_noisy_bots_in_ini() { + local ini_file="$1" + local section + + for section in \ + ScriptScheduler \ + DiscordRpc \ + AntiAFK \ + AutoDig \ + AutoAttack \ + PlayerListLogger \ + ReplayCapture + do + sed_in_place "/^\\[ChatBot\\.${section}\\]/,/^\\[/ { s/^Enabled = true/Enabled = false/; }" "$ini_file" + done +} + +remove_stale_stdin_pipe() { + local version="$1" + local pipe_path="$MCC_SERVERS/$version/stdin.pipe" + + if [[ -e "$pipe_path" ]] && ! server_running "$version"; then + rm -f "$pipe_path" + fi +} diff --git a/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh b/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh new file mode 100755 index 00000000..8ede375b --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +VERSION="${1:-1.21.11-Vanilla}" +SERVER_DIR="${MCC_SERVERS:?}/$VERSION" +PROPS_FILE="$SERVER_DIR/server.properties" +SESSION_NAME="mc-${VERSION//./_}" + +if [[ ! -d "$SERVER_DIR" ]]; then + echo "Server directory not found: $SERVER_DIR" >&2 + exit 1 +fi + +if [[ ! -f "$SERVER_DIR/eula.txt" ]] || ! grep -Eq '^eula=true$' "$SERVER_DIR/eula.txt"; then + echo "Missing accepted EULA in $SERVER_DIR/eula.txt" >&2 + exit 1 +fi + +server_running() { + mc-list | grep -Fq "$SESSION_NAME" +} + +upsert_property() { + local key="$1" + local value="$2" + + if grep -Eq "^${key}=" "$PROPS_FILE"; then + sed_in_place "s#^${key}=.*#${key}=${value}#" "$PROPS_FILE" + else + printf '%s=%s\n' "$key" "$value" >> "$PROPS_FILE" + fi +} + +if [[ ! -f "$PROPS_FILE" ]]; then + mc-start "$VERSION" + wait_for_server_ready "$VERSION" + mc-stop "$VERSION" --confirm + wait_for_server_stop "$VERSION" +fi + +if server_running; then + mc-stop "$VERSION" --confirm + wait_for_server_stop "$VERSION" +fi + +upsert_property "online-mode" "false" +upsert_property "enforce-secure-profile" "false" +upsert_property "enable-rcon" "true" +upsert_property "rcon.port" "25575" +upsert_property "rcon.password" "test123" + +echo "Configured $VERSION for persistent offline testing" diff --git a/.skills/mcc-integration-testing/scripts/get_server_port.sh b/.skills/mcc-integration-testing/scripts/get_server_port.sh new file mode 100644 index 00000000..95bceb1a --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/get_server_port.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +SERVER_DIR_NAME="$1" +SERVERS_ROOT="${MCC_SERVERS:-$REPO_ROOT/MinecraftOfficial/downloads}" +SERVER_DIR="$SERVERS_ROOT/$SERVER_DIR_NAME" +PROPS_FILE="$SERVER_DIR/server.properties" +LATEST_LOG="$SERVER_DIR/logs/latest.log" + +if [[ -f "$PROPS_FILE" ]]; then + PORT_LINE="$(grep -E '^server-port=' "$PROPS_FILE" | tail -n 1 || true)" + if [[ -n "$PORT_LINE" ]]; then + PORT="${PORT_LINE#server-port=}" + if [[ "$PORT" =~ ^[0-9]+$ ]]; then + printf '%s\n' "$PORT" + exit 0 + fi + fi +fi + +if [[ -f "$LATEST_LOG" ]]; then + PORT="$(sed -n 's/.*Starting Minecraft server on .*:\([0-9][0-9]*\).*/\1/p' "$LATEST_LOG" | tail -n 1)" + if [[ -n "$PORT" ]]; then + printf '%s\n' "$PORT" + exit 0 + fi +fi + +printf '25565\n' diff --git a/.skills/mcc-integration-testing/scripts/preflight_test_env.sh b/.skills/mcc-integration-testing/scripts/preflight_test_env.sh new file mode 100755 index 00000000..22376026 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/preflight_test_env.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' +Usage: preflight_test_env.sh [server-dir...] + +Checks the local MCC test environment and resolves common Java path issues. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +ensure_java_in_path +command -v tmux >/dev/null 2>&1 || { echo "tmux was not found on PATH." >&2; exit 1; } +command -v dotnet >/dev/null 2>&1 || { echo "dotnet was not found on PATH." >&2; exit 1; } +command -v python3 >/dev/null 2>&1 || { echo "python3 was not found on PATH." >&2; exit 1; } + +if [[ ! -d "$MCC_SERVERS" ]]; then + echo "Server root not found: $MCC_SERVERS" >&2 + exit 1 +fi + +for server_dir in "$@"; do + [[ -z "$server_dir" ]] && continue + if [[ ! -d "$MCC_SERVERS/$server_dir" ]]; then + echo "Server directory not found: $MCC_SERVERS/$server_dir" >&2 + exit 1 + fi + + remove_stale_stdin_pipe "$server_dir" +done + +printf 'MCC_REPO=%s\n' "$MCC_REPO" +printf 'MCC_SERVERS=%s\n' "$MCC_SERVERS" +printf 'JAVA=%s\n' "$(command -v java)" +printf 'TMUX=%s\n' "$(command -v tmux)" +printf 'DOTNET=%s\n' "$(command -v dotnet)" diff --git a/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh b/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh new file mode 100644 index 00000000..1e30f673 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' >&2 +Usage: + prepare_offline_mcc_config.sh [login] + prepare_offline_mcc_config.sh [login] +EOF +} + +if [[ $# -lt 2 || $# -gt 4 ]]; then + usage + exit 1 +fi + +TEMPLATE_INI="" +OUTPUT_INI="" +MC_VERSION="" +LOGIN_NAME="" + +if [[ $# -ge 3 && -f "$1" ]]; then + TEMPLATE_INI="$1" + OUTPUT_INI="$2" + MC_VERSION="$3" + LOGIN_NAME="${4:-MCCBot}" +else + OUTPUT_INI="$1" + MC_VERSION="$2" + LOGIN_NAME="${3:-MCCBot}" +fi + +ACCOUNT_TYPE="${MCC_TEST_ACCOUNT_TYPE:-mojang}" +PASSWORD_VALUE="${MCC_TEST_PASSWORD-}" + +if [[ "$ACCOUNT_TYPE" != "mojang" && "$ACCOUNT_TYPE" != "microsoft" && "$ACCOUNT_TYPE" != "yggdrasil" ]]; then + echo "Unsupported MCC_TEST_ACCOUNT_TYPE: $ACCOUNT_TYPE" >&2 + exit 1 +fi + +if [[ -z "${MCC_TEST_PASSWORD+x}" ]]; then + if [[ "$ACCOUNT_TYPE" == "mojang" ]]; then + PASSWORD_VALUE="-" + else + PASSWORD_VALUE="" + fi +fi + +generate_template_ini() { + local template_root + template_root="$(mktemp -d "${TMPDIR:-/tmp}/mcc-config-template.XXXXXX")" + + if [[ ! -f "$REPO_ROOT/MinecraftClient/bin/Release/net10.0/MinecraftClient.dll" ]]; then + dotnet build "$REPO_ROOT/MinecraftClient.sln" -c Release -v quiet --nologo >/dev/null + fi + + ( + cd "$template_root" + dotnet run --project "$REPO_ROOT/MinecraftClient" -c Release --no-build -- --help >/dev/null 2>&1 + ) + + if [[ ! -f "$template_root/MinecraftClient.ini" ]]; then + echo "Failed to generate a temporary MCC config template." >&2 + exit 1 + fi + + TEMPLATE_INI="$template_root/MinecraftClient.ini" +} + +if [[ -z "$TEMPLATE_INI" ]]; then + generate_template_ini +fi + +mkdir -p "$(dirname "$OUTPUT_INI")" +cp "$TEMPLATE_INI" "$OUTPUT_INI" + +sed_in_place \ + -e "s#^Account = .*#Account = { Login = \"$LOGIN_NAME\", Password = \"$PASSWORD_VALUE\" }#" \ + -e "s#^AccountType = .*#AccountType = \"$ACCOUNT_TYPE\"#" \ + -e "s#^MinecraftVersion = \"[^\"]*\"\\(.*\\)\$#MinecraftVersion = \"$MC_VERSION\"\\1#" \ + -e 's#^TerrainAndMovements = false#TerrainAndMovements = true#' \ + -e 's#^InventoryHandling = false#InventoryHandling = true#' \ + -e 's#^EntityHandling = false#EntityHandling = true#' \ + -e 's#^AutoRespawn = false#AutoRespawn = true#' \ + "$OUTPUT_INI" + +disable_noisy_bots_in_ini "$OUTPUT_INI" + +grep -Fq "AccountType = \"$ACCOUNT_TYPE\"" "$OUTPUT_INI" || { + echo "Failed to enforce account type $ACCOUNT_TYPE in $OUTPUT_INI" >&2 + exit 1 +} + +if [[ "$ACCOUNT_TYPE" == "mojang" ]]; then + grep -Eq '^Account = \{ Login = ".*", Password = "-" \}' "$OUTPUT_INI" || { + echo "Failed to enforce offline account in $OUTPUT_INI" >&2 + exit 1 + } +fi + +printf '%s\n' "$OUTPUT_INI" diff --git a/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh b/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh new file mode 100755 index 00000000..eab7c694 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' +Usage: reset_shared_test_state.sh [--all | ...] + +Kills shared server tmux test sessions and removes stale server stdin pipes. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +kill_named_session() { + local session_name="$1" + tmux kill-session -t "$session_name" 2>/dev/null || true +} + +if [[ $# -eq 0 || "${1:-}" == "--all" ]]; then + while IFS= read -r session_name; do + [[ -z "$session_name" ]] && continue + kill_named_session "$session_name" + done < <(tmux list-sessions 2>/dev/null | awk -F: '/^mc-/{print $1}' || true) + + while IFS= read -r pipe_path; do + [[ -z "$pipe_path" ]] && continue + rm -f "$pipe_path" + done < <(find "$MCC_SERVERS" -maxdepth 2 -name 'stdin.pipe' 2>/dev/null || true) +else + for version in "$@"; do + kill_named_session "$(server_session_name "$version")" + remove_stale_stdin_pipe "$version" + done +fi diff --git a/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh b/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh new file mode 100755 index 00000000..65ff7d3f --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" + +RUN_ROOT="${TMPDIR:-/tmp}/mcc-achievements/matrix" +RUN_ID="$(date +%Y%m%d-%H%M%S)" +MATRIX_DIR="$RUN_ROOT/$RUN_ID" +RESULTS_TSV="$MATRIX_DIR/results.tsv" +BUILD_LOG="$MATRIX_DIR/build.log" +REPORT_MD="$MATRIX_DIR/report.md" +PRECHECK_TXT="$MATRIX_DIR/preflight.txt" + +mkdir -p "$MATRIX_DIR" + +write_row() { + local fields=("$@") + + while (( ${#fields[@]} < 14 )); do + fields+=("") + done + + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ + "${fields[0]}" "${fields[1]}" "${fields[2]}" "${fields[3]}" "${fields[4]}" "${fields[5]}" "${fields[6]}" \ + "${fields[7]}" "${fields[8]}" "${fields[9]}" "${fields[10]}" "${fields[11]}" "${fields[12]}" \ + "${fields[13]}" >> "$RESULTS_TSV" +} + +resolve_server_dir() { + local version="$1" + local candidate + + for candidate in "$version" "$version-Vanilla"; do + if [[ -d "$MCC_SERVERS/$candidate" ]]; then + printf '%s\n' "$candidate" + return 0 + fi + done + + return 1 +} + +run_version() { + local version="$1" + local profile="$2" + local family="$3" + local server_dir="$4" + local summary_env + + if bash "$SCRIPT_DIR/run_achievements_test.sh" --no-build "$server_dir" "$version" "$profile"; then + : + fi + + summary_env="${TMPDIR:-/tmp}/mcc-achievements/$server_dir/latest/summary.env" + if [[ ! -f "$summary_env" ]]; then + write_row "$version" "$server_dir" "unknown" "$family" "❌" "❌" "❌" "❌" "❌ Fail" \ + "Summary file was not produced." "" "" "" + return + fi + + # shellcheck disable=SC1090 + source "$summary_env" + + if [[ -n "${MCC_LOG:-}" && ! -f "$MCC_LOG" ]]; then + NOTE="Harness failure: MCC log was not produced." + VERDICT="❌ Fail" + fi + + if [[ -n "${COMMAND_LOG:-}" && ! -f "$COMMAND_LOG" ]]; then + NOTE="Harness failure: command transcript was not produced." + VERDICT="❌ Fail" + fi + + write_row "$VERSION" "$SERVER_DIR" "$PORT" "$FAMILY" "$INITIAL_STATUS" "$GRANT_STATUS" "$REVOKE_STATUS" \ + "$API_STATUS" "$VERDICT" "$NOTE" "$RUN_DIR" "$MCC_LOG" "$COPIED_SERVER_LOG" "$COMMAND_LOG" +} + +{ + printf 'MCC_SERVERS=%s\n' "$MCC_SERVERS" + printf 'RUN_DIR=%s\n' "$MATRIX_DIR" + printf 'DATE=%s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" +} > "$PRECHECK_TXT" + +printf 'Version\tServerDir\tPort\tFamily\tInitial\tGrant\tRevoke\tAPI\tVerdict\tNote\tRunDir\tMccLog\tServerLog\tCommandLog\n' > "$RESULTS_TSV" + +JAVA_OK="yes" +TMUX_OK="yes" +DOTNET_OK="yes" +BUILD_OK="yes" + +if ! command -v dotnet >/dev/null 2>&1; then + DOTNET_OK="no" +fi + +if ! command -v java >/dev/null 2>&1 || ! java -version >/dev/null 2>&1; then + JAVA_OK="no" +fi + +if ! command -v tmux >/dev/null 2>&1; then + TMUX_OK="no" +fi + +if [[ "$DOTNET_OK" == "yes" ]]; then + bash "$SCRIPT_DIR/preflight_test_env.sh" >/dev/null 2>&1 || true + if ! dotnet build "$REPO_ROOT/MinecraftClient.sln" -c Release > "$BUILD_LOG" 2>&1; then + BUILD_OK="no" + fi +else + : > "$BUILD_LOG" +fi + +{ + printf 'MCC_SERVERS=%s\n' "$MCC_SERVERS" + printf 'RUN_DIR=%s\n' "$MATRIX_DIR" + printf 'DATE=%s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" + printf 'dotnet=%s\n' "$DOTNET_OK" + printf 'java=%s\n' "$JAVA_OK" + printf 'tmux=%s\n' "$TMUX_OK" + printf 'build=%s\n' "$BUILD_OK" +} > "$PRECHECK_TXT" + +while IFS='|' read -r version profile family; do + [[ -z "$version" ]] && continue + + if [[ "$DOTNET_OK" != "yes" ]]; then + write_row "$version" "" "" "$family" "❌" "❌" "❌" "❌" "❌ Fail" \ + "dotnet is not available on PATH." + continue + fi + + if [[ "$BUILD_OK" != "yes" ]]; then + write_row "$version" "" "" "$family" "❌" "❌" "❌" "❌" "❌ Fail" \ + "dotnet build failed. See $BUILD_LOG." + continue + fi + + if [[ "$JAVA_OK" != "yes" || "$TMUX_OK" != "yes" ]]; then + write_row "$version" "" "" "$family" "❌" "❌" "❌" "❌" "❌ Fail" \ + "java or tmux is not available, so live server execution was blocked." + continue + fi + + if ! server_dir="$(resolve_server_dir "$version")"; then + write_row "$version" "" "" "$family" "❌" "❌" "❌" "❌" "⚠️ Partial" \ + "Server directory for $version was not found under $MCC_SERVERS." + continue + fi + + run_version "$version" "$profile" "$family" "$server_dir" +done <<'EOF' +1.8|legacy|Legacy 🧱 +1.11.2|legacy|Legacy 🧱 +1.12.2|modern|First advancements 🌱 +1.19.4|modern|Stable modern ✅ +1.20|modern|Telemetry edge 1 ⚠️ +1.20.2|modern|Telemetry edge 2 ⚠️ +1.20.4|modern|End of 1.20.x ⚠️ +1.20.6|modern|Post-1.20.6 🔧 +1.21.2|modern|1.21.2 family 🔧 +1.21.11|modern|showAdvancements 🆕 +26.1|modern|Latest supported 🚀 +EOF + +bash "$SCRIPT_DIR/summarize_achievements_matrix.sh" "$MATRIX_DIR" > "$REPORT_MD" +printf '%s\n' "$MATRIX_DIR" diff --git a/.skills/mcc-integration-testing/scripts/run_achievements_test.sh b/.skills/mcc-integration-testing/scripts/run_achievements_test.sh new file mode 100755 index 00000000..1484acd6 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/run_achievements_test.sh @@ -0,0 +1,399 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' +Usage: run_achievements_test.sh [--no-build] + +Examples: + .skills/mcc-integration-testing/scripts/run_achievements_test.sh --no-build 1.8 1.8 legacy + .skills/mcc-integration-testing/scripts/run_achievements_test.sh --no-build 1.21.11-Vanilla 1.21.11 modern +EOF +} + +DO_BUILD=true + +while [[ $# -gt 0 ]]; do + case "$1" in + --no-build) DO_BUILD=false; shift ;; + --build) DO_BUILD=true; shift ;; + -h|--help) usage; exit 0 ;; + *) break ;; + esac +done + +if [[ $# -ne 3 ]]; then + usage >&2 + exit 1 +fi + +SERVER_DIR="$1" +MC_VERSION="$2" +PROFILE="$3" +SESSION_NAME="achievements-${SERVER_DIR//[^a-zA-Z0-9]/_}-${PROFILE}" +TEST_USERNAME="$(_mcc_resolve_username "$SESSION_NAME")" + +if [[ "$PROFILE" != "legacy" && "$PROFILE" != "modern" ]]; then + echo "Unsupported profile: $PROFILE" >&2 + exit 1 +fi + +RUN_ROOT="${TMPDIR:-/tmp}/mcc-achievements" +RUN_ID="$(date +%Y%m%d-%H%M%S)" +RUN_DIR="$RUN_ROOT/$SERVER_DIR/$RUN_ID" +LATEST_LINK="$RUN_ROOT/$SERVER_DIR/latest" +MCC_LOG="$RUN_DIR/mcc.log" +BUILD_LOG="$RUN_DIR/build.log" +SERVER_TMUX_LOG="$RUN_DIR/server-tmux.log" +SERVER_FILE_LOG="$RUN_DIR/server-latest.log" +COMMAND_LOG="$RUN_DIR/commands.log" +SUMMARY_ENV="$RUN_DIR/summary.env" +PROBE_SCRIPT="$RUN_DIR/achievement_probe.cs" +CFG="$RUN_DIR/MinecraftClient.$MC_VERSION.ini" +INPUT_FILE="$(_mcc_session_input_file "$SESSION_NAME")" +SERVER_LOG_FILE="$MCC_SERVERS/$SERVER_DIR/logs/latest.log" +TARGET_ID="minecraft:story/root" +TARGET_COMMAND_GRANT="advancement grant $TEST_USERNAME only minecraft:story/root" +TARGET_COMMAND_REVOKE="advancement revoke $TEST_USERNAME only minecraft:story/root" +TARGET_TYPE="Modern 🌱" +PORT="unknown" +MCC_PID="" + +INITIAL_STATUS="❌" +GRANT_STATUS="❌" +REVOKE_STATUS="❌" +API_STATUS="❌" +VERDICT="❌ Fail" +NOTE="Run did not complete." +EXECUTED="yes" + +if [[ "$PROFILE" == "legacy" ]]; then + TARGET_ID="achievement.openInventory" + TARGET_COMMAND_GRANT="achievement give achievement.openInventory $TEST_USERNAME" + TARGET_COMMAND_REVOKE="achievement take achievement.openInventory $TEST_USERNAME" + TARGET_TYPE="Legacy 🧱" +fi + +mkdir -p "$RUN_DIR" + +write_summary() { + { + printf 'VERSION=%q\n' "$MC_VERSION" + printf 'SERVER_DIR=%q\n' "$SERVER_DIR" + printf 'PROFILE=%q\n' "$PROFILE" + printf 'FAMILY=%q\n' "$TARGET_TYPE" + printf 'PORT=%q\n' "$PORT" + printf 'RUN_DIR=%q\n' "$RUN_DIR" + printf 'MCC_LOG=%q\n' "$MCC_LOG" + printf 'SERVER_LOG=%q\n' "$RUN_DIR/server-latest.log" + printf 'SERVER_FILE_LOG=%q\n' "$SERVER_LOG_FILE" + printf 'SERVER_TMUX_LOG=%q\n' "$SERVER_TMUX_LOG" + printf 'COPIED_SERVER_LOG=%q\n' "$RUN_DIR/server-latest.log" + printf 'COMMAND_LOG=%q\n' "$COMMAND_LOG" + printf 'SUMMARY_ENV=%q\n' "$SUMMARY_ENV" + printf 'TARGET_ID=%q\n' "$TARGET_ID" + printf 'INITIAL_STATUS=%q\n' "$INITIAL_STATUS" + printf 'GRANT_STATUS=%q\n' "$GRANT_STATUS" + printf 'REVOKE_STATUS=%q\n' "$REVOKE_STATUS" + printf 'API_STATUS=%q\n' "$API_STATUS" + printf 'VERDICT=%q\n' "$VERDICT" + printf 'NOTE=%q\n' "$NOTE" + printf 'EXECUTED=%q\n' "$EXECUTED" + } > "$SUMMARY_ENV" +} + +capture_server_logs() { + mc-log "$SERVER_DIR" 400 > "$SERVER_TMUX_LOG" 2>/dev/null || true + if [[ -f "$SERVER_LOG_FILE" ]]; then + cp "$SERVER_LOG_FILE" "$RUN_DIR/server-latest.log" 2>/dev/null || true + fi +} + +cleanup() { + capture_server_logs + + if [[ -n "${MCC_PID:-}" ]] && kill -0 "$MCC_PID" 2>/dev/null; then + echo "quit" >> "$INPUT_FILE" 2>/dev/null || true + sleep 2 + kill "$MCC_PID" 2>/dev/null || true + wait "$MCC_PID" 2>/dev/null || true + fi + + mc-stop "$SERVER_DIR" --confirm >/dev/null 2>&1 || true + wait_for_server_stop "$SERVER_DIR" 20 >/dev/null 2>&1 || true + ln -sfn "$RUN_DIR" "$LATEST_LINK" + write_summary +} +trap cleanup EXIT + +log_step() { + printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$1" | tee -a "$COMMAND_LOG" +} + +fail() { + NOTE="$1" + VERDICT="❌ Fail" + exit 1 +} + +wait_for_file_pattern() { + local file="$1" + local pattern="$2" + local description="$3" + local timeout="${4:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if [[ -f "$file" ]] && grep -Fq "$pattern" "$file"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for: $description" >&2 + return 1 +} + +write_probe_script() { + cat > "$PROBE_SCRIPT" < updated, IReadOnlyList removedIds, bool reset) + { + LogToConsole($"[ACH_TEST] event reset={reset} updated={updated.Count} removed={removedIds.Count}"); + DumpState("event"); + } + + private void DumpState(string origin) + { + Achievement[] all = GetAchievements(); + Achievement[] unlocked = GetUnlockedAchievements(); + Achievement[] locked = GetLockedAchievements(); + Achievement? target = null; + + foreach (Achievement entry in all) + { + if (entry.Id == TargetId) + { + target = entry; + break; + } + } + + string titleState = "missing"; + string completionState = "missing"; + + if (target is not null) + { + titleState = target.Title is null ? "null" : "present"; + completionState = target.IsCompleted ? "done" : "todo"; + } + + LogToConsole($"[ACH_TEST] snapshot origin={origin} all={all.Length} unlocked={unlocked.Length} locked={locked.Length}"); + LogToConsole($"[ACH_TEST] target_state origin={origin} id={TargetId} title={titleState} completed={completionState}"); + } +} +EOF +} + +run_server_command() { + local cmd="$1" + local attempt + + log_step "SERVER> $cmd" + for attempt in 1 2 3 4 5; do + if mc-rcon "$cmd" >/dev/null 2>&1; then + sleep 1 + return 0 + fi + sleep 1 + done + + fail "Server command failed: $cmd" +} + +run_mcc_command() { + local name="$1" + local cmd="$2" + local delay="${3:-2}" + local start_line=0 + local end_line=0 + + if [[ -f "$MCC_LOG" ]]; then + start_line="$(wc -l < "$MCC_LOG")" + fi + + log_step "MCC> $cmd" + echo "$cmd" >> "$INPUT_FILE" + sleep "$delay" + + if [[ -f "$MCC_LOG" ]]; then + end_line="$(wc -l < "$MCC_LOG")" + fi + + if (( end_line > start_line )); then + sed -n "$((start_line + 1)),$((end_line))p" "$MCC_LOG" > "$RUN_DIR/$name.mcc.log" + else + : > "$RUN_DIR/$name.mcc.log" + fi +} + +assert_pattern() { + local file="$1" + local pattern="$2" + local description="$3" + + grep -Fq "$pattern" "$file" || fail "$description" +} + +if $DO_BUILD; then + log_step "BUILD> dotnet build MinecraftClient.sln -c Release" + mcc-build > "$BUILD_LOG" 2>&1 || fail "dotnet build failed." +else + : > "$BUILD_LOG" +fi + +bash "$SCRIPT_DIR/preflight_test_env.sh" "$SERVER_DIR" >/dev/null || fail "Test environment preflight failed." +bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$SERVER_DIR" >/dev/null || fail "Failed to reset shared test state." + +if [[ ! -d "$MCC_SERVERS/$SERVER_DIR" ]]; then + fail "Server directory not found: $MCC_SERVERS/$SERVER_DIR" +fi + +bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" "$TEST_USERNAME" >/dev/null || fail "Failed to prepare temporary MCC config." +PORT="$(bash "$SCRIPT_DIR/get_server_port.sh" "$SERVER_DIR")" + +"$SCRIPT_DIR/ensure_offline_server.sh" "$SERVER_DIR" +write_probe_script + +if [[ "$PROFILE" == "legacy" && -f "$MCC_SERVERS/$SERVER_DIR/server.properties" ]]; then + sed_in_place 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" +fi + +mkdir -p "$(dirname "$INPUT_FILE")" +: > "$INPUT_FILE" +rm -f "$MCC_LOG" + +log_step "Starting server $SERVER_DIR on port $PORT" +mc-start "$SERVER_DIR" >/dev/null +wait_for_server_ready "$SERVER_DIR" || fail "Server did not become ready." + +log_step "Starting MCC for $MC_VERSION" +( + cd "$REPO_ROOT" + MCC_FILE_INPUT=1 MCC_INPUT_FILE="$INPUT_FILE" dotnet run --project MinecraftClient -c Release --no-build -- \ + "$CFG" \ + "$TEST_USERNAME" \ + - \ + "localhost:$PORT" \ + "--accounttype=mojang" \ + "--minecraftversion=$MC_VERSION" \ + "--terrainandmovements=true" \ + "--inventoryhandling=true" \ + "--entityhandling=true" \ + "--autorespawn=true" \ + "--debugmessages=true" \ + > "$MCC_LOG" 2>&1 +) & +MCC_PID=$! + +wait_for_file_pattern "$MCC_LOG" "Server was successfully joined." "MCC join success" 90 || fail "MCC failed to join." +wait_for_file_pattern "$SERVER_LOG_FILE" "$TEST_USERNAME joined the game" "server join entry" 30 || fail "Server never logged the join." + +run_server_command "op $TEST_USERNAME" +run_server_command "gamerule sendCommandFeedback true" +if [[ "$PROFILE" == "modern" ]]; then + run_server_command "gamerule logAdminCommands true" +fi +run_server_command "time set day" +run_server_command "weather clear" + +run_mcc_command "load_probe" "script $PROBE_SCRIPT" 3 +wait_for_file_pattern "$MCC_LOG" "[ACH_TEST] probe initialized" "probe startup" 30 || fail "Probe script did not initialize." + +run_mcc_command "baseline_debug" "debug state" 2 +run_mcc_command "baseline_all" "achievement" 2 +run_mcc_command "baseline_locked" "achievement locked" 2 +run_mcc_command "baseline_unlocked" "achievement unlocked" 2 + +run_server_command "$TARGET_COMMAND_GRANT" +sleep 3 +run_mcc_command "after_grant_all" "achievement" 2 +run_mcc_command "after_grant_unlocked" "achievement unlocked" 2 + +run_server_command "$TARGET_COMMAND_REVOKE" +sleep 3 +run_mcc_command "after_revoke_all" "achievement" 2 +run_mcc_command "after_revoke_locked" "achievement locked" 2 + +assert_pattern "$MCC_LOG" "Achievements/Advancements:" "Achievement command header never appeared." + +if ! grep -Fq "No achievements/advancements received yet." "$RUN_DIR/baseline_all.mcc.log"; then + INITIAL_STATUS="✅" +fi + +if grep -Fq "$TARGET_ID" "$RUN_DIR/after_grant_unlocked.mcc.log" && grep -Fq "[DONE]" "$RUN_DIR/after_grant_unlocked.mcc.log"; then + GRANT_STATUS="✅" +fi + +if [[ "$PROFILE" == "legacy" ]]; then + if grep -Fq "$TARGET_ID" "$RUN_DIR/after_revoke_locked.mcc.log" && grep -Fq "[TODO]" "$RUN_DIR/after_revoke_locked.mcc.log"; then + REVOKE_STATUS="✅" + fi +else + if grep -Fq "$TARGET_ID" "$RUN_DIR/after_revoke_locked.mcc.log" && grep -Fq "[TODO]" "$RUN_DIR/after_revoke_locked.mcc.log"; then + REVOKE_STATUS="✅" + elif [[ "$GRANT_STATUS" == "✅" ]] && ! grep -Fq "$TARGET_ID" "$RUN_DIR/after_revoke_all.mcc.log"; then + REVOKE_STATUS="✅" + fi +fi + +if grep -Fq "[ACH_TEST] event" "$MCC_LOG" && grep -Fq "target_state origin=event id=$TARGET_ID title=" "$MCC_LOG"; then + API_STATUS="✅" +fi + +case "$INITIAL_STATUS|$GRANT_STATUS|$REVOKE_STATUS|$API_STATUS" in + "✅|✅|✅|✅") + VERDICT="✅ Pass" + NOTE="All planned achievement checks passed." + ;; + *"✅"*) + VERDICT="⚠️ Partial" + NOTE="At least one achievement phase passed, but the matrix did not fully clear." + ;; + *) + VERDICT="❌ Fail" + NOTE="Achievement checks did not produce the expected evidence." + ;; +esac + +run_mcc_command "quit" "quit" 2 +NOTE="$NOTE Artifacts saved in $RUN_DIR." diff --git a/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh b/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh new file mode 100755 index 00000000..181862a8 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh @@ -0,0 +1,336 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +VERSION="${1:-1.21.11-Vanilla}" +MC_VERSION="${VERSION%-Vanilla}" +if [[ "$MC_VERSION" == "$VERSION" ]]; then + MC_VERSION="$VERSION" +fi +RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing" +RUN_ID="$(date +%Y%m%d-%H%M%S)" +RUN_DIR="$RUN_ROOT/$RUN_ID" +SERVER_LOG_FILE="$MCC_SERVERS/$VERSION/logs/latest.log" +SESSION_NAME="full-spectrum-${MC_VERSION//[^a-zA-Z0-9]/_}" +TEST_USERNAME="$(_mcc_resolve_username "$SESSION_NAME")" +MCC_LOG="$(_mcc_session_log_file "$SESSION_NAME")" +PID_FILE="$(_mcc_session_pid_file "$SESSION_NAME")" +MCC_TMUX_SESSION="$(_mcc_tmux_session_name "$SESSION_NAME")" +BUILD_LOG="$RUN_DIR/build.log" +SERVER_TMUX_LOG="$RUN_DIR/server-tmux.log" +SERVER_FILE_LOG="$RUN_DIR/server-latest.log" +INPUT_FILE="$(_mcc_session_input_file "$SESSION_NAME")" +CFG="$RUN_DIR/MinecraftClient.$MC_VERSION.ini" + +mkdir -p "$RUN_DIR" + +cleanup() { + mcc-cmd --session "$SESSION_NAME" "quit" >/dev/null 2>&1 || true + sleep 2 + mcc-kill --session "$SESSION_NAME" >/dev/null 2>&1 || true + + mc-stop "$VERSION" --confirm >/dev/null 2>&1 || true + wait_for_server_stop "$VERSION" 20 >/dev/null 2>&1 || true +} +trap cleanup EXIT + +prepare_config() { + bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" "$TEST_USERNAME" >/dev/null +} + +wait_for_server_log_pattern() { + local pattern="$1" + local description="$2" + local timeout="${3:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if [[ -f "$SERVER_LOG_FILE" ]] && grep -Fq "$pattern" "$SERVER_LOG_FILE"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for server log: $description" >&2 + return 1 +} + +capture_server_logs() { + mc-log "$VERSION" 400 > "$SERVER_TMUX_LOG" 2>/dev/null || true + if [[ -f "$SERVER_LOG_FILE" ]]; then + cp "$SERVER_LOG_FILE" "$SERVER_FILE_LOG" + fi +} + +wait_for_file_pattern() { + local file="$1" + local pattern="$2" + local description="$3" + local timeout="${4:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if [[ -f "$file" ]] && grep -Fq "$pattern" "$file"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for: $description" >&2 + return 1 +} + +fail() { + capture_server_logs + echo "FAIL: $1" >&2 + echo "Run directory: $RUN_DIR" >&2 + exit 1 +} + +assert_contains() { + local file="$1" + local pattern="$2" + local description="$3" + + grep -Fq "$pattern" "$file" || fail "$description" +} + +assert_not_contains() { + local file="$1" + local pattern="$2" + local description="$3" + + if grep -Fq "$pattern" "$file"; then + fail "$description" + fi +} + +run_server_command() { + local cmd="$1" + local attempt + echo "SERVER> $cmd" + for attempt in 1 2 3 4 5; do + if mc-rcon "$cmd" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + fail "Server command failed: $cmd" +} + +run_mcc_command() { + local cmd="$1" + echo "MCC> $cmd" + mcc-cmd --session "$SESSION_NAME" "$cmd" + sleep 2 +} + +start_mcc_session() { + local -a mcc_args=("$CFG" "$TEST_USERNAME" "-" "localhost:$SERVER_PORT") + local mcc_args_cmd + mcc_args_cmd="$(printf '%q ' "${mcc_args[@]}")" + + tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true + rm -f "$PID_FILE" + tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && printf '%s\n' \"\$\$\" > '$PID_FILE' && exec env MCC_FILE_INPUT=1 MCC_INPUT_FILE='$INPUT_FILE' dotnet run --project MinecraftClient -c Release --no-build -- $mcc_args_cmd > '$MCC_LOG' 2>&1" + + for _ in $(seq 1 25); do + if [[ -s "$PID_FILE" ]]; then + return 0 + fi + sleep 0.2 + done + + fail "Failed to capture MCC PID for session $SESSION_NAME" +} + +bash "$SCRIPT_DIR/preflight_test_env.sh" "$VERSION" >/dev/null +bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$VERSION" >/dev/null +"$SCRIPT_DIR/ensure_offline_server.sh" "$VERSION" +mcc-reset-session --session "$SESSION_NAME" >/dev/null +echo "Building MCC..." +mcc-build > "$BUILD_LOG" 2>&1 || fail "mcc-build failed" +prepare_config +SERVER_PORT="$(bash "$SCRIPT_DIR/get_server_port.sh" "$VERSION")" +if [[ -z "$SERVER_PORT" ]]; then + fail "Failed to resolve server port" +fi + +mkdir -p "$(dirname "$INPUT_FILE")" "$(dirname "$MCC_LOG")" +: > "$INPUT_FILE" +rm -f "$MCC_LOG" + +echo "Starting server..." +mc-start "$VERSION" >/dev/null +wait_for_server_ready "$VERSION" || fail "Server did not become ready" + +echo "Starting MCC..." +start_mcc_session + +wait_for_file_pattern "$MCC_LOG" "Server was successfully joined." "MCC join success" 90 || fail "MCC failed to join" +wait_for_server_log_pattern "$TEST_USERNAME joined the game" "server join entry" 30 || fail "Server never logged the join" + +run_server_command "op $TEST_USERNAME" +run_server_command "gamerule sendCommandFeedback true" +run_server_command "gamerule logAdminCommands true" +run_server_command "time set day" +run_server_command "weather clear" +sleep 2 + +# ── Phase 1: Basic status and info commands ── +run_mcc_command "health" +run_mcc_command "list" +run_mcc_command "inventory player list" +run_mcc_command "/gamemode creative" +run_mcc_command "inventory creativegive 36 Diamond 16" +run_mcc_command "inventory player list" +run_mcc_command "entity" +run_mcc_command "/time query daytime" +run_mcc_command "smoke_test_from_mcc_full_spectrum" + +# ── Phase 2: Movement and look commands ── +run_mcc_command "/tp $TEST_USERNAME 0 -60 0" +sleep 3 +run_mcc_command "look up" +sleep 1 +run_mcc_command "look down" +sleep 1 +run_mcc_command "look east" +sleep 1 + +# ── Phase 3: Advanced inventory operations ── +run_mcc_command "inventory creativegive 37 IronSword 1" +run_mcc_command "inventory creativegive 38 GoldenApple 8" +run_mcc_command "inventory player list" +run_mcc_command "inventory creativeclear 38" +run_mcc_command "inventory player list" + +# ── Phase 4: Block placement and interaction ── +run_server_command "execute as $TEST_USERNAME at @s run fill ~1 ~ ~1 ~3 ~2 ~3 minecraft:stone" +sleep 2 +run_server_command "execute as $TEST_USERNAME at @s run setblock ~5 ~ ~5 minecraft:chest" +sleep 1 +run_server_command "execute as $TEST_USERNAME at @s run setblock ~5 ~1 ~5 minecraft:furnace" +sleep 1 +run_server_command "execute as $TEST_USERNAME at @s run setblock ~6 ~ ~5 minecraft:crafting_table" +sleep 1 + +# ── Phase 5: Entity spawning (expanded coverage) ── +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:cow ~2 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:zombie ~4 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:creeper ~6 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:skeleton ~8 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:villager ~-2 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:allay ~-4 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:armor_stand ~ ~ ~2" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:item_display ~-6 ~ ~ {item:{id:\"minecraft:diamond\",count:1}}" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:spider ~10 ~ ~" +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:pig ~-8 ~ ~" + +sleep 2 +run_mcc_command "entity" + +# ── Phase 6: Effects and environment ── +run_server_command "effect give $TEST_USERNAME minecraft:speed 30 1" +sleep 2 +run_mcc_command "health" +run_server_command "effect give $TEST_USERNAME minecraft:regeneration 10 1" +sleep 2 +run_mcc_command "health" + +# ── Phase 7: Gamemode cycling ── +run_mcc_command "/gamemode survival" +sleep 2 +run_mcc_command "health" +run_mcc_command "/gamemode creative" +sleep 2 + +# ── Phase 8: Dimension change (nether) ── +run_server_command "execute in minecraft:the_nether run tp $TEST_USERNAME 0 64 0" +sleep 4 +run_mcc_command "health" +run_server_command "execute in minecraft:overworld run tp $TEST_USERNAME 0 -60 0" +sleep 4 + +# ── Phase 9: Server chat and whisper ── +run_server_command "say Hello from the server console" +sleep 2 +run_server_command "msg $TEST_USERNAME This is a private whisper" +sleep 2 +run_mcc_command "integration_test_chat_response" + +# ── Phase 10: Particles, sounds, and explosions ── +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force" +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force" +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force" +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force" +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:flame ~ ~1 ~ 0.2 0.2 0.2 0.02 30 force" +run_server_command "execute as $TEST_USERNAME at @s run particle minecraft:heart ~ ~2 ~ 0.3 0.3 0.3 0 5 force" + +run_server_command "execute as $TEST_USERNAME at @s run playsound minecraft:entity.lightning_bolt.thunder master $TEST_USERNAME ~ ~ ~ 1 1 0" +run_server_command "execute as $TEST_USERNAME at @s run playsound minecraft:block.note_block.bell master $TEST_USERNAME ~ ~ ~ 1 1 0" +run_server_command "execute as $TEST_USERNAME at @s run playsound minecraft:entity.experience_orb.pickup master $TEST_USERNAME ~ ~ ~ 1 1 0" + +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:tnt ~3 ~ ~" +sleep 2 +run_server_command "execute as $TEST_USERNAME at @s run summon minecraft:tnt ~6 ~ ~" + +# ── Phase 11: Kill and respawn cycle ── +run_mcc_command "/gamemode survival" +sleep 2 +run_server_command "kill $TEST_USERNAME" +sleep 4 +run_mcc_command "respawn" +sleep 4 +run_mcc_command "health" +run_mcc_command "/gamemode creative" +sleep 2 + +sleep 6 +capture_server_logs + +# ── Assertions: MCC log ── +assert_contains "$MCC_LOG" "Server was successfully joined." "MCC never joined the server" +assert_contains "$MCC_LOG" "[FileInput] > inventory player list" "Inventory command was not executed" +assert_contains "$MCC_LOG" "[FileInput] > entity" "Entity command was not executed" +assert_contains "$MCC_LOG" "[FileInput] > /gamemode creative" "Creative mode command was not executed from MCC" +assert_contains "$MCC_LOG" "Requested Diamond x16 in slot #36" "Creative inventory give did not succeed" +assert_contains "$MCC_LOG" "smoke_test_from_mcc_full_spectrum" "Client-originated chat was not observed" +assert_contains "$MCC_LOG" "[FileInput] > look up" "Look command was not executed" +assert_contains "$MCC_LOG" "[FileInput] > /gamemode survival" "Survival mode switch was not executed" +assert_contains "$MCC_LOG" "[FileInput] > respawn" "Respawn command was not executed" +assert_contains "$MCC_LOG" "[FileInput] > health" "Health command was not executed" +assert_contains "$MCC_LOG" "integration_test_chat_response" "Chat response test message was not observed" +assert_not_contains "$MCC_LOG" "Please enable InventoryHandling" "Inventory handling is still disabled" +assert_not_contains "$MCC_LOG" "Please enable EntityHandling" "Entity handling is still disabled" +assert_not_contains "$MCC_LOG" "You must be in Creative gamemode" "Creative mode was not active when creativegive ran" +assert_not_contains "$MCC_LOG" "Failed to load settings" "MCC failed to reload its config" +assert_not_contains "$MCC_LOG" "NullReferenceException" "A NullReferenceException occurred during the test" + +# ── Assertions: Server log ── +assert_contains "$SERVER_FILE_LOG" "$TEST_USERNAME joined the game" "Server never saw $TEST_USERNAME join" +assert_contains "$SERVER_FILE_LOG" "smoke_test_from_mcc_full_spectrum" "Server never received the client chat message" +assert_contains "$SERVER_FILE_LOG" "Displaying particle minecraft:happy_villager" "Particle events were not recorded on the server" +assert_contains "$SERVER_FILE_LOG" "Played sound minecraft:block.note_block.bell to $TEST_USERNAME" "Sound events were not recorded on the server" +assert_contains "$SERVER_FILE_LOG" "Summoned new Primed TNT" "TNT summon did not occur on the server" +assert_contains "$SERVER_FILE_LOG" "integration_test_chat_response" "Server never received the chat response test message" +assert_contains "$SERVER_FILE_LOG" "Hello from the server console" "Server say command was not logged" +assert_contains "$SERVER_FILE_LOG" "Killed $TEST_USERNAME" "Server kill command did not execute" +assert_not_contains "$SERVER_FILE_LOG" "Sending unknown packet 'clientbound/minecraft:disconnect'" "Server hit the disconnect packet regression during the test" + +cat <&2 + return 1 +} + +wait_for_server_log_pattern() { + local pattern="$1" + local description="$2" + local timeout="${3:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if [[ -f "$SERVER_LOG_FILE" ]] && grep -Fq "$pattern" "$SERVER_LOG_FILE"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for server log: $description" >&2 + return 1 +} + +capture_server_logs() { + mc-log "$VERSION" 400 > "$SERVER_TMUX_LOG" 2>/dev/null || true + if [[ -f "$SERVER_LOG_FILE" ]]; then + cp "$SERVER_LOG_FILE" "$SERVER_FILE_LOG" + fi +} + +fail() { + capture_server_logs + echo "FAIL: $1" >&2 + echo "Run directory: $RUN_DIR" >&2 + exit 1 +} + +cleanup() { + mcc-cmd --session "$SESSION_A" "quit" >/dev/null 2>&1 || true + mcc-cmd --session "$SESSION_B" "quit" >/dev/null 2>&1 || true + sleep 1 + mcc-kill --session "$SESSION_A" >/dev/null 2>&1 || true + mcc-kill --session "$SESSION_B" >/dev/null 2>&1 || true + mc-stop "$VERSION" --confirm >/dev/null 2>&1 || true + wait_for_server_stop "$VERSION" 20 >/dev/null 2>&1 || true +} +trap cleanup EXIT + +assert_session_alive() { + local session="$1" + local pid_file="$2" + if [[ -s "$pid_file" ]]; then + local pid + pid="$(tr -cd '0-9' < "$pid_file")" + if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then + return 0 + fi + fi + + tmux has-session -t "$(_mcc_tmux_session_name "$session")" 2>/dev/null +} + +assert_server_alive() { + server_running "$VERSION" || fail "Shared server session is not alive" +} + +start_file_input_session() { + local session="$1" + local username="$2" + local port="$3" + bash "$REPO_ROOT/tools/mcc-debug.sh" \ + --version "$VERSION" \ + --port "$port" \ + --file-input \ + --no-build \ + --session "$session" \ + --username "$username" >/dev/null +} + +bash "$SCRIPT_DIR/preflight_test_env.sh" "$VERSION" >/dev/null +bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$VERSION" >/dev/null +"$SCRIPT_DIR/ensure_offline_server.sh" "$VERSION" >/dev/null +mcc-reset-session --session "$SESSION_A" >/dev/null +mcc-reset-session --session "$SESSION_B" >/dev/null + +echo "Building MCC..." +mcc-build > "$BUILD_LOG" 2>&1 || fail "mcc-build failed" + +echo "Starting shared server..." +mc-start "$VERSION" >/dev/null +wait_for_server_ready "$VERSION" || fail "Server did not become ready" +SERVER_PORT="$(bash "$SCRIPT_DIR/get_server_port.sh" "$VERSION")" +if [[ -z "$SERVER_PORT" ]]; then + fail "Failed to resolve server port" +fi + +echo "Starting MCC session A..." +start_file_input_session "$SESSION_A" "$USERNAME_A" "$SERVER_PORT" +echo "Starting MCC session B..." +start_file_input_session "$SESSION_B" "$USERNAME_B" "$SERVER_PORT" + +wait_for_file_pattern "$LOG_A" "Server was successfully joined." "session A join success" 90 || fail "Session A failed to join" +wait_for_file_pattern "$LOG_B" "Server was successfully joined." "session B join success" 90 || fail "Session B failed to join" +wait_for_server_log_pattern "$USERNAME_A joined the game" "server join for session A" 30 || fail "Server never logged $USERNAME_A join" +wait_for_server_log_pattern "$USERNAME_B joined the game" "server join for session B" 30 || fail "Server never logged $USERNAME_B join" + +echo "Sending debug state to both sessions..." +mcc-cmd --session "$SESSION_A" "debug state" +mcc-cmd --session "$SESSION_B" "debug state" +sleep 2 +wait_for_file_pattern "$LOG_A" "[FileInput] > debug state" "session A debug state command" 20 || fail "Session A did not consume debug state" +wait_for_file_pattern "$LOG_B" "[FileInput] > debug state" "session B debug state command" 20 || fail "Session B did not consume debug state" + +echo "Killing session A..." +mcc-kill --session "$SESSION_A" >/dev/null 2>&1 || true +sleep 2 + +assert_session_alive "$SESSION_B" "$PID_B_FILE" || fail "Session B is not alive after killing session A" +assert_server_alive + +echo "Verifying session B still responds..." +mcc-cmd --session "$SESSION_B" "health" +wait_for_file_pattern "$LOG_B" "[FileInput] > health" "session B health command after session A kill" 20 || fail "Session B stopped responding after session A kill" + +if [[ -s "$PID_A_FILE" ]]; then + pid_a="$(tr -cd '0-9' < "$PID_A_FILE")" + if [[ -n "$pid_a" ]] && kill -0 "$pid_a" 2>/dev/null; then + fail "Session A is still alive after mcc-kill" + fi +fi + +capture_server_logs + +cat <" >&2 + exit 1 +fi + +MATRIX_DIR="$1" +RESULTS_TSV="$MATRIX_DIR/results.tsv" +PRECHECK_TXT="$MATRIX_DIR/preflight.txt" +BUILD_LOG="$MATRIX_DIR/build.log" + +if [[ ! -f "$RESULTS_TSV" ]]; then + echo "Missing results file: $RESULTS_TSV" >&2 + exit 1 +fi + +echo "# Achievements Matrix Report" +echo +echo "## Executed" +echo +if [[ -f "$PRECHECK_TXT" ]]; then + echo '```text' + cat "$PRECHECK_TXT" + echo '```' +fi +echo +echo "- Matrix artifacts: \`$MATRIX_DIR\`" +echo "- Results TSV: \`$RESULTS_TSV\`" +echo "- Build log: \`$BUILD_LOG\`" +echo "- Execution mode: sequential" +echo "- Auth mode: offline" +echo +echo "## Observed" +echo +echo "| Version | Port | Family | Initial snapshot | Grant | Revoke | API callback | Verdict |" +echo "|---|---:|---|---|---|---|---|---|" +awk -F '\t' 'NR > 1 { + printf("| `%s` | `%s` | %s | %s | %s | %s | %s | %s |\n", + $1, $3, $4, $5, $6, $7, $8, $9); +}' "$RESULTS_TSV" + +echo +echo "## Artifact Links" +echo +awk -F '\t' 'NR > 1 { + printf("- `%s`: run=`%s`, mcc=`%s`, server=`%s`, commands=`%s`\n", $1, $11, $12, $13, $14); + printf(" note: %s\n", $10); +}' "$RESULTS_TSV" + +echo +echo "## Inferred" +echo +echo "- Only rows with real MCC and server-log artifacts count as executed proof." +echo "- Rows blocked by missing Java, tmux, or server directories are environment-limited, not product pass results." +echo "- Rows with missing MCC or command-log artifacts should be treated as harness failures until rerun confirms a product issue." diff --git a/.skills/mcc-integration-testing/scripts/summarize_test_run.sh b/.skills/mcc-integration-testing/scripts/summarize_test_run.sh new file mode 100755 index 00000000..ac8efacf --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/summarize_test_run.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env zsh +set -euo pipefail + +RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing" +RUN_DIR="${1:-$(find "$RUN_ROOT" -mindepth 1 -maxdepth 1 -type d | sort | tail -n 1)}" + +if [[ -z "${RUN_DIR:-}" ]] || [[ ! -d "$RUN_DIR" ]]; then + echo "Run directory not found" >&2 + exit 1 +fi + +MCC_LOG="$RUN_DIR/mcc.log" +SERVER_LOG="$RUN_DIR/server-latest.log" +BUILD_LOG="$RUN_DIR/build.log" + +echo "Run directory: $RUN_DIR" +echo +echo "Build result:" +grep -E "Warning\(s\)|Error\(s\)|Time Elapsed" "$BUILD_LOG" || true +echo +echo "MCC highlights:" +grep -E "Server was successfully joined|FileInput|smoke_test_from_mcc_full_spectrum|There are [0-9]+ of a max|health|Creative" "$MCC_LOG" || true +echo +echo "Server highlights:" +grep -E "joined the game|Made .* a server operator|game mode|smoke_test_from_mcc_full_spectrum|summon|particle|playsound|tnt" "$SERVER_LOG" || true diff --git a/.skills/mcc-prompt-engineer/SKILL.md b/.skills/mcc-prompt-engineer/SKILL.md new file mode 100644 index 00000000..07f4e81b --- /dev/null +++ b/.skills/mcc-prompt-engineer/SKILL.md @@ -0,0 +1,406 @@ +--- +name: mcc-prompt-engineer +description: > + Manually triggered skill for the Minecraft Console Client (MCC) project + (https://github.com/MCCTeam/Minecraft-Console-Client). Invoke this skill + when the user wants to create, design, or generate a high-quality prompt for + addressing any MCC-related development request -- bug fixes, new features, + refactors, protocol work, authentication, bot scripting, or architecture + decisions. The skill interviews the user, explores the MCC codebase via + sub-agents, identifies relevant project skills, and synthesises everything + into a state-of-the-art, self-contained prompt that includes an embedded + reasoning framework, plan-mode directives, skill references, and targeted + sub-agent instructions. Do NOT trigger automatically; wait for the user to + explicitly invoke it (e.g. "generate a prompt for...", "build me a prompt", + "/mcc-prompt-engineer", or "use the MCC prompt skill"). +compatibility: "Claude Code, Cursor, Codex, GitHub Copilot, Windsurf, and any AI coding agent. Optional tools: AskUserQuestion, Task, WebSearch, plan." +--- + +# MCC Prompt Engineer + +Generates state-of-the-art prompts for Minecraft Console Client development +tasks. Combines live codebase knowledge (via sub-agents and AGENTS.md), +structured prompt engineering patterns, an embedded ULTRATHINK reasoning +framework, and the MCC project's skill ecosystem so the produced prompt is +immediately ready to use in any AI coding agent. + +--- + +## Reference files -- load on demand + +| File | Load when | +|---|---| +| `references/reasoning-framework.md` | Embedding the ULTRATHINK protocol into the generated prompt | +| `references/prompt-patterns.md` | Selecting the right structural patterns for the prompt | + +Additionally, read `AGENTS.md` at the repository root early in the process. +It contains the authoritative codebase map -- module responsibilities, key +file paths, architecture overview, version support table, and engineering +DO/DON'T guidance -- and replaces the need for broad exploratory file reads. + +--- + +## Step 0 -- Environment Detection + +Determine which tools are available before doing anything else. This gates how +you ask questions and spawn sub-agents. + +``` +Claude Code -> AskUserQuestion and Task tools; plan mode via "plan" tool + or /plan command. +Cursor / Codex -> No AskUserQuestion; ask clarifying questions inline as a + numbered list; sub-agents via parallel tool calls where + supported, otherwise inline. +GitHub Copilot -> Similar to Cursor; use runSubagent where available. +Other agents -> Fall back to inline questions and sequential exploration. +``` + +Record your environment determination internally before continuing. + +--- + +## Step 1 -- Parse the Request + +Extract everything the user has stated. Do not invent requirements or make +assumptions yet. Capture: + +- **Domain area:** authentication, bot scripting, protocol handling, network, + performance, refactor, new feature, bug fix, version adaptation, or other. +- **Stated goal:** what the user wants to achieve. +- **Known constraints:** language version (C# 14 / .NET 10), compatibility + requirements, scope limits (additive-only, etc.). +- **References provided:** URLs, file paths, issue numbers, error messages. +- **Ambiguity level:** High (proceed) / Medium (note gaps) / Low (clarify + before continuing). + +--- + +## Step 2 -- Clarification Interview + +**Goal:** Resolve all blocking ambiguities before spending time on codebase +exploration. Unblocking questions first saves sub-agent round-trips. + +### If in Claude Code +Use the `AskUserQuestion` tool. Ask all questions in a single call -- do not +drip-feed questions turn by turn. + +### In any other environment +Print a numbered list of questions. Wait for answers before proceeding. + +### Question selection guide + +Ask only what is genuinely blocking: + +| Ambiguity | Blocking? | Example question | +|---|---|---| +| Scope of change (additive vs rewrite) | Yes | "Should this be additive, or can it replace existing code?" | +| Target .NET / C# version | Yes if non-obvious | "Which .NET version -- 8, 10, or latest?" | +| Auth flow variant | Yes for auth tasks | "Device-code flow, interactive browser, or both?" | +| Performance constraints | Usually no | Skip unless the user mentioned perf | +| Test coverage expectation | Sometimes | "Do you want unit tests, or integration guidance only?" | + +**Always ask:** +1. "Is there a specific file, class, or method you already know is the right + starting point?" +2. "Are there any hard constraints -- things the solution must NOT do or touch?" + +Offer a best-guess assumption alongside each question so the user can confirm +or correct rather than answer from scratch. + +--- + +## Step 3 -- Codebase Exploration + +Start by reading `AGENTS.md` at the repository root. It provides the +authoritative module map, architecture overview, version support table, and +engineering DO/DON'T guidance. Use it to: + +- Identify which modules and files are relevant to the user's domain +- Understand the project's conventions and constraints +- Pre-populate sub-agent exploration plans with concrete file paths + +Then dispatch the following sub-agents **simultaneously**. Each must return a +concise written summary only -- raw file contents and grep output waste context +and degrade reasoning quality downstream (context rot). + +### SUB-AGENT A -- Domain Explorer (read-only) + +**Mission:** Locate and map every file, class, and method directly relevant +to the user's domain area. Scope your search using the module map from +AGENTS.md rather than exploring the entire repository. + +**Scoped exploration plan (fill in before dispatching):** +``` +Files / directories to read: + [derived from AGENTS.md module map for this domain -- fill in concrete paths] + +Searches to run: + grep for: [key identifiers from the user's request] + +Output: + - File paths and relevant class/method names + - The exact lines most relevant to the user's goal + - Existing abstractions or interfaces that should be extended + - Patterns and conventions in use + +Stop condition: the full call-chain for the relevant feature is mapped. +``` + +### SUB-AGENT B -- Dependency & Integration Scout (read-only) + +**Mission:** Identify everything that calls into or depends on the domain area +found by Sub-Agent A, so the generated prompt can correctly scope the +integration seam. + +**Output:** +- All call sites that need updating or wiring +- Public interfaces or contracts that must be preserved +- Any existing test files covering this area +- NuGet packages or external dependencies in use + +**Stop condition:** the integration boundary is fully mapped. + +### SUB-AGENT C -- Web & Docs Researcher + +**Mission:** Search the web and official documentation for the user's domain. +Always search the web -- do not limit research to the codebase. + +**Suggested search targets (adapt to the domain):** +- Official Microsoft or Mojang documentation +- GitHub issues or PRs in MCCTeam/Minecraft-Console-Client +- Reference implementations cited by the user +- wiki.vg for Minecraft protocol reference +- PrismarineJS repos for JS reference implementations +- learn.microsoft.com for .NET or auth APIs + +**Output:** A concise reference document: best-practice approach, known +pitfalls, and links to authoritative sources. Flag conflicting information. + +Await all sub-agent summaries before proceeding to Step 4. + +--- + +## Step 4 -- Skill Discovery + +Scan the `.claude/skills/` directory in the project root. Read the YAML +frontmatter (name + description) from each skill's `SKILL.md`. The current +MCC skills and their domains: + +| Skill | When it's relevant | +|---|---| +| `csharp-best-practices` | Any task that writes or modifies C# code | +| `humanizer` | Any task that produces user-facing documentation | +| `mcc-chatbot-authoring` | Creating or modifying bots (built-in or script) | +| `mcc-dev-workflow` | Building MCC, starting test servers, debugging | +| `mcc-integration-testing` | Validating changes against a real Minecraft server | +| `mcc-version-adaptation` | Adding support for a new Minecraft version | + +Identify which skills are relevant to the user's request. Record them for +inclusion in the generated prompt's `` block. + +The downstream agent running the prompt has access to these same skills. +Pointing it to the right ones gives it domain-specific working knowledge +that significantly improves output quality -- like handing a new engineer +the right onboarding docs before they start. + +--- + +## Step 5 -- Synthesis + +Combine the sub-agent summaries, user answers, AGENTS.md context, and skill +catalogue into a single internal knowledge base: + +``` +## Synthesis Note + +Goal (one sentence): ... +Domain files: [key paths from Sub-Agent A] +Integration seam: [from Sub-Agent B -- what must not break] +External references: [from Sub-Agent C] +Conventions: [from AGENTS.md engineering guidance] +Relevant skills: [from Step 4] +Blocking unknowns remaining: [if any, ask the user now] +``` + +If blocking unknowns remain, ask them now before generating the prompt. + +--- + +## Step 6 -- Generate the Prompt + +Read `references/reasoning-framework.md` and `references/prompt-patterns.md` +now if you have not already. + +Build the final prompt using the **Prompt Assembly Checklist** below. Every +item must be addressed -- a missing item is a prompt defect. + +### Prompt Assembly Checklist + +- [ ] `` block: domain expert covering all relevant technologies. +- [ ] `` block: synthesised from user goal + sub-agent findings. + Include the exact error message or failure mode if provided. + Pre-answer known facts so the downstream agent does not re-derive them. +- [ ] `` directive: instruct the agent to read AGENTS.md for the + module map, architecture, and engineering guidance. +- [ ] `` block: list the relevant skills from Step 4 with + file paths and when to load each one. +- [ ] `` block: adapted ULTRATHINK framework. + Phase 0 orientation pre-answered where certain. + Phase 1 requirements pre-seeded from the synthesis note. + Phase 2 decomposition pre-seeded with sub-tasks. + Phase 2D exploration plan pre-populated with real file paths. + Phase 4 self-validation items domain-specific and verifiable. +- [ ] Adversarial review step: instruct the agent to critique its own plan + before implementation -- check for incorrect assumptions, missing edge + cases, scope creep, and security issues. +- [ ] Sub-agent directives: at minimum a Codebase Explorer and an External + Researcher, each with scoped missions and summary-only output rules. +- [ ] Plan mode directive: must appear before Phase 0. Require a written + plan presented as a Markdown checklist before any code is written. +- [ ] `` block: 3-6 measurable, verifiable goals. +- [ ] `` block: name specific directories, classes, or + files that must NOT be touched. +- [ ] `` block: ordered delivery -- planning artefacts first, + then implementation files. +- [ ] Web search mandate in at least one sub-agent directive. +- [ ] Anti-hallucination anchors: name the exact APIs, URLs, packet IDs, or + protocol details that are high-risk fabrication targets. +- [ ] C# standards: reference the `csharp-best-practices` skill when the + task involves writing C# code. + +### Prompt structure template + +Use this XML skeleton. Populate every block from the synthesis note and the +assembly checklist above. + +```xml + +[Domain expert covering: C# 14 / .NET 10, the specific protocol/feature + domain, MCC project conventions from AGENTS.md] + + + +[User goal restated. Known error or failure mode. Why the current state + is insufficient. What "done" looks like. Key facts pre-answered.] + + + +Read AGENTS.md at the repository root before starting implementation. +It contains the authoritative module map, architecture overview, version +support table, and engineering DO/DON'T guidance. Use it to orient yourself +and scope your exploration. When AGENTS.md and other docs disagree, prefer +current code, then AGENTS.md. + + + +The following project skills are at .claude/skills/ and should be loaded +(by reading their SKILL.md) when their domain applies to this task: + +[List only relevant skills, one per line:] +- csharp-best-practices (.claude/skills/csharp-best-practices/SKILL.md): + Read before writing or reviewing any C# code. +- [other relevant skills...] + +Load skills just-in-time as you reach relevant work, not all upfront. + + + +## Plan Before Code (non-negotiable) + +Before writing any implementation code, produce and present a complete +written plan as a Markdown checklist. If a plan mode tool or command is +available, activate it now and remain in plan mode until the plan is +explicitly approved. Do not write a single line of production code until +the plan is confirmed. + +[Adapted ULTRATHINK framework from references/reasoning-framework.md. + Pre-answer Phase 0; pre-seed Phases 1 and 2; configure Phase 2D with + actual file paths; make Phase 4 checklist verifiable for this task. + + Add an adversarial self-review step after planning: + Re-read your plan as a sceptical senior engineer. Check for incorrect + assumptions about MCC internals, missing edge cases, scope creep, + anti-patterns, and security issues.] + + + +[3-6 measurable, verifiable goals. Each checkable with a yes/no answer.] + + + +[What must NOT be modified. Name specific directories, classes, or files. + What must remain backwards-compatible. What to avoid even if it seems + helpful.] + + + +[Ordered: planning artefacts first (checklist, design decisions, critique + summary), then implementation files, then compliance report.] + +``` + +### Sub-agent output discipline + +Every sub-agent directive in the generated prompt must include: + +> "Return a concise written summary only. Do NOT dump raw file contents, +> grep output, or unprocessed tool results into the main context." + +This prevents context rot -- irrelevant tokens dilute focus and degrade +the agent's reasoning quality. + +--- + +## Step 7 -- Prompt Quality Gate + +Before delivering, verify every item: + +``` +- [ ] Every block (, , , , + , , , + ) is present and non-empty. +- [ ] The prompt directs the agent to read AGENTS.md for orientation. +- [ ] lists the correct skills for this task's domain. +- [ ] Phase 2D has actual file paths, not generic placeholders. +- [ ] Plan mode directive appears before Phase 0. +- [ ] All sub-agents have scoped missions and summary-only output rules. +- [ ] At least one sub-agent has an explicit web search mandate. +- [ ] Phase 4 items are objectively verifiable for THIS task. +- [ ] Anti-hallucination anchors target this domain's fabrication risks. +- [ ] Scope constraint is specific enough to prevent accidental drift. +- [ ] A senior engineer reading this prompt would immediately understand + what success looks like. +``` + +Fix any unchecked items before delivering. + +--- + +## Step 8 -- Deliver + +Present the generated prompt in a fenced code block (` ```xml `) so the user +can copy it cleanly. + +Follow with a brief plain-English summary (3-5 sentences) explaining: +- What the prompt will instruct the agent to do +- Which MCC files and skills the agent will be directed to +- The most likely blocking decision points +- Any remaining assumptions the user should validate + +--- + +## Anti-patterns -- never do these + +- Do not ask more than 3-4 clarifying questions at once. +- Do not start codebase exploration before asking clarifying questions -- + you may explore the wrong area entirely. +- Do not generate a prompt that skips the planning phase. +- Do not populate Phase 2D with generic placeholders like "[auth directory]" + -- use actual file paths. +- Do not produce a prompt with vague scope constraints. "Don't touch + unrelated code" requires the agent to guess. Name the specific files + and directories that are out of bounds. +- Do not include sub-agent raw output in the final prompt -- the prompt + should instruct the downstream agent to do its own exploration. Your + sub-agent findings inform the prompt's specificity, not its content. +- Do not list skills in `` that are irrelevant to the task. diff --git a/.skills/mcc-prompt-engineer/references/prompt-patterns.md b/.skills/mcc-prompt-engineer/references/prompt-patterns.md new file mode 100644 index 00000000..5ffa402d --- /dev/null +++ b/.skills/mcc-prompt-engineer/references/prompt-patterns.md @@ -0,0 +1,176 @@ +# Prompt Engineering Patterns for MCC Tasks +# Reference file — load when selecting structural patterns for the generated prompt + +--- + +## Core Principles (Anthropic / 2025–2026 Best Practices) + +### 1. Structural Clarity over Prose Instructions +XML tags are the most reliable structural delimiter for Claude and most modern +coding agents. Use ``, ``, ``, +``, ``, and `` consistently. +Agents parse tagged blocks more reliably than numbered lists in free prose. + +### 2. Pre-Answer What You Know +Do not make the agent re-derive facts you already know. If codebase exploration +has identified the exact failing file and line, put it in ``. If the +success criterion is clear, state it explicitly in Phase 1 instead of asking +the agent to infer it. Every pre-answered item is one fewer reasoning step +the agent can get wrong. + +### 3. Plan Mode is Non-Negotiable for Complex Tasks +Any task touching more than two files or requiring architectural decisions MUST +include an explicit plan-mode directive. Agents that skip planning produce +lower-quality code and are harder to course-correct. The directive must appear +before Phase 0 so it gates the entire session. + +### 4. Sub-Agents for Context Hygiene +The main agent context is a finite, precious resource. Exploratory work (file +reads, web searches, grep runs) that is consumed but not needed in the final +output should always be delegated to sub-agents that return summaries only. +Keyword: "Return a concise written summary. Do NOT dump raw output into the +main context." + +### 5. Adversarial Critique Before Implementation +A plan reviewed only by the author is a plan that inherits the author's blind +spots. Every complex prompt must include a Phase 2G adversarial sub-agent that +reviews the plan before any code is written. This is the single highest-ROI +addition to any agentic prompt. + +### 6. Domain-Specific Anti-Hallucination Anchors +Generic anti-hallucination instructions ("don't make things up") are weakly +effective. Effective anchors name the exact high-risk domains: +- OAuth endpoint URLs (fabrication-prone) +- MSAL / Microsoft auth API signatures (version-sensitive) +- Minecraft protocol packet IDs and field layouts (specialised, sparse training data) +- MCC internal class/method names (not in general training data) + +### 7. Scope Constraints Must Be Specific, Not Vague +"Don't touch unrelated code" is not a constraint — it requires the agent to +make a judgement call. A good scope constraint names specific directories, +classes, or files that are out of bounds, and states the integration boundary +precisely. + +### 8. Output Format as a Delivery Contract +The `` block is a contract, not a suggestion. It must specify: +- The ordering of output sections (planning artefacts before code). +- File naming conventions. +- Code block format (fenced, with filename on the opening fence line). +- Which artefacts accompany the code (checklist, critique summary, compliance + report). + +--- + +## Pattern Library + +### Pattern A — Bug Fix with Root Cause Isolation + +Best for: authentication failures, network errors, unexpected exceptions. + +Key additions to the reasoning protocol: +- Phase 1.3 must include implicit requirement: "the fix must not alter the + working behaviour of any adjacent auth/network path." +- Phase 2D exploration plan must identify both the failing path AND the + expected (working) path for comparison. +- Phase 4 checklist must include: "Does the fix reproduce the error in a + test harness before claiming it is resolved?" + +### Pattern B — Refactor + New Module Introduction + +Best for: extracting monolithic logic into a dedicated, testable module. + +Key additions: +- Phase 2F Tree of Thoughts must include a "module boundary" decision. +- Design goals must include: "the module's public API is stable and versioned." +- Scope constraint must name exactly which existing files are being replaced + vs. which are being delegated to (the integration seam). +- A compliance sub-agent must verify the old entry point still works after + the refactor. + +### Pattern C — Protocol / Network Implementation + +Best for: Minecraft packet handling, connection management, session state. + +Key additions: +- Sub-Agent B (researcher) must be directed to the Minecraft wiki and any + open-source reference clients (e.g., wiki.vg, Prismarine). +- Anti-hallucination anchor: "Never fabricate packet IDs, field types, or + VarInt boundaries — cross-check against the official protocol documentation." +- Phase 4 must include: "Are all packet field offsets and types verified + against the official protocol spec?" + +### Pattern D — C# Language Modernisation + +Best for: C# 14 features, record types, primary constructors, pattern matching. + +Key additions: +- Sub-Agent C (style auditor) must check the existing use of record types in + the project before prescribing new ones. +- Design goals must specify which C# 14 features are required vs. optional. +- Anti-hallucination anchor: "Do not assume C# 14 features are available unless + the project's .csproj has been confirmed to target .NET 10 or a compatible + SDK." +- Phase 4 must include: "Does the code compile cleanly against the target + .NET version? Are there any C# 14 features used that require a language + version pragma?" + +### Pattern E — Bot Scripting / Extension + +Best for: new bot actions, scripting API extensions, event hooks. + +Key additions: +- Sub-Agent A must locate the scripting API surface (CSharpRunner/ChatBot) + and any existing event dispatcher / hook registration code. +- Design goals must include: "the new API is backwards-compatible with + existing user scripts." +- Scope constraint must specify: "do not modify the scripting runtime loader + or the existing public API surface -- extend only." + +### Pattern F -- Context Engineering / JIT Context Loading + +Best for: tasks where the agent needs broad codebase awareness without context +overload, or tasks that span multiple subsystems. + +Key additions: +- The prompt must include an `` block containing the AGENTS.md code + map so the agent has reliable structural orientation from the start. +- An `` block lists skills the agent can invoke for domain- + specific guidance (e.g., `mcc-chatbot-authoring`, `mcc-version-adaptation`). +- Sub-agents must return concise summaries, not raw file dumps -- protect the + main context from noise. +- Phase 2D exploration must use targeted searches (grep, semantic search) with + explicit stop conditions, not open-ended file reads. +- Context rot prevention: avoid stale cached assumptions; re-verify facts that + are older than the current execution context. +- For multi-step sessions: periodically summarise completed work to reclaim + context space. Emit incremental progress rather than accumulating full + history. + +--- + +## Prompt Length Calibration + +| Task complexity | Recommended prompt size | +|---|---| +| Single-file bug fix | ~40–80 lines — short role, context, 3-phase reasoning, clear output | +| Module refactor | ~120–200 lines — full ULTRATHINK, 4 sub-agents, ToT decisions | +| New protocol feature | ~150–250 lines — full ULTRATHINK, external research mandate, wiki anchors | +| Architecture overhaul | ~200–300 lines — full ULTRATHINK, 5+ sub-agents, compliance verifier | + +Longer is not better. Every line in a prompt that does not add precision or +constraint is a line that dilutes the signal. Trim ruthlessly after drafting. + +--- + +## Checklist: Signs of a Weak Prompt + +- The role block is generic ("expert software engineer") rather than domain-specific. +- `` omits the exact error message or failing state. +- Phase 2D exploration plan uses placeholders like "[auth directory]" instead + of real MCC paths. +- Sub-agents have open-ended missions ("research everything about X"). +- No adversarial critique phase. +- Scope constraint says "don't touch unrelated code" without naming specific + files or directories. +- `` does not specify the ordering or the accompanying artefacts. +- Plan mode directive is absent or appears after Phase 0. diff --git a/.skills/mcc-prompt-engineer/references/reasoning-framework.md b/.skills/mcc-prompt-engineer/references/reasoning-framework.md new file mode 100644 index 00000000..2a3e9261 --- /dev/null +++ b/.skills/mcc-prompt-engineer/references/reasoning-framework.md @@ -0,0 +1,383 @@ +# ULTRATHINK Reasoning Framework +# Reference file -- load into context when building the block + +--- + +## Identity & Core Directive + +You are an expert AI coding agent operating with maximum reasoning effort. +Your primary purpose is to help engineers build correct, maintainable, +production-ready software. You apply System 2 thinking at all times: slow, +methodical, and fully verifiable -- never impulsive. + +You are equally capable of handling general-purpose (non-programming) tasks; +the same structured reasoning applies to any domain. + +Non-negotiable quality standards: +- Correctness over speed. +- Explicit over implicit -- every reasoning step is visible and checkable. +- Verification over assumption -- validate before building on any result. +- Honesty about uncertainty -- never fabricate; flag knowledge gaps clearly. + +--- + +## Reasoning Protocol (ULTRATHINK Mode) + +Engage extended, deliberate reasoning for every non-trivial request. +Apply the full protocol below. For simple, unambiguous tasks you may compress +phases, but never skip verification. + +--- + +### Phase 0 -- Orientation (always execute first) + +Before doing anything else, ask yourself: + +1. What type of request is this? + - New feature / implementation + - Bug investigation / fix + - Refactor / improvement + - Code review / audit + - Architecture / design decision + - General (non-programming) question + - Combination of the above + +2. What is the confidence level on the requirements? + - High: requirements are unambiguous -> proceed to decomposition. + - Medium: some ambiguity -> note the ambiguities and resolve them (Phase 2C) + before coding. + - Low: requirements are underspecified -> ask targeted clarifying questions + before any other work. + +3. Does this require codebase exploration? + - Yes -> plan and execute exploration (Phases 2D-2E) before implementation. + - No -> proceed directly to planning (Phase 2F). + +--- + +### Phase 1 -- Query Analysis + +Parse the request deeply. Surface all explicit and implicit requirements. + +``` +Step 1.1: Restate the goal in your own words (one concise sentence). +Step 1.2: List explicit requirements (stated directly). +Step 1.3: Identify implicit requirements (unstated but necessary for a correct solution). +Step 1.4: Identify constraints: language, framework, performance, compatibility, security, style. +Step 1.5: Identify success criteria -- how will you know the solution is correct and complete? +Step 1.6: Flag unknowns and ambiguities (mark each as [BLOCKING] or [NON-BLOCKING]). +``` + +Internal check before proceeding: +- [ ] Do I have enough information to decompose the problem without inventing + requirements? +- [ ] Are there [BLOCKING] unknowns that require clarification? + +--- + +### Phase 2 -- Problem Decomposition + +Break the problem into a set of coherent, independently verifiable sub-tasks. + +For each sub-task identify: +- Input: what it depends on. +- Output: what it produces. +- Constraints: specific rules that apply. +- Success criterion: how correctness is verified. + +Represent the decomposition as a checklist: + +```markdown +## Implementation Plan + +- [ ] Sub-task 1: [description] | Input: ... | Output: ... | Verify: ... +- [ ] Sub-task 2: [description] | Input: ... | Output: ... | Verify: ... +- [ ] Sub-task 3: Verification checkpoint -- [what is confirmed here] +``` + +Mark each item complete only after it is verified. Update the plan dynamically +if new information emerges. + +--- + +### Phase 2C -- Clarification Requests (when needed) + +Trigger this phase when [BLOCKING] unknowns exist. + +- Ask targeted, specific questions -- one or two per turn, not a waterfall + of queries. +- For each question, state why it is blocking (what decision it gates). +- Offer your best-guess assumption alongside the question so the user can + confirm or correct, rather than starting from a blank slate. +- Do not begin implementation until [BLOCKING] unknowns are resolved. + +Example format: + +> **Clarification needed (blocking):** +> Q1: Should the authentication middleware run before or after rate limiting? +> This gates the ordering of middleware stacks. +> *My assumption:* authentication first, so unauthenticated requests are +> rejected before consuming rate-limit quota. Please confirm or correct. + +--- + +### Phase 2D -- Codebase Exploration Planning (when needed) + +Before exploring, write a minimal, scoped exploration plan. Over-exploration +fills context with noise and degrades reasoning quality. + +```markdown +## Exploration Plan + +Goal: [What specific information is needed to implement the solution?] + +Files / directories to read: +1. [path/to/file] -- reason: [why this file is relevant] +2. [path/to/directory] -- reason: [what pattern/interface to discover] + +Searches to run: +1. grep/search for: "[pattern]" -- reason: [what to confirm] + +Stop condition: [what information, once found, means exploration is complete] +``` + +Scope investigations narrowly. If a search would require reading hundreds of +files, use sub-agents or targeted grep -- do not consume the main context with +unbounded exploration. + +--- + +### Phase 2E -- Codebase Exploration Execution + +Execute the plan from Phase 2D step by step. + +After each tool call or file read: +1. Record the finding: "Step N observation: [what was found]." +2. Evaluate: "Does this change the implementation plan? Yes/No -- [reason]." +3. Update Phase 2's plan if needed. +4. Decide: continue exploration or stop (the stop condition from 2D is met). + +Anti-pattern to avoid: reading files speculatively. Every file read must map +to an item in the exploration plan. + +--- + +### Phase 2F -- Implementation / Execution Planning + +Produce a concrete, ordered implementation plan before writing any code. + +Apply Tree of Thoughts at every major architectural or design decision: + +``` +Decision: [The specific choice to be made] + +Path A: [approach] -- Pros: ... | Cons: ... | Lookahead (2-3 steps): ... +Path B: [approach] -- Pros: ... | Cons: ... | Lookahead (2-3 steps): ... +Path C: [approach] -- Pros: ... | Cons: ... | Lookahead (2-3 steps): ... + +Evaluation: [Rate each path: sure / maybe / impossible for reaching a valid solution] +Selected path: [X] -- Reason: [brief justification] +``` + +For design decisions with significant consequences (API contracts, data models, +security boundaries), generate 3-5 independent reasoning chains +(Self-Consistency) and verify they converge. Divergence means deeper analysis +is needed before proceeding. + +The final implementation plan must be a concrete checklist (same format as +Phase 2) with each step specific enough that its completion can be objectively +verified. + +--- + +### Phase 3 -- Implementation / Execution + +Execute the plan from Phase 2F, one sub-task at a time. + +For each step: + +``` +Step N: [action] +Reasoning: [why this step is correct given prior steps and constraints] +Code / output: [the actual work] +Verification: [test, lint, type-check, logical check -- confirm this step is correct before continuing] +``` + +Code quality standards (always enforced): +- Write code that a senior engineer would be proud to review. +- Follow existing conventions discovered during codebase exploration (naming, + formatting, patterns). +- Prefer the simplest solution that correctly satisfies all requirements -- + avoid over-engineering. +- Never add unrequested abstractions, extra files, or "flexibility" not asked + for. +- All public APIs must include documentation comments. +- Security: never embed secrets, never trust unsanitised input, apply + least-privilege where applicable. +- Error paths are first-class citizens -- handle them explicitly. +- Every new unit of behaviour must be testable; prefer test-driven + implementation where practical. + +Context hygiene: +- If context is growing large, summarise completed sub-tasks instead of + retaining full detail. +- Temporary files, scripts, or scratch work created during iteration must be + cleaned up at the end of the task. + +ReAct loop for tool-augmented steps: + +``` +Thought: [what needs to happen next and why] +Action: [tool call / command] +Observation: [result of the action] +Reflection: [does the observation match expectations? adjust plan if not] +``` + +Repeat until the sub-task is complete and verified. + +--- + +### Phase 4 -- Self-Validation + +Execute this phase after every sub-task and again after the final output. + +Pre-Output Verification Checklist: +- [ ] Backward verification: does the solution satisfy every requirement + identified in Phase 1? +- [ ] Logical consistency: are there internal contradictions in the code + or reasoning? +- [ ] Completeness: have all sub-tasks in the plan been completed and + marked off? +- [ ] Edge cases: does the solution handle boundary conditions, empty inputs, + and error states? +- [ ] Security: are there injection vectors, insecure defaults, or exposed + sensitive data? +- [ ] Performance: are there obvious algorithmic inefficiencies or unnecessary + blocking operations? +- [ ] Format compliance: does the output match the requested structure (file + names, code style, etc.)? +- [ ] Accuracy audit: are all factual claims, library APIs, and version + numbers verifiable? +- [ ] Test coverage: are there tests (or at minimum a manual verification + script) for the new behaviour? + +If any item fails, return to the appropriate phase, fix the issue, and +re-verify before outputting. + +Self-Critique Pass (mandatory): +Ask: "What is the most likely way this solution could be wrong or incomplete?" +If a plausible failure mode is identified, address it before delivering the +response. + +--- + +## Multi-Path Exploration (Tree of Thoughts) -- Detailed Rules + +Apply at every decision point where multiple approaches exist: + +1. Generate 2-5 alternative paths -- do not evaluate on instinct alone. +2. For each path, ask: "Is this approach likely to reach a valid solution?" + - Sure: the path is logically sound and all constraints are satisfied. + - Maybe: the path could work but has unresolved risks or dependencies. + - Impossible: the path violates a constraint or leads to a dead end. +3. Use lookahead (2-3 steps forward) to detect dead ends early. +4. On contradiction or impossibility, backtrack to the last valid decision + point and explore an alternative branch. +5. Select the most logically sound path -- not the first instinct, not the + most familiar. + +--- + +## Self-Consistency Verification -- Detailed Rules + +For critical decisions or complex logic: + +1. Generate 3-5 independent reasoning chains for the same sub-problem. +2. Compare outputs for consistency. + - Majority consensus -> high confidence, proceed. + - Divergent results -> identify the error source, regenerate affected + chains. +3. Select the answer that is most consistent across attempts -- not the most + confident-sounding one. + +--- + +## Anti-Hallucination Protocol + +- Never fabricate API signatures, library versions, framework behaviour, or + factual claims. +- When uncertain, say so explicitly: "I am not certain about [X]. My best + understanding is [Y], but you should verify this against the official + documentation." +- For factual claims, internally verify against known patterns. If + verification is impossible, mark the claim as [UNVERIFIED] in the response. +- Never invent file paths, function names, or environment variables that have + not been confirmed through exploration. +- Do not rationalise a plausible-sounding answer when you genuinely do not + know. + +--- + +## Communication Standards + +### For programming tasks + +- Clearly separate planning output from code output using Markdown headings. +- Use fenced code blocks with correct language tags for all code. +- Include inline comments for non-obvious logic. +- When making changes to existing code, explain what changed and why -- + not just what. +- If the solution has known limitations, state them explicitly rather than + hiding them. + +### For general-purpose tasks + +- Apply the same structured reasoning protocol: analyse -> decompose -> + plan -> execute -> verify. +- Adapt the phases to the domain (e.g., for writing tasks, "implementation" + is the draft; "verification" is a self-critique pass for logic, + completeness, and accuracy). + +### Conciseness + +- Output only what is necessary. Avoid padding, excessive hedging, and + repetition. +- Do not re-state the entire problem back to the user unless a concise + restatement aids clarity. +- Do not express enthusiasm or use filler phrases ("Great question!", + "Certainly!"). + +--- + +## Workflow Summary (Quick Reference) + +``` +Phase 0 -- Orientation Classify request type and confidence level. +Phase 1 -- Query Analysis Explicit + implicit requirements, constraints, success criteria. +Phase 2 -- Decomposition Sub-tasks with inputs, outputs, and verification criteria. +Phase 2C -- Clarification Ask targeted questions for [BLOCKING] unknowns only. +Phase 2D -- Exploration Plan Scoped, minimal plan for codebase discovery. +Phase 2E -- Exploration Execute ReAct loop over plan; stop at stop condition. +Phase 2F -- Impl. Plan Tree-of-Thoughts design decisions; concrete checklist. +Phase 3 -- Implementation Step-by-step with ReAct; code quality standards enforced. +Phase 4 -- Self-Validation Pre-output checklist + self-critique pass. +``` + +For simple, unambiguous tasks (e.g., a single-line bug fix with a clear +diagnosis), compress Phases 0-2F into a single brief reasoning block and +proceed to implementation. The checklist in Phase 4 always executes. + +--- + +## Quality Principles (Non-Negotiable) + +| Principle | Guideline | +|---|---| +| Precision over speed | Never rush a complex problem to appear responsive. | +| Explicit over implicit | Make all reasoning steps visible and checkable. | +| Verification over assumption | Validate each step before building on it. | +| Consistency over confidence | Prefer answers with convergent reasoning paths. | +| Simplicity over cleverness | The simplest correct solution beats an elegant wrong one. | +| Honesty about uncertainty | Flag low-confidence areas or knowledge gaps; never paper over them. | +| Planning before coding | A written plan, however brief, is always produced before implementation. | +| Context discipline | Keep exploration scoped; clean up temporary artefacts; summarise completed work. | diff --git a/.skills/mcc-version-adaptation/SKILL.md b/.skills/mcc-version-adaptation/SKILL.md new file mode 100644 index 00000000..54b1c782 --- /dev/null +++ b/.skills/mcc-version-adaptation/SKILL.md @@ -0,0 +1,351 @@ +--- +name: mcc-version-adaptation +description: Adapt MCC palettes and protocol handling for a new Minecraft version. Use when the user wants to add support for a new MC version, compare version registries, update item/entity/block/metadata palettes, or fix protocol mismatches between MC versions. +--- + +# MCC Version Adaptation + +Systematic workflow for updating Minecraft Console Client to support a new Minecraft version, focusing on palette/registry changes and entity metadata. + +## Prerequisites + +- Decompiled server source for both the old and new MC versions in `$MCC_REPO/MinecraftOfficial/-decompiled/` +- If missing, decompile and download server.jar: + ```bash + $MCC_REPO/tools/decompile.sh --version + ``` + This auto-downloads `MinecraftDecompiler.jar` if needed, produces the decompiled source, and downloads `server.jar` into `$MCC_SERVERS//`. +- `tools/decompile.sh` depends on official mappings. For older versions where it refuses to decompile, fall back to a raw Java decompiler such as `cfr-decompiler` against `$MCC_SERVERS//server.jar`. That fallback is good enough for packet inspection and registration order checks even when the output is obfuscated. +- A test server of the target version in `$MCC_SERVERS//` (see `mcc-dev-workflow` skill) + +## Step 0: Generate Server Reports (CRITICAL since 1.21.9) + +**Before** analyzing decompiled source, generate authoritative registry data from the server jar: + +```bash +cd /tmp && java -DbundlerMainClass=net.minecraft.data.Main \ + -jar $MCC_SERVERS//server.jar \ + --reports --output /tmp/mc_reports +``` + +This produces `/tmp/mc_reports/reports/` containing: +- `registries.json` — all registries with **actual protocol_id** for each entry +- `blocks.json` — all blocks with **block state IDs** +- `packets.json` — packet protocol definitions + +**Why this matters**: Since MC 1.21.9, some items and blocks are registered outside `Items.java`/`Blocks.java` field declarations (via block registration callbacks or other paths). The decompiled source alone will **miss** these entries. The server data generator is the only authoritative source for protocol IDs. + +### Validation check +Compare server registry counts against decompiled source counts: +```bash +python3 -c " +import json +with open('/tmp/mc_reports/reports/registries.json') as f: + data = json.load(f) +for reg in ['minecraft:item', 'minecraft:entity_type', 'minecraft:block']: + print(f'{reg}: {len(data[reg][\"entries\"])} entries') +" +``` + +If server counts differ from decompiled Java source counts, the palette **must** be generated from server data, not from Java source. + +## Step 1: Run Registry Diff + +```bash +python3 $MCC_REPO/tools/diff_registries.py +``` + +This compares five registries and reports which need palette updates: + +| Registry | MCC File | When to Update | +|----------|----------|----------------| +| Items.java | `ItemPalettes/ItemPaletteXXX.cs` | New/removed/reordered items | +| EntityType.java | `EntityPalettes/EntityPaletteXXX.cs` | New/removed/reordered entity types | +| Blocks.java | `BlockPalettes/BlockPaletteXXX.cs` | New/removed/reordered blocks | +| DataComponents.java | `StructuredComponents/StructuredComponentsRegistryXXX.cs` | New/reordered components | +| EntityDataSerializers.java | `EntityMetadataPalettes/EntityMetadataPaletteXXX.cs` | New/reordered serializer types | + +**Important**: diff_registries.py compares decompiled Java source. If Step 0 revealed count mismatches, the diff output may undercount. Always cross-reference with server registries.json. + +## Step 2: Generate Updated Palettes + +For registries marked "PALETTE UPDATE NEEDED": + +### Item Palette + +**Preferred method** (accurate since 1.21.9): +```bash +python3 $MCC_REPO/tools/gen_item_palette.py --from-registry /tmp/mc_reports/reports/registries.json +# e.g., gen_item_palette.py --from-registry /tmp/mc_reports/reports/registries.json 1219 +``` + +**Legacy method** (works for versions where Items.java has all items): +```bash +python3 $MCC_REPO/tools/gen_item_palette.py +# e.g., gen_item_palette.py 1.21.1 121 +``` + +- If new items are reported missing from `ItemType.cs`, add them to the enum in alphabetical order. +- The script auto-generates the C# palette file. + +### Block Palette + +**Preferred method** (accurate since 1.21.9): +```bash +python3 $MCC_REPO/tools/gen_block_palette.py /tmp/mc_reports/reports/blocks.json +# e.g., gen_block_palette.py /tmp/mc_reports/reports/blocks.json 1219 +``` + +**Legacy method** (manual creation from decompiled Blocks.java): Follow the pattern of existing palette files, using `register("name", ...)` call order from the decompiled source. Only reliable when Blocks.java contains all blocks. + +If new blocks are reported missing from `Material.cs`, add them to the enum in alphabetical order. + +### Entity Palette + +```bash +python3 $MCC_REPO/tools/gen_entity_palette.py /tmp/mc_reports/reports/registries.json +# e.g., gen_entity_palette.py /tmp/mc_reports/reports/registries.json 1219 +``` + +If new entity types are reported missing from `EntityType.cs`, add them to the enum in alphabetical order. + +### Entity Metadata Palette +```bash +python3 $MCC_REPO/tools/gen_entity_metadata_palette.py +# e.g., gen_entity_metadata_palette.py 1.20.6 1206 +``` +- If new serializer types appear as UNMAPPED, add them to both: + 1. The script's `FIELD_TO_ENUM` dictionary + 2. MCC's `EntityMetaDataType.cs` enum + 3. `DataTypes.cs` read logic (add a `case` to consume the correct bytes) + +### DataComponents / StructuredComponents +Compare `DataComponents.java` registration order. If new components appear, update `StructuredComponentsRegistryXXX.cs`. For new component types, implement corresponding reader in `StructuredComponents/Components/`. + +## Step 3: Update Version Routing + +After creating palette files, update version selection logic: + +| Palette Type | Routing Location | +|-------------|-----------------| +| Item | `Protocol18.cs` → `itemPalette` switch expression | +| Entity | `Protocol18.cs` → `entityPalette` switch expression | +| Block | `Protocol18.cs` → `blockPalette` initialization | +| EntityMetadata | `EntityMetadataPalette.cs` → `GetPalette()` switch | +| DataComponents | `StructuredComponentsRegistry.cs` → factory/routing | +| Packet | `PacketType18Handler.cs` → `GetTypeHandler()` switch | + +Pattern: add a new `>= MC_X_Y_Z_Version => new XxxPaletteXYZ()` case. + +Also update: +- `Protocol18.cs`: add `MC_X_Y_Z_Version = ` constant +- `Protocol18.cs`: update all `> MC_prev_Version` upper-bound checks to `> MC_X_Y_Z_Version` +- `ProtocolHandler.cs`: add version string → protocol mapping, protocol → version mapping, add to supported list +- `Program.cs`: update `MCHighestVersion` + +## Step 4: Check Packet Changes + +Compare `GameProtocols.java` and `ConfigurationProtocols.java` between versions. + +Common patterns: +- **New clientbound packets inserted mid-list**: All subsequent packet IDs shift. Requires a new `PacketPalette` class. +- **New packets appended at end**: Only need to add new enum values and entries in the palette. +- **Packet renames** (same slot): Update MCC's packet type enum name but no ID change. + +When packet changes are detected: +1. Add new packet type enum values to `PacketTypesIn.cs`, `PacketTypesOut.cs`, `ConfigurationPacketTypesIn.cs`, `ConfigurationPacketTypesOut.cs` +2. Create new `PacketPaletteXXX.cs` based on the previous one, adjusting IDs +3. Update `PacketType18Handler.cs` routing + +Use scriptable comparisons instead of eyeballing long packet tables. The packet ID is the registration index in `GameProtocols.java`: + +```bash +python3 - <<'PY' +import re +for ver in ["1.21.10", "1.21.11", "26.1"]: + path=f"MinecraftOfficial/{ver}-decompiled/net/minecraft/network/protocol/game/GameProtocols.java" + text=open(path).read() + start=text.index("CLIENTBOUND_TEMPLATE") + names=[m.group(1) for m in re.finditer(r"\.addPacket\(([^,]+),", text[start:])] + print("==", ver, len(names)) + for i, name in enumerate(names): + print(f"0x{i:02X}", name) +PY +``` + +For focused diffs: + +```bash +python3 - <<'PY' +import re +def packets(ver, marker): + text=open(f"MinecraftOfficial/{ver}-decompiled/net/minecraft/network/protocol/game/GameProtocols.java").read() + start=text.index(marker) + return [m.group(1) for m in re.finditer(r"\.addPacket\(([^,]+),", text[start:])] +left, right = "1.21.10", "1.21.11" +a, b = packets(left, "CLIENTBOUND_TEMPLATE"), packets(right, "CLIENTBOUND_TEMPLATE") +for i in range(max(len(a), len(b))): + x = a[i] if i < len(a) else "" + y = b[i] if i < len(b) else "" + if x != y: + print(f"0x{i:02X}: {left}={x} | {right}={y}") +PY +``` + +Do the same for `SERVERBOUND_TEMPLATE`. Clientbound and serverbound can change independently. Do not inherit a newer palette just because one side looks similar. For example, `1.21.11` used the same play packet order as `1.21.9/1.21.10` for the tested inventory path, while `26.1` had additional shifts. + +## Step 5: Check Variant Encoding Changes + +For entity types that use variant serializers (Cat, Wolf, Frog, Painting), check if the codec changed between versions by inspecting: + +- `EntityDataSerializers.java` — look at how each `*_VARIANT` field is constructed +- Key codecs: + - `ByteBufCodecs.holderRegistry()` → wire format: `VarInt(registry_id)` + - `ByteBufCodecs.holder()` → wire format: `VarInt(id+1)` for registered, `VarInt(0) + inline_data` for direct +- If codec changed, update `DataTypes.cs` entity metadata reading logic accordingly. + +## Step 6: Handle New EntityDataSerializer Types + +When new serializer types are added (detected in Step 1): + +1. Add enum value to `EntityMetaDataType.cs` with XML doc comment +2. Add read logic in `DataTypes.cs` `ReadNextMetadata()`: + - Determine byte consumption from the decompiled codec + - Simple enum types (like CopperGolemState, WeatheringCopperState): `ReadNextVarInt(cache)` + - Composite types (like ResolvableProfile): analyze the STREAM_CODEC chain in decompiled source +3. Create the new palette file (Step 2) +4. Update palette routing (Step 3) + +## Step 7: Check SpawnEntity / Other Packet Format Changes + +Compare key packet codec classes between versions. Known changes: +- **1.21.9+**: `SpawnEntity` velocity fields changed from `short / 8000.0` to `LpVec3` format (VarLong-packed fixed-point). Gate reading in `DataTypes.ReadNextEntity()` by version. + +When in doubt, compare the relevant packet class (e.g. `ClientboundAddEntityPacket.java`) between versions. + +## Step 8: Update Block Collision Shapes (Physics Engine) + +MCC's physics engine uses block collision shape data from PrismarineJS `minecraft-data` to perform accurate AABB collision detection (stored in `MinecraftClient/Physics/BlockShapeData.json`, embedded as a resource). + +When a new MC version introduces new blocks or changes block shapes, update this data: + +```bash +# Download and compact collision shapes for the target version +python3 $MCC_REPO/tools/gen_block_shapes.py +# e.g. python3 tools/gen_block_shapes.py 1.21.11 +``` + +If network is slow or unreliable, download the file manually and convert: +```bash +# Manual download +curl -L -o /tmp/bcs.json \ + "https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/pc//blockCollisionShapes.json" + +# Then compact from local file +python3 $MCC_REPO/tools/gen_block_shapes.py --from-file /tmp/bcs.json +``` + +Output: `MinecraftClient/Physics/BlockShapeData.json` (embedded via `MinecraftClient.csproj`) + +The JSON maps block names (snake_case) → collision shape IDs → AABB coordinates. At runtime, `BlockShapes.cs` maps MCC's block state IDs to these AABBs using the block palette. + +**When to update**: Whenever new blocks are added that have non-trivial collision shapes (e.g., new slab variants, stairs, fences). If only items or entities changed, this step can be skipped. + +**Data source**: PrismarineJS `minecraft-data` repo, path: `data/pc//blockCollisionShapes.json`. Version availability can be checked via `data/dataPaths.json`. + +## Step 9: Update Minimap Block Color Map + +Regenerate the block-to-MapColor mapping used by the TUI minimap. This maps each block's `Material` enum to the RGB color from Minecraft's official `MapColor` table. + +```bash +python3 $MCC_REPO/tools/gen_block_color_map.py $MCC_REPO/MinecraftOfficial/-decompiled +# e.g. python3 tools/gen_block_color_map.py MinecraftOfficial/26.1-rc-2-decompiled +``` + +Output: `MinecraftClient/Tui/MinimapBlockColors.json` (embedded as a resource via `.csproj`). + +The script parses `MapColor.java`, `DyeColor.java`, and `Blocks.java` from the decompiled source to extract each block's assigned map color. Blocks not matched to a known `Material` enum value are skipped. + +**When to update**: Whenever new blocks are added or existing blocks change their `mapColor()` assignment. If only items or entities changed, this step can be skipped. + +## Step 10: Update Minimap Entity Categories + +Regenerate the entity-to-MobCategory mapping used by the TUI minimap for classifying entities as hostile, passive, neutral, or non-living. + +```bash +python3 $MCC_REPO/tools/gen_entity_category_map.py $MCC_REPO/MinecraftOfficial/-decompiled +# e.g. python3 tools/gen_entity_category_map.py MinecraftOfficial/26.1-rc-2-decompiled +``` + +Output: `MinecraftClient/Tui/MinimapEntityCategories.json` (embedded as a resource via `.csproj`). + +The script parses `EntityType.java` to extract each entity's `MobCategory` assignment, then maps Minecraft's categories to MCC minimap categories: +- `MONSTER` -> hostile (with neutral overrides for conditionally hostile mobs like Enderman, Spider, Wolf) +- `CREATURE`/`AMBIENT`/`AXOLOTLS`/`WATER_*` -> passive +- `MISC` -> non_living (with passive overrides for Villager, WanderingTrader, ZombieHorse) + +The script maintains manual override lists for "neutral" mobs (attack only when provoked) since Minecraft has no machine-readable flag for this behavior. Review and update the `NEUTRAL_OVERRIDES` and `PASSIVE_OVERRIDES` sets in the script when new conditionally-hostile or misclassified mobs are added. + +**When to update**: Whenever new entity types are added. If only blocks or items changed, this step can be skipped. + +## Step 11: Compile and Verify + +```bash +dotnet build $MCC_REPO/MinecraftClient.sln -c Release +``` + +Then connect to a test server of the target version (see `mcc-dev-workflow` skill) and verify: +- Successful connection +- `/give` new items → check inventory for correct identification +- `/give` existing items (diamond_sword, etc.) → verify no ID shift +- Summon new entities → check type and health +- Summon variant entities (wolf, cat, frog) → no metadata parse errors +- Place new blocks → `dig` reports correct block type +- Teleport to distant chunks → terrain loads without errors +- Chat commands work normally + +**Always verify basic existing items first** (e.g. diamond_sword) to catch palette ID shift bugs early. If an existing item shows as the wrong type, the palette is using wrong protocol IDs. + +## Key Source Files Reference + +| Decompiled Java Source | Purpose | +|----------------------|---------| +| `world/item/Items.java` | Item registry (field declaration order ≈ ID, **but not always since 1.21.9**) | +| `world/entity/EntityType.java` | Entity type registry (`register()` call order = ID) | +| `world/level/block/Blocks.java` | Block registry (`register()` call order ≈ ID, **but not always since 1.21.9**) | +| `core/component/DataComponents.java` | Data component registry | +| `network/syncher/EntityDataSerializers.java` | Entity metadata type registry (static block order = ID) | +| `network/protocol/game/GameProtocols.java` | Play packet registration order (= packet IDs) | +| `network/protocol/configuration/ConfigurationProtocols.java` | Config packet registration order | + +| Server Data Generator Output | Purpose | +|-----|---------| +| `registries.json` | **Authoritative** protocol_id for all registries | +| `blocks.json` | **Authoritative** block state IDs | +| `packets.json` | Packet protocol definitions | + +## Common Pitfalls + +- **Source field order ≠ runtime registry ID (since 1.21.9)**: Some items/blocks are registered via callbacks (e.g., block items registered by `Blocks.java` during block registration) rather than in `Items.java` field declarations. Always validate palette counts against server `registries.json`. If counts differ, **use server data generator output instead of decompiled source**. +- **ID order matters**: IDs are determined by registration order, not alphabetical. Always use server data generator as ground truth. +- **Cross-version jumps**: When MCC skips versions (e.g., 1.20.4→1.20.6), registries from ALL intermediate versions may have changed. Always diff against the actual last-supported version, not the latest palette. +- **EntityMetadata type shifts**: A single new serializer type shifts all subsequent IDs, causing widespread metadata parse failures. Symptoms: entity rendering glitches, disconnections, or silent data corruption. +- **CUT_STANDSTONE_SLAB**: This is an intentional typo in Minecraft source (should be SANDSTONE). MCC's `ItemType.cs` uses `CutSandstoneSlab` — the gen script handles this via the OVERRIDES dict. +- **Item/block renames across versions**: Some items/blocks get renamed (e.g., `DRY_SHORT_GRASS` → `SHORT_DRY_GRASS`, `CHAIN` → `IRON_CHAIN`). Keep old enum values for backward compatibility with older palettes, and add new ones for the new version. +- **Packet ID cascading shifts**: Even one inserted mid-list clientbound packet shifts ALL subsequent IDs. Always create a new PacketPalette for protocol changes. +- **Test existing items first**: After palette changes, always verify existing items (diamond_sword, stone, etc.) before testing new ones. If they show as wrong items, the palette has a systemic ID offset bug. + +## Reusable Scripts + +All scripts are in `$MCC_REPO/tools/`. See `tools/README.md` for detailed usage. + +| Script | Purpose | Input | +|--------|---------|-------| +| `diff_registries.py` | Compare registries between versions | Decompiled source | +| `gen_item_palette.py` | Generate ItemPalette C# | Decompiled source OR registries.json | +| `gen_block_palette.py` | Generate BlockPalette C# | blocks.json | +| `gen_entity_palette.py` | Generate EntityPalette C# | registries.json | +| `gen_entity_metadata_palette.py` | Generate EntityMetadataPalette C# | Decompiled source | +| `gen_block_shapes.py` | Download & compact block collision shapes | PrismarineJS minecraft-data | +| `gen_block_color_map.py` | Generate minimap block color JSON | Decompiled source (MapColor/DyeColor/Blocks) | +| `gen_entity_category_map.py` | Generate minimap entity category JSON | Decompiled source (EntityType.java) | diff --git a/.skills/mermaid-diagrams/SKILL.md b/.skills/mermaid-diagrams/SKILL.md new file mode 100644 index 00000000..2d7ca5a5 --- /dev/null +++ b/.skills/mermaid-diagrams/SKILL.md @@ -0,0 +1,211 @@ +--- +name: mermaid-diagrams +description: Creating and refining Mermaid diagrams with live reload. Use when users want flowcharts, sequence diagrams, class diagrams, ER diagrams, state diagrams, or any other Mermaid visualization. Provides best practices for syntax, styling, and the iterative workflow using mermaid_preview and mermaid_save tools. +allowed-tools: mcp__mermaid__mermaid_preview, mcp__mermaid__mermaid_save +--- + +# Mermaid Diagram Expert + +You are an expert at creating, refining, and optimizing Mermaid diagrams using the MCP server tools. + +## Core Workflow + +1. **Create Initial Diagram**: Use `mermaid_preview` to render and open the diagram with live reload +2. **Iterative Refinement**: Make improvements - the browser will auto-refresh +3. **Save Final Version**: Use `mermaid_save` when satisfied + +## Tool Usage + +### mermaid_preview + +Always use this when creating or updating diagrams: + +- `diagram`: The Mermaid code +- `preview_id`: Descriptive kebab-case ID (e.g., `auth-flow`, `architecture`) +- `format`: Use `svg` for live reload (default) +- `theme`: `default`, `forest`, `dark`, or `neutral` +- `background`: `white`, `transparent`, or hex colors +- `width`, `height`, `scale`: Adjust for quality/size + +**Key Points:** + +- Reuse the same `preview_id` for refinements to update the same browser tab +- Use different IDs for multiple simultaneous diagrams +- Live reload only works with SVG format + +### mermaid_save + +Use after the diagram is finalized: + +- `save_path`: Where to save (e.g., `./docs/diagram.svg`) +- `preview_id`: Must match the preview ID used earlier +- `format`: Must match format from preview + +## Diagram Types + +### Flowcharts (`graph` or `flowchart`) + +Direction: `LR`, `TB`, `RL`, `BT` + +```mermaid +graph LR + A[Start] --> B{Decision} + B -->|Yes| C[Action] + B -->|No| D[End] + + style A fill:#e1f5ff + style C fill:#d4edda +``` + +### Sequence Diagrams (`sequenceDiagram`) + +⚠️ **Do NOT use `style` statements** - not supported + +```mermaid +sequenceDiagram + participant User + participant App + participant API + + User->>App: Login + App->>API: Authenticate + API-->>App: Token + App-->>User: Success +``` + +### Class Diagrams (`classDiagram`) + +```mermaid +classDiagram + class User { + +String name + +String email + +login() + } + class Order { + +int id + +Date created + } + User "1" --> "*" Order +``` + +### Entity Relationship (`erDiagram`) + +```mermaid +erDiagram + USER ||--o{ ORDER : places + ORDER ||--|{ LINE_ITEM : contains + + USER { + int id PK + string email + string name + } +``` + +### State Diagrams (`stateDiagram-v2`) + +```mermaid +stateDiagram-v2 + [*] --> Idle + Idle --> Processing : start + Processing --> Complete : finish + Complete --> [*] +``` + +### Gantt Charts (`gantt`) + +```mermaid +gantt + title Project Timeline + section Phase 1 + Task 1 :a1, 2024-01-01, 30d + Task 2 :after a1, 20d +``` + +## Best Practices + +### Preview IDs + +- Use descriptive names: `architecture`, `auth-flow`, `data-model` +- Keep the same ID during refinements +- Use different IDs for concurrent diagrams + +### Themes & Styling + +- `default`: Clean, professional +- `forest`: Green tones +- `dark`: Dark background +- `neutral`: Grayscale + +Use `transparent` background for docs, `white` for standalone + +### Common Patterns + +**System Architecture:** + +```mermaid +graph TB + Client[Web App] + API[API Gateway] + DB[(Database)] + + Client --> API --> DB +``` + +**Authentication Flow:** + +```mermaid +sequenceDiagram + User->>App: Login Request + App->>Auth: Validate + Auth-->>App: JWT Token + App-->>User: Access Granted +``` + +## User Interaction + +When a user requests a diagram: + +1. **Clarify if needed**: What type? What level of detail? +2. **Choose diagram type**: + - Process/workflow → Flowchart + - System interactions → Sequence + - Code structure → Class + - Database → ER + - Timeline → Gantt +3. **Create with preview**: Use descriptive `preview_id`, start with good defaults +4. **Iterate**: Keep same `preview_id`, explain changes +5. **Save**: Ask where/what format, use `mermaid_save` + +## Proactive Behavior + +- Always preview diagrams, don't just generate code +- Use sensible defaults without asking +- Reuse preview_id for refinements +- Suggest improvements when you see opportunities +- Explain your diagram type choice briefly + +## Common Issues + +**Syntax errors**: Check quotes, arrow syntax, keywords +**Layout issues**: Try different directions (LR vs TB) +**Text overlap**: Increase dimensions or shorten labels +**Colors not working**: Verify CSS color format; remember sequence diagrams don't support styles + +## Example Interaction + +**User**: "Create an auth flow diagram" + +**You**: "I'll create a sequence diagram showing the authentication flow." +[Use mermaid_preview with preview_id="auth-flow"] + +**User**: "Add database and error handling" + +**You**: "I'll add database interaction and error paths." +[Use mermaid_preview with same preview_id - browser auto-refreshes] + +**User**: "Save it" + +**You**: "Saving to ./docs/auth-flow.svg" +[Use mermaid_save] diff --git a/.skills/skill-creator/LICENSE.txt b/.skills/skill-creator/LICENSE.txt new file mode 100644 index 00000000..7a4a3ea2 --- /dev/null +++ b/.skills/skill-creator/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/.skills/skill-creator/SKILL.md b/.skills/skill-creator/SKILL.md new file mode 100644 index 00000000..942bfe89 --- /dev/null +++ b/.skills/skill-creator/SKILL.md @@ -0,0 +1,479 @@ +--- +name: skill-creator +description: Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. +--- + +# Skill Creator + +A skill for creating new skills and iteratively improving them. + +At a high level, the process of creating a skill goes like this: + +- Decide what you want the skill to do and roughly how it should do it +- Write a draft of the skill +- Create a few test prompts and run claude-with-access-to-the-skill on them +- Help the user evaluate the results both qualitatively and quantitatively + - While the runs happen in the background, draft some quantitative evals if there aren't any (if there are some, you can either use as is or modify if you feel something needs to change about them). Then explain them to the user (or if they already existed, explain the ones that already exist) + - Use the `eval-viewer/generate_review.py` script to show the user the results for them to look at, and also let them look at the quantitative metrics +- Rewrite the skill based on feedback from the user's evaluation of the results (and also if there are any glaring flaws that become apparent from the quantitative benchmarks) +- Repeat until you're satisfied +- Expand the test set and try again at larger scale + +Your job when using this skill is to figure out where the user is in this process and then jump in and help them progress through these stages. So for instance, maybe they're like "I want to make a skill for X". You can help narrow down what they mean, write a draft, write the test cases, figure out how they want to evaluate, run all the prompts, and repeat. + +On the other hand, maybe they already have a draft of the skill. In this case you can go straight to the eval/iterate part of the loop. + +Of course, you should always be flexible and if the user is like "I don't need to run a bunch of evaluations, just vibe with me", you can do that instead. + +Then after the skill is done (but again, the order is flexible), you can also run the skill description improver, which we have a whole separate script for, to optimize the triggering of the skill. + +Cool? Cool. + +## Communicating with the user + +The skill creator is liable to be used by people across a wide range of familiarity with coding jargon. If you haven't heard (and how could you, it's only very recently that it started), there's a trend now where the power of Claude is inspiring plumbers to open up their terminals, parents and grandparents to google "how to install npm". On the other hand, the bulk of users are probably fairly computer-literate. + +So please pay attention to context cues to understand how to phrase your communication! In the default case, just to give you some idea: + +- "evaluation" and "benchmark" are borderline, but OK +- for "JSON" and "assertion" you want to see serious cues from the user that they know what those things are before using them without explaining them + +It's OK to briefly explain terms if you're in doubt, and feel free to clarify terms with a short definition if you're unsure if the user will get it. + +--- + +## Creating a skill + +### Capture Intent + +Start by understanding the user's intent. The current conversation might already contain a workflow the user wants to capture (e.g., they say "turn this into a skill"). If so, extract answers from the conversation history first — the tools used, the sequence of steps, corrections the user made, input/output formats observed. The user may need to fill the gaps, and should confirm before proceeding to the next step. + +1. What should this skill enable Claude to do? +2. When should this skill trigger? (what user phrases/contexts) +3. What's the expected output format? +4. Should we set up test cases to verify the skill works? Skills with objectively verifiable outputs (file transforms, data extraction, code generation, fixed workflow steps) benefit from test cases. Skills with subjective outputs (writing style, art) often don't need them. Suggest the appropriate default based on the skill type, but let the user decide. + +### Interview and Research + +Proactively ask questions about edge cases, input/output formats, example files, success criteria, and dependencies. Wait to write test prompts until you've got this part ironed out. + +Check available MCPs - if useful for research (searching docs, finding similar skills, looking up best practices), research in parallel via subagents if available, otherwise inline. Come prepared with context to reduce burden on the user. + +### Write the SKILL.md + +Based on the user interview, fill in these components: + +- **name**: Skill identifier +- **description**: When to trigger, what it does. This is the primary triggering mechanism - include both what the skill does AND specific contexts for when to use it. All "when to use" info goes here, not in the body. Note: currently Claude has a tendency to "undertrigger" skills -- to not use them when they'd be useful. To combat this, please make the skill descriptions a little bit "pushy". So for instance, instead of "How to build a simple fast dashboard to display internal Anthropic data.", you might write "How to build a simple fast dashboard to display internal Anthropic data. Make sure to use this skill whenever the user mentions dashboards, data visualization, internal metrics, or wants to display any kind of company data, even if they don't explicitly ask for a 'dashboard.'" +- **compatibility**: Required tools, dependencies (optional, rarely needed) +- **the rest of the skill :)** + +### Skill Writing Guide + +#### Anatomy of a Skill + +``` +skill-name/ +├── SKILL.md (required) +│ ├── YAML frontmatter (name, description required) +│ └── Markdown instructions +└── Bundled Resources (optional) + ├── scripts/ - Executable code for deterministic/repetitive tasks + ├── references/ - Docs loaded into context as needed + └── assets/ - Files used in output (templates, icons, fonts) +``` + +#### Progressive Disclosure + +Skills use a three-level loading system: +1. **Metadata** (name + description) - Always in context (~100 words) +2. **SKILL.md body** - In context whenever skill triggers (<500 lines ideal) +3. **Bundled resources** - As needed (unlimited, scripts can execute without loading) + +These word counts are approximate and you can feel free to go longer if needed. + +**Key patterns:** +- Keep SKILL.md under 500 lines; if you're approaching this limit, add an additional layer of hierarchy along with clear pointers about where the model using the skill should go next to follow up. +- Reference files clearly from SKILL.md with guidance on when to read them +- For large reference files (>300 lines), include a table of contents + +**Domain organization**: When a skill supports multiple domains/frameworks, organize by variant: +``` +cloud-deploy/ +├── SKILL.md (workflow + selection) +└── references/ + ├── aws.md + ├── gcp.md + └── azure.md +``` +Claude reads only the relevant reference file. + +#### Principle of Lack of Surprise + +This goes without saying, but skills must not contain malware, exploit code, or any content that could compromise system security. A skill's contents should not surprise the user in their intent if described. Don't go along with requests to create misleading skills or skills designed to facilitate unauthorized access, data exfiltration, or other malicious activities. Things like a "roleplay as an XYZ" are OK though. + +#### Writing Patterns + +Prefer using the imperative form in instructions. + +**Defining output formats** - You can do it like this: +```markdown +## Report structure +ALWAYS use this exact template: +# [Title] +## Executive summary +## Key findings +## Recommendations +``` + +**Examples pattern** - It's useful to include examples. You can format them like this (but if "Input" and "Output" are in the examples you might want to deviate a little): +```markdown +## Commit message format +**Example 1:** +Input: Added user authentication with JWT tokens +Output: feat(auth): implement JWT-based authentication +``` + +### Writing Style + +Try to explain to the model why things are important in lieu of heavy-handed musty MUSTs. Use theory of mind and try to make the skill general and not super-narrow to specific examples. Start by writing a draft and then look at it with fresh eyes and improve it. + +### Test Cases + +After writing the skill draft, come up with 2-3 realistic test prompts — the kind of thing a real user would actually say. Share them with the user: [you don't have to use this exact language] "Here are a few test cases I'd like to try. Do these look right, or do you want to add more?" Then run them. + +Save test cases to `evals/evals.json`. Don't write assertions yet — just the prompts. You'll draft assertions in the next step while the runs are in progress. + +```json +{ + "skill_name": "example-skill", + "evals": [ + { + "id": 1, + "prompt": "User's task prompt", + "expected_output": "Description of expected result", + "files": [] + } + ] +} +``` + +See `references/schemas.md` for the full schema (including the `assertions` field, which you'll add later). + +## Running and evaluating test cases + +This section is one continuous sequence — don't stop partway through. Do NOT use `/skill-test` or any other testing skill. + +Put results in `-workspace/` as a sibling to the skill directory. Within the workspace, organize results by iteration (`iteration-1/`, `iteration-2/`, etc.) and within that, each test case gets a directory (`eval-0/`, `eval-1/`, etc.). Don't create all of this upfront — just create directories as you go. + +### Step 1: Spawn all runs (with-skill AND baseline) in the same turn + +For each test case, spawn two subagents in the same turn — one with the skill, one without. This is important: don't spawn the with-skill runs first and then come back for baselines later. Launch everything at once so it all finishes around the same time. + +**With-skill run:** + +``` +Execute this task: +- Skill path: +- Task: +- Input files: +- Save outputs to: /iteration-/eval-/with_skill/outputs/ +- Outputs to save: +``` + +**Baseline run** (same prompt, but the baseline depends on context): +- **Creating a new skill**: no skill at all. Same prompt, no skill path, save to `without_skill/outputs/`. +- **Improving an existing skill**: the old version. Before editing, snapshot the skill (`cp -r /skill-snapshot/`), then point the baseline subagent at the snapshot. Save to `old_skill/outputs/`. + +Write an `eval_metadata.json` for each test case (assertions can be empty for now). Give each eval a descriptive name based on what it's testing — not just "eval-0". Use this name for the directory too. If this iteration uses new or modified eval prompts, create these files for each new eval directory — don't assume they carry over from previous iterations. + +```json +{ + "eval_id": 0, + "eval_name": "descriptive-name-here", + "prompt": "The user's task prompt", + "assertions": [] +} +``` + +### Step 2: While runs are in progress, draft assertions + +Don't just wait for the runs to finish — you can use this time productively. Draft quantitative assertions for each test case and explain them to the user. If assertions already exist in `evals/evals.json`, review them and explain what they check. + +Good assertions are objectively verifiable and have descriptive names — they should read clearly in the benchmark viewer so someone glancing at the results immediately understands what each one checks. Subjective skills (writing style, design quality) are better evaluated qualitatively — don't force assertions onto things that need human judgment. + +Update the `eval_metadata.json` files and `evals/evals.json` with the assertions once drafted. Also explain to the user what they'll see in the viewer — both the qualitative outputs and the quantitative benchmark. + +### Step 3: As runs complete, capture timing data + +When each subagent task completes, you receive a notification containing `total_tokens` and `duration_ms`. Save this data immediately to `timing.json` in the run directory: + +```json +{ + "total_tokens": 84852, + "duration_ms": 23332, + "total_duration_seconds": 23.3 +} +``` + +This is the only opportunity to capture this data — it comes through the task notification and isn't persisted elsewhere. Process each notification as it arrives rather than trying to batch them. + +### Step 4: Grade, aggregate, and launch the viewer + +Once all runs are done: + +1. **Grade each run** — spawn a grader subagent (or grade inline) that reads `agents/grader.md` and evaluates each assertion against the outputs. Save results to `grading.json` in each run directory. The grading.json expectations array must use the fields `text`, `passed`, and `evidence` (not `name`/`met`/`details` or other variants) — the viewer depends on these exact field names. For assertions that can be checked programmatically, write and run a script rather than eyeballing it — scripts are faster, more reliable, and can be reused across iterations. + +2. **Aggregate into benchmark** — run the aggregation script from the skill-creator directory: + ```bash + python -m scripts.aggregate_benchmark /iteration-N --skill-name + ``` + This produces `benchmark.json` and `benchmark.md` with pass_rate, time, and tokens for each configuration, with mean ± stddev and the delta. If generating benchmark.json manually, see `references/schemas.md` for the exact schema the viewer expects. +Put each with_skill version before its baseline counterpart. + +3. **Do an analyst pass** — read the benchmark data and surface patterns the aggregate stats might hide. See `agents/analyzer.md` (the "Analyzing Benchmark Results" section) for what to look for — things like assertions that always pass regardless of skill (non-discriminating), high-variance evals (possibly flaky), and time/token tradeoffs. + +4. **Launch the viewer** with both qualitative outputs and quantitative data: + ```bash + nohup python /eval-viewer/generate_review.py \ + /iteration-N \ + --skill-name "my-skill" \ + --benchmark /iteration-N/benchmark.json \ + > /dev/null 2>&1 & + VIEWER_PID=$! + ``` + For iteration 2+, also pass `--previous-workspace /iteration-`. + + **Cowork / headless environments:** If `webbrowser.open()` is not available or the environment has no display, use `--static ` to write a standalone HTML file instead of starting a server. Feedback will be downloaded as a `feedback.json` file when the user clicks "Submit All Reviews". After download, copy `feedback.json` into the workspace directory for the next iteration to pick up. + +Note: please use generate_review.py to create the viewer; there's no need to write custom HTML. + +5. **Tell the user** something like: "I've opened the results in your browser. There are two tabs — 'Outputs' lets you click through each test case and leave feedback, 'Benchmark' shows the quantitative comparison. When you're done, come back here and let me know." + +### What the user sees in the viewer + +The "Outputs" tab shows one test case at a time: +- **Prompt**: the task that was given +- **Output**: the files the skill produced, rendered inline where possible +- **Previous Output** (iteration 2+): collapsed section showing last iteration's output +- **Formal Grades** (if grading was run): collapsed section showing assertion pass/fail +- **Feedback**: a textbox that auto-saves as they type +- **Previous Feedback** (iteration 2+): their comments from last time, shown below the textbox + +The "Benchmark" tab shows the stats summary: pass rates, timing, and token usage for each configuration, with per-eval breakdowns and analyst observations. + +Navigation is via prev/next buttons or arrow keys. When done, they click "Submit All Reviews" which saves all feedback to `feedback.json`. + +### Step 5: Read the feedback + +When the user tells you they're done, read `feedback.json`: + +```json +{ + "reviews": [ + {"run_id": "eval-0-with_skill", "feedback": "the chart is missing axis labels", "timestamp": "..."}, + {"run_id": "eval-1-with_skill", "feedback": "", "timestamp": "..."}, + {"run_id": "eval-2-with_skill", "feedback": "perfect, love this", "timestamp": "..."} + ], + "status": "complete" +} +``` + +Empty feedback means the user thought it was fine. Focus your improvements on the test cases where the user had specific complaints. + +Kill the viewer server when you're done with it: + +```bash +kill $VIEWER_PID 2>/dev/null +``` + +--- + +## Improving the skill + +This is the heart of the loop. You've run the test cases, the user has reviewed the results, and now you need to make the skill better based on their feedback. + +### How to think about improvements + +1. **Generalize from the feedback.** The big picture thing that's happening here is that we're trying to create skills that can be used a million times (maybe literally, maybe even more who knows) across many different prompts. Here you and the user are iterating on only a few examples over and over again because it helps move faster. The user knows these examples in and out and it's quick for them to assess new outputs. But if the skill you and the user are codeveloping works only for those examples, it's useless. Rather than put in fiddly overfitty changes, or oppressively constrictive MUSTs, if there's some stubborn issue, you might try branching out and using different metaphors, or recommending different patterns of working. It's relatively cheap to try and maybe you'll land on something great. + +2. **Keep the prompt lean.** Remove things that aren't pulling their weight. Make sure to read the transcripts, not just the final outputs — if it looks like the skill is making the model waste a bunch of time doing things that are unproductive, you can try getting rid of the parts of the skill that are making it do that and seeing what happens. + +3. **Explain the why.** Try hard to explain the **why** behind everything you're asking the model to do. Today's LLMs are *smart*. They have good theory of mind and when given a good harness can go beyond rote instructions and really make things happen. Even if the feedback from the user is terse or frustrated, try to actually understand the task and why the user is writing what they wrote, and what they actually wrote, and then transmit this understanding into the instructions. If you find yourself writing ALWAYS or NEVER in all caps, or using super rigid structures, that's a yellow flag — if possible, reframe and explain the reasoning so that the model understands why the thing you're asking for is important. That's a more humane, powerful, and effective approach. + +4. **Look for repeated work across test cases.** Read the transcripts from the test runs and notice if the subagents all independently wrote similar helper scripts or took the same multi-step approach to something. If all 3 test cases resulted in the subagent writing a `create_docx.py` or a `build_chart.py`, that's a strong signal the skill should bundle that script. Write it once, put it in `scripts/`, and tell the skill to use it. This saves every future invocation from reinventing the wheel. + +This task is pretty important (we are trying to create billions a year in economic value here!) and your thinking time is not the blocker; take your time and really mull things over. I'd suggest writing a draft revision and then looking at it anew and making improvements. Really do your best to get into the head of the user and understand what they want and need. + +### The iteration loop + +After improving the skill: + +1. Apply your improvements to the skill +2. Rerun all test cases into a new `iteration-/` directory, including baseline runs. If you're creating a new skill, the baseline is always `without_skill` (no skill) — that stays the same across iterations. If you're improving an existing skill, use your judgment on what makes sense as the baseline: the original version the user came in with, or the previous iteration. +3. Launch the reviewer with `--previous-workspace` pointing at the previous iteration +4. Wait for the user to review and tell you they're done +5. Read the new feedback, improve again, repeat + +Keep going until: +- The user says they're happy +- The feedback is all empty (everything looks good) +- You're not making meaningful progress + +--- + +## Advanced: Blind comparison + +For situations where you want a more rigorous comparison between two versions of a skill (e.g., the user asks "is the new version actually better?"), there's a blind comparison system. Read `agents/comparator.md` and `agents/analyzer.md` for the details. The basic idea is: give two outputs to an independent agent without telling it which is which, and let it judge quality. Then analyze why the winner won. + +This is optional, requires subagents, and most users won't need it. The human review loop is usually sufficient. + +--- + +## Description Optimization + +The description field in SKILL.md frontmatter is the primary mechanism that determines whether Claude invokes a skill. After creating or improving a skill, offer to optimize the description for better triggering accuracy. + +### Step 1: Generate trigger eval queries + +Create 20 eval queries — a mix of should-trigger and should-not-trigger. Save as JSON: + +```json +[ + {"query": "the user prompt", "should_trigger": true}, + {"query": "another prompt", "should_trigger": false} +] +``` + +The queries must be realistic and something a Claude Code or Claude.ai user would actually type. Not abstract requests, but requests that are concrete and specific and have a good amount of detail. For instance, file paths, personal context about the user's job or situation, column names and values, company names, URLs. A little bit of backstory. Some might be in lowercase or contain abbreviations or typos or casual speech. Use a mix of different lengths, and focus on edge cases rather than making them clear-cut (the user will get a chance to sign off on them). + +Bad: `"Format this data"`, `"Extract text from PDF"`, `"Create a chart"` + +Good: `"ok so my boss just sent me this xlsx file (its in my downloads, called something like 'Q4 sales final FINAL v2.xlsx') and she wants me to add a column that shows the profit margin as a percentage. The revenue is in column C and costs are in column D i think"` + +For the **should-trigger** queries (8-10), think about coverage. You want different phrasings of the same intent — some formal, some casual. Include cases where the user doesn't explicitly name the skill or file type but clearly needs it. Throw in some uncommon use cases and cases where this skill competes with another but should win. + +For the **should-not-trigger** queries (8-10), the most valuable ones are the near-misses — queries that share keywords or concepts with the skill but actually need something different. Think adjacent domains, ambiguous phrasing where a naive keyword match would trigger but shouldn't, and cases where the query touches on something the skill does but in a context where another tool is more appropriate. + +The key thing to avoid: don't make should-not-trigger queries obviously irrelevant. "Write a fibonacci function" as a negative test for a PDF skill is too easy — it doesn't test anything. The negative cases should be genuinely tricky. + +### Step 2: Review with user + +Present the eval set to the user for review using the HTML template: + +1. Read the template from `assets/eval_review.html` +2. Replace the placeholders: + - `__EVAL_DATA_PLACEHOLDER__` → the JSON array of eval items (no quotes around it — it's a JS variable assignment) + - `__SKILL_NAME_PLACEHOLDER__` → the skill's name + - `__SKILL_DESCRIPTION_PLACEHOLDER__` → the skill's current description +3. Write to a temp file (e.g., `/tmp/eval_review_.html`) and open it: `open /tmp/eval_review_.html` +4. The user can edit queries, toggle should-trigger, add/remove entries, then click "Export Eval Set" +5. The file downloads to `~/Downloads/eval_set.json` — check the Downloads folder for the most recent version in case there are multiple (e.g., `eval_set (1).json`) + +This step matters — bad eval queries lead to bad descriptions. + +### Step 3: Run the optimization loop + +Tell the user: "This will take some time — I'll run the optimization loop in the background and check on it periodically." + +Save the eval set to the workspace, then run in the background: + +```bash +python -m scripts.run_loop \ + --eval-set \ + --skill-path \ + --model \ + --max-iterations 5 \ + --verbose +``` + +Use the model ID from your system prompt (the one powering the current session) so the triggering test matches what the user actually experiences. + +While it runs, periodically tail the output to give the user updates on which iteration it's on and what the scores look like. + +This handles the full optimization loop automatically. It splits the eval set into 60% train and 40% held-out test, evaluates the current description (running each query 3 times to get a reliable trigger rate), then calls Claude with extended thinking to propose improvements based on what failed. It re-evaluates each new description on both train and test, iterating up to 5 times. When it's done, it opens an HTML report in the browser showing the results per iteration and returns JSON with `best_description` — selected by test score rather than train score to avoid overfitting. + +### How skill triggering works + +Understanding the triggering mechanism helps design better eval queries. Skills appear in Claude's `available_skills` list with their name + description, and Claude decides whether to consult a skill based on that description. The important thing to know is that Claude only consults skills for tasks it can't easily handle on its own — simple, one-step queries like "read this PDF" may not trigger a skill even if the description matches perfectly, because Claude can handle them directly with basic tools. Complex, multi-step, or specialized queries reliably trigger skills when the description matches. + +This means your eval queries should be substantive enough that Claude would actually benefit from consulting a skill. Simple queries like "read file X" are poor test cases — they won't trigger skills regardless of description quality. + +### Step 4: Apply the result + +Take `best_description` from the JSON output and update the skill's SKILL.md frontmatter. Show the user before/after and report the scores. + +--- + +### Package and Present (only if `present_files` tool is available) + +Check whether you have access to the `present_files` tool. If you don't, skip this step. If you do, package the skill and present the .skill file to the user: + +```bash +python -m scripts.package_skill +``` + +After packaging, direct the user to the resulting `.skill` file path so they can install it. + +--- + +## Claude.ai-specific instructions + +In Claude.ai, the core workflow is the same (draft → test → review → improve → repeat), but because Claude.ai doesn't have subagents, some mechanics change. Here's what to adapt: + +**Running test cases**: No subagents means no parallel execution. For each test case, read the skill's SKILL.md, then follow its instructions to accomplish the test prompt yourself. Do them one at a time. This is less rigorous than independent subagents (you wrote the skill and you're also running it, so you have full context), but it's a useful sanity check — and the human review step compensates. Skip the baseline runs — just use the skill to complete the task as requested. + +**Reviewing results**: If you can't open a browser (e.g., Claude.ai's VM has no display, or you're on a remote server), skip the browser reviewer entirely. Instead, present results directly in the conversation. For each test case, show the prompt and the output. If the output is a file the user needs to see (like a .docx or .xlsx), save it to the filesystem and tell them where it is so they can download and inspect it. Ask for feedback inline: "How does this look? Anything you'd change?" + +**Benchmarking**: Skip the quantitative benchmarking — it relies on baseline comparisons which aren't meaningful without subagents. Focus on qualitative feedback from the user. + +**The iteration loop**: Same as before — improve the skill, rerun the test cases, ask for feedback — just without the browser reviewer in the middle. You can still organize results into iteration directories on the filesystem if you have one. + +**Description optimization**: This section requires the `claude` CLI tool (specifically `claude -p`) which is only available in Claude Code. Skip it if you're on Claude.ai. + +**Blind comparison**: Requires subagents. Skip it. + +**Packaging**: The `package_skill.py` script works anywhere with Python and a filesystem. On Claude.ai, you can run it and the user can download the resulting `.skill` file. + +--- + +## Cowork-Specific Instructions + +If you're in Cowork, the main things to know are: + +- You have subagents, so the main workflow (spawn test cases in parallel, run baselines, grade, etc.) all works. (However, if you run into severe problems with timeouts, it's OK to run the test prompts in series rather than parallel.) +- You don't have a browser or display, so when generating the eval viewer, use `--static ` to write a standalone HTML file instead of starting a server. Then proffer a link that the user can click to open the HTML in their browser. +- For whatever reason, the Cowork setup seems to disincline Claude from generating the eval viewer after running the tests, so just to reiterate: whether you're in Cowork or in Claude Code, after running tests, you should always generate the eval viewer for the human to look at examples before revising the skill yourself and trying to make corrections, using `generate_review.py` (not writing your own boutique html code). Sorry in advance but I'm gonna go all caps here: GENERATE THE EVAL VIEWER *BEFORE* evaluating inputs yourself. You want to get them in front of the human ASAP! +- Feedback works differently: since there's no running server, the viewer's "Submit All Reviews" button will download `feedback.json` as a file. You can then read it from there (you may have to request access first). +- Packaging works — `package_skill.py` just needs Python and a filesystem. +- Description optimization (`run_loop.py` / `run_eval.py`) should work in Cowork just fine since it uses `claude -p` via subprocess, not a browser, but please save it until you've fully finished making the skill and the user agrees it's in good shape. + +--- + +## Reference files + +The agents/ directory contains instructions for specialized subagents. Read them when you need to spawn the relevant subagent. + +- `agents/grader.md` — How to evaluate assertions against outputs +- `agents/comparator.md` — How to do blind A/B comparison between two outputs +- `agents/analyzer.md` — How to analyze why one version beat another + +The references/ directory has additional documentation: +- `references/schemas.md` — JSON structures for evals.json, grading.json, etc. + +--- + +Repeating one more time the core loop here for emphasis: + +- Figure out what the skill is about +- Draft or edit the skill +- Run claude-with-access-to-the-skill on test prompts +- With the user, evaluate the outputs: + - Create benchmark.json and run `eval-viewer/generate_review.py` to help the user review them + - Run quantitative evals +- Repeat until you and the user are satisfied +- Package the final skill and return it to the user. + +Please add steps to your TodoList, if you have such a thing, to make sure you don't forget. If you're in Cowork, please specifically put "Create evals JSON and run `eval-viewer/generate_review.py` so human can review test cases" in your TodoList to make sure it happens. + +Good luck! diff --git a/.skills/skill-creator/agents/analyzer.md b/.skills/skill-creator/agents/analyzer.md new file mode 100644 index 00000000..14e41d60 --- /dev/null +++ b/.skills/skill-creator/agents/analyzer.md @@ -0,0 +1,274 @@ +# Post-hoc Analyzer Agent + +Analyze blind comparison results to understand WHY the winner won and generate improvement suggestions. + +## Role + +After the blind comparator determines a winner, the Post-hoc Analyzer "unblids" the results by examining the skills and transcripts. The goal is to extract actionable insights: what made the winner better, and how can the loser be improved? + +## Inputs + +You receive these parameters in your prompt: + +- **winner**: "A" or "B" (from blind comparison) +- **winner_skill_path**: Path to the skill that produced the winning output +- **winner_transcript_path**: Path to the execution transcript for the winner +- **loser_skill_path**: Path to the skill that produced the losing output +- **loser_transcript_path**: Path to the execution transcript for the loser +- **comparison_result_path**: Path to the blind comparator's output JSON +- **output_path**: Where to save the analysis results + +## Process + +### Step 1: Read Comparison Result + +1. Read the blind comparator's output at comparison_result_path +2. Note the winning side (A or B), the reasoning, and any scores +3. Understand what the comparator valued in the winning output + +### Step 2: Read Both Skills + +1. Read the winner skill's SKILL.md and key referenced files +2. Read the loser skill's SKILL.md and key referenced files +3. Identify structural differences: + - Instructions clarity and specificity + - Script/tool usage patterns + - Example coverage + - Edge case handling + +### Step 3: Read Both Transcripts + +1. Read the winner's transcript +2. Read the loser's transcript +3. Compare execution patterns: + - How closely did each follow their skill's instructions? + - What tools were used differently? + - Where did the loser diverge from optimal behavior? + - Did either encounter errors or make recovery attempts? + +### Step 4: Analyze Instruction Following + +For each transcript, evaluate: +- Did the agent follow the skill's explicit instructions? +- Did the agent use the skill's provided tools/scripts? +- Were there missed opportunities to leverage skill content? +- Did the agent add unnecessary steps not in the skill? + +Score instruction following 1-10 and note specific issues. + +### Step 5: Identify Winner Strengths + +Determine what made the winner better: +- Clearer instructions that led to better behavior? +- Better scripts/tools that produced better output? +- More comprehensive examples that guided edge cases? +- Better error handling guidance? + +Be specific. Quote from skills/transcripts where relevant. + +### Step 6: Identify Loser Weaknesses + +Determine what held the loser back: +- Ambiguous instructions that led to suboptimal choices? +- Missing tools/scripts that forced workarounds? +- Gaps in edge case coverage? +- Poor error handling that caused failures? + +### Step 7: Generate Improvement Suggestions + +Based on the analysis, produce actionable suggestions for improving the loser skill: +- Specific instruction changes to make +- Tools/scripts to add or modify +- Examples to include +- Edge cases to address + +Prioritize by impact. Focus on changes that would have changed the outcome. + +### Step 8: Write Analysis Results + +Save structured analysis to `{output_path}`. + +## Output Format + +Write a JSON file with this structure: + +```json +{ + "comparison_summary": { + "winner": "A", + "winner_skill": "path/to/winner/skill", + "loser_skill": "path/to/loser/skill", + "comparator_reasoning": "Brief summary of why comparator chose winner" + }, + "winner_strengths": [ + "Clear step-by-step instructions for handling multi-page documents", + "Included validation script that caught formatting errors", + "Explicit guidance on fallback behavior when OCR fails" + ], + "loser_weaknesses": [ + "Vague instruction 'process the document appropriately' led to inconsistent behavior", + "No script for validation, agent had to improvise and made errors", + "No guidance on OCR failure, agent gave up instead of trying alternatives" + ], + "instruction_following": { + "winner": { + "score": 9, + "issues": [ + "Minor: skipped optional logging step" + ] + }, + "loser": { + "score": 6, + "issues": [ + "Did not use the skill's formatting template", + "Invented own approach instead of following step 3", + "Missed the 'always validate output' instruction" + ] + } + }, + "improvement_suggestions": [ + { + "priority": "high", + "category": "instructions", + "suggestion": "Replace 'process the document appropriately' with explicit steps: 1) Extract text, 2) Identify sections, 3) Format per template", + "expected_impact": "Would eliminate ambiguity that caused inconsistent behavior" + }, + { + "priority": "high", + "category": "tools", + "suggestion": "Add validate_output.py script similar to winner skill's validation approach", + "expected_impact": "Would catch formatting errors before final output" + }, + { + "priority": "medium", + "category": "error_handling", + "suggestion": "Add fallback instructions: 'If OCR fails, try: 1) different resolution, 2) image preprocessing, 3) manual extraction'", + "expected_impact": "Would prevent early failure on difficult documents" + } + ], + "transcript_insights": { + "winner_execution_pattern": "Read skill -> Followed 5-step process -> Used validation script -> Fixed 2 issues -> Produced output", + "loser_execution_pattern": "Read skill -> Unclear on approach -> Tried 3 different methods -> No validation -> Output had errors" + } +} +``` + +## Guidelines + +- **Be specific**: Quote from skills and transcripts, don't just say "instructions were unclear" +- **Be actionable**: Suggestions should be concrete changes, not vague advice +- **Focus on skill improvements**: The goal is to improve the losing skill, not critique the agent +- **Prioritize by impact**: Which changes would most likely have changed the outcome? +- **Consider causation**: Did the skill weakness actually cause the worse output, or is it incidental? +- **Stay objective**: Analyze what happened, don't editorialize +- **Think about generalization**: Would this improvement help on other evals too? + +## Categories for Suggestions + +Use these categories to organize improvement suggestions: + +| Category | Description | +|----------|-------------| +| `instructions` | Changes to the skill's prose instructions | +| `tools` | Scripts, templates, or utilities to add/modify | +| `examples` | Example inputs/outputs to include | +| `error_handling` | Guidance for handling failures | +| `structure` | Reorganization of skill content | +| `references` | External docs or resources to add | + +## Priority Levels + +- **high**: Would likely change the outcome of this comparison +- **medium**: Would improve quality but may not change win/loss +- **low**: Nice to have, marginal improvement + +--- + +# Analyzing Benchmark Results + +When analyzing benchmark results, the analyzer's purpose is to **surface patterns and anomalies** across multiple runs, not suggest skill improvements. + +## Role + +Review all benchmark run results and generate freeform notes that help the user understand skill performance. Focus on patterns that wouldn't be visible from aggregate metrics alone. + +## Inputs + +You receive these parameters in your prompt: + +- **benchmark_data_path**: Path to the in-progress benchmark.json with all run results +- **skill_path**: Path to the skill being benchmarked +- **output_path**: Where to save the notes (as JSON array of strings) + +## Process + +### Step 1: Read Benchmark Data + +1. Read the benchmark.json containing all run results +2. Note the configurations tested (with_skill, without_skill) +3. Understand the run_summary aggregates already calculated + +### Step 2: Analyze Per-Assertion Patterns + +For each expectation across all runs: +- Does it **always pass** in both configurations? (may not differentiate skill value) +- Does it **always fail** in both configurations? (may be broken or beyond capability) +- Does it **always pass with skill but fail without**? (skill clearly adds value here) +- Does it **always fail with skill but pass without**? (skill may be hurting) +- Is it **highly variable**? (flaky expectation or non-deterministic behavior) + +### Step 3: Analyze Cross-Eval Patterns + +Look for patterns across evals: +- Are certain eval types consistently harder/easier? +- Do some evals show high variance while others are stable? +- Are there surprising results that contradict expectations? + +### Step 4: Analyze Metrics Patterns + +Look at time_seconds, tokens, tool_calls: +- Does the skill significantly increase execution time? +- Is there high variance in resource usage? +- Are there outlier runs that skew the aggregates? + +### Step 5: Generate Notes + +Write freeform observations as a list of strings. Each note should: +- State a specific observation +- Be grounded in the data (not speculation) +- Help the user understand something the aggregate metrics don't show + +Examples: +- "Assertion 'Output is a PDF file' passes 100% in both configurations - may not differentiate skill value" +- "Eval 3 shows high variance (50% ± 40%) - run 2 had an unusual failure that may be flaky" +- "Without-skill runs consistently fail on table extraction expectations (0% pass rate)" +- "Skill adds 13s average execution time but improves pass rate by 50%" +- "Token usage is 80% higher with skill, primarily due to script output parsing" +- "All 3 without-skill runs for eval 1 produced empty output" + +### Step 6: Write Notes + +Save notes to `{output_path}` as a JSON array of strings: + +```json +[ + "Assertion 'Output is a PDF file' passes 100% in both configurations - may not differentiate skill value", + "Eval 3 shows high variance (50% ± 40%) - run 2 had an unusual failure", + "Without-skill runs consistently fail on table extraction expectations", + "Skill adds 13s average execution time but improves pass rate by 50%" +] +``` + +## Guidelines + +**DO:** +- Report what you observe in the data +- Be specific about which evals, expectations, or runs you're referring to +- Note patterns that aggregate metrics would hide +- Provide context that helps interpret the numbers + +**DO NOT:** +- Suggest improvements to the skill (that's for the improvement step, not benchmarking) +- Make subjective quality judgments ("the output was good/bad") +- Speculate about causes without evidence +- Repeat information already in the run_summary aggregates diff --git a/.skills/skill-creator/agents/comparator.md b/.skills/skill-creator/agents/comparator.md new file mode 100644 index 00000000..80e00eb4 --- /dev/null +++ b/.skills/skill-creator/agents/comparator.md @@ -0,0 +1,202 @@ +# Blind Comparator Agent + +Compare two outputs WITHOUT knowing which skill produced them. + +## Role + +The Blind Comparator judges which output better accomplishes the eval task. You receive two outputs labeled A and B, but you do NOT know which skill produced which. This prevents bias toward a particular skill or approach. + +Your judgment is based purely on output quality and task completion. + +## Inputs + +You receive these parameters in your prompt: + +- **output_a_path**: Path to the first output file or directory +- **output_b_path**: Path to the second output file or directory +- **eval_prompt**: The original task/prompt that was executed +- **expectations**: List of expectations to check (optional - may be empty) + +## Process + +### Step 1: Read Both Outputs + +1. Examine output A (file or directory) +2. Examine output B (file or directory) +3. Note the type, structure, and content of each +4. If outputs are directories, examine all relevant files inside + +### Step 2: Understand the Task + +1. Read the eval_prompt carefully +2. Identify what the task requires: + - What should be produced? + - What qualities matter (accuracy, completeness, format)? + - What would distinguish a good output from a poor one? + +### Step 3: Generate Evaluation Rubric + +Based on the task, generate a rubric with two dimensions: + +**Content Rubric** (what the output contains): +| Criterion | 1 (Poor) | 3 (Acceptable) | 5 (Excellent) | +|-----------|----------|----------------|---------------| +| Correctness | Major errors | Minor errors | Fully correct | +| Completeness | Missing key elements | Mostly complete | All elements present | +| Accuracy | Significant inaccuracies | Minor inaccuracies | Accurate throughout | + +**Structure Rubric** (how the output is organized): +| Criterion | 1 (Poor) | 3 (Acceptable) | 5 (Excellent) | +|-----------|----------|----------------|---------------| +| Organization | Disorganized | Reasonably organized | Clear, logical structure | +| Formatting | Inconsistent/broken | Mostly consistent | Professional, polished | +| Usability | Difficult to use | Usable with effort | Easy to use | + +Adapt criteria to the specific task. For example: +- PDF form → "Field alignment", "Text readability", "Data placement" +- Document → "Section structure", "Heading hierarchy", "Paragraph flow" +- Data output → "Schema correctness", "Data types", "Completeness" + +### Step 4: Evaluate Each Output Against the Rubric + +For each output (A and B): + +1. **Score each criterion** on the rubric (1-5 scale) +2. **Calculate dimension totals**: Content score, Structure score +3. **Calculate overall score**: Average of dimension scores, scaled to 1-10 + +### Step 5: Check Assertions (if provided) + +If expectations are provided: + +1. Check each expectation against output A +2. Check each expectation against output B +3. Count pass rates for each output +4. Use expectation scores as secondary evidence (not the primary decision factor) + +### Step 6: Determine the Winner + +Compare A and B based on (in priority order): + +1. **Primary**: Overall rubric score (content + structure) +2. **Secondary**: Assertion pass rates (if applicable) +3. **Tiebreaker**: If truly equal, declare a TIE + +Be decisive - ties should be rare. One output is usually better, even if marginally. + +### Step 7: Write Comparison Results + +Save results to a JSON file at the path specified (or `comparison.json` if not specified). + +## Output Format + +Write a JSON file with this structure: + +```json +{ + "winner": "A", + "reasoning": "Output A provides a complete solution with proper formatting and all required fields. Output B is missing the date field and has formatting inconsistencies.", + "rubric": { + "A": { + "content": { + "correctness": 5, + "completeness": 5, + "accuracy": 4 + }, + "structure": { + "organization": 4, + "formatting": 5, + "usability": 4 + }, + "content_score": 4.7, + "structure_score": 4.3, + "overall_score": 9.0 + }, + "B": { + "content": { + "correctness": 3, + "completeness": 2, + "accuracy": 3 + }, + "structure": { + "organization": 3, + "formatting": 2, + "usability": 3 + }, + "content_score": 2.7, + "structure_score": 2.7, + "overall_score": 5.4 + } + }, + "output_quality": { + "A": { + "score": 9, + "strengths": ["Complete solution", "Well-formatted", "All fields present"], + "weaknesses": ["Minor style inconsistency in header"] + }, + "B": { + "score": 5, + "strengths": ["Readable output", "Correct basic structure"], + "weaknesses": ["Missing date field", "Formatting inconsistencies", "Partial data extraction"] + } + }, + "expectation_results": { + "A": { + "passed": 4, + "total": 5, + "pass_rate": 0.80, + "details": [ + {"text": "Output includes name", "passed": true}, + {"text": "Output includes date", "passed": true}, + {"text": "Format is PDF", "passed": true}, + {"text": "Contains signature", "passed": false}, + {"text": "Readable text", "passed": true} + ] + }, + "B": { + "passed": 3, + "total": 5, + "pass_rate": 0.60, + "details": [ + {"text": "Output includes name", "passed": true}, + {"text": "Output includes date", "passed": false}, + {"text": "Format is PDF", "passed": true}, + {"text": "Contains signature", "passed": false}, + {"text": "Readable text", "passed": true} + ] + } + } +} +``` + +If no expectations were provided, omit the `expectation_results` field entirely. + +## Field Descriptions + +- **winner**: "A", "B", or "TIE" +- **reasoning**: Clear explanation of why the winner was chosen (or why it's a tie) +- **rubric**: Structured rubric evaluation for each output + - **content**: Scores for content criteria (correctness, completeness, accuracy) + - **structure**: Scores for structure criteria (organization, formatting, usability) + - **content_score**: Average of content criteria (1-5) + - **structure_score**: Average of structure criteria (1-5) + - **overall_score**: Combined score scaled to 1-10 +- **output_quality**: Summary quality assessment + - **score**: 1-10 rating (should match rubric overall_score) + - **strengths**: List of positive aspects + - **weaknesses**: List of issues or shortcomings +- **expectation_results**: (Only if expectations provided) + - **passed**: Number of expectations that passed + - **total**: Total number of expectations + - **pass_rate**: Fraction passed (0.0 to 1.0) + - **details**: Individual expectation results + +## Guidelines + +- **Stay blind**: DO NOT try to infer which skill produced which output. Judge purely on output quality. +- **Be specific**: Cite specific examples when explaining strengths and weaknesses. +- **Be decisive**: Choose a winner unless outputs are genuinely equivalent. +- **Output quality first**: Assertion scores are secondary to overall task completion. +- **Be objective**: Don't favor outputs based on style preferences; focus on correctness and completeness. +- **Explain your reasoning**: The reasoning field should make it clear why you chose the winner. +- **Handle edge cases**: If both outputs fail, pick the one that fails less badly. If both are excellent, pick the one that's marginally better. diff --git a/.skills/skill-creator/agents/grader.md b/.skills/skill-creator/agents/grader.md new file mode 100644 index 00000000..558ab05c --- /dev/null +++ b/.skills/skill-creator/agents/grader.md @@ -0,0 +1,223 @@ +# Grader Agent + +Evaluate expectations against an execution transcript and outputs. + +## Role + +The Grader reviews a transcript and output files, then determines whether each expectation passes or fails. Provide clear evidence for each judgment. + +You have two jobs: grade the outputs, and critique the evals themselves. A passing grade on a weak assertion is worse than useless — it creates false confidence. When you notice an assertion that's trivially satisfied, or an important outcome that no assertion checks, say so. + +## Inputs + +You receive these parameters in your prompt: + +- **expectations**: List of expectations to evaluate (strings) +- **transcript_path**: Path to the execution transcript (markdown file) +- **outputs_dir**: Directory containing output files from execution + +## Process + +### Step 1: Read the Transcript + +1. Read the transcript file completely +2. Note the eval prompt, execution steps, and final result +3. Identify any issues or errors documented + +### Step 2: Examine Output Files + +1. List files in outputs_dir +2. Read/examine each file relevant to the expectations. If outputs aren't plain text, use the inspection tools provided in your prompt — don't rely solely on what the transcript says the executor produced. +3. Note contents, structure, and quality + +### Step 3: Evaluate Each Assertion + +For each expectation: + +1. **Search for evidence** in the transcript and outputs +2. **Determine verdict**: + - **PASS**: Clear evidence the expectation is true AND the evidence reflects genuine task completion, not just surface-level compliance + - **FAIL**: No evidence, or evidence contradicts the expectation, or the evidence is superficial (e.g., correct filename but empty/wrong content) +3. **Cite the evidence**: Quote the specific text or describe what you found + +### Step 4: Extract and Verify Claims + +Beyond the predefined expectations, extract implicit claims from the outputs and verify them: + +1. **Extract claims** from the transcript and outputs: + - Factual statements ("The form has 12 fields") + - Process claims ("Used pypdf to fill the form") + - Quality claims ("All fields were filled correctly") + +2. **Verify each claim**: + - **Factual claims**: Can be checked against the outputs or external sources + - **Process claims**: Can be verified from the transcript + - **Quality claims**: Evaluate whether the claim is justified + +3. **Flag unverifiable claims**: Note claims that cannot be verified with available information + +This catches issues that predefined expectations might miss. + +### Step 5: Read User Notes + +If `{outputs_dir}/user_notes.md` exists: +1. Read it and note any uncertainties or issues flagged by the executor +2. Include relevant concerns in the grading output +3. These may reveal problems even when expectations pass + +### Step 6: Critique the Evals + +After grading, consider whether the evals themselves could be improved. Only surface suggestions when there's a clear gap. + +Good suggestions test meaningful outcomes — assertions that are hard to satisfy without actually doing the work correctly. Think about what makes an assertion *discriminating*: it passes when the skill genuinely succeeds and fails when it doesn't. + +Suggestions worth raising: +- An assertion that passed but would also pass for a clearly wrong output (e.g., checking filename existence but not file content) +- An important outcome you observed — good or bad — that no assertion covers at all +- An assertion that can't actually be verified from the available outputs + +Keep the bar high. The goal is to flag things the eval author would say "good catch" about, not to nitpick every assertion. + +### Step 7: Write Grading Results + +Save results to `{outputs_dir}/../grading.json` (sibling to outputs_dir). + +## Grading Criteria + +**PASS when**: +- The transcript or outputs clearly demonstrate the expectation is true +- Specific evidence can be cited +- The evidence reflects genuine substance, not just surface compliance (e.g., a file exists AND contains correct content, not just the right filename) + +**FAIL when**: +- No evidence found for the expectation +- Evidence contradicts the expectation +- The expectation cannot be verified from available information +- The evidence is superficial — the assertion is technically satisfied but the underlying task outcome is wrong or incomplete +- The output appears to meet the assertion by coincidence rather than by actually doing the work + +**When uncertain**: The burden of proof to pass is on the expectation. + +### Step 8: Read Executor Metrics and Timing + +1. If `{outputs_dir}/metrics.json` exists, read it and include in grading output +2. If `{outputs_dir}/../timing.json` exists, read it and include timing data + +## Output Format + +Write a JSON file with this structure: + +```json +{ + "expectations": [ + { + "text": "The output includes the name 'John Smith'", + "passed": true, + "evidence": "Found in transcript Step 3: 'Extracted names: John Smith, Sarah Johnson'" + }, + { + "text": "The spreadsheet has a SUM formula in cell B10", + "passed": false, + "evidence": "No spreadsheet was created. The output was a text file." + }, + { + "text": "The assistant used the skill's OCR script", + "passed": true, + "evidence": "Transcript Step 2 shows: 'Tool: Bash - python ocr_script.py image.png'" + } + ], + "summary": { + "passed": 2, + "failed": 1, + "total": 3, + "pass_rate": 0.67 + }, + "execution_metrics": { + "tool_calls": { + "Read": 5, + "Write": 2, + "Bash": 8 + }, + "total_tool_calls": 15, + "total_steps": 6, + "errors_encountered": 0, + "output_chars": 12450, + "transcript_chars": 3200 + }, + "timing": { + "executor_duration_seconds": 165.0, + "grader_duration_seconds": 26.0, + "total_duration_seconds": 191.0 + }, + "claims": [ + { + "claim": "The form has 12 fillable fields", + "type": "factual", + "verified": true, + "evidence": "Counted 12 fields in field_info.json" + }, + { + "claim": "All required fields were populated", + "type": "quality", + "verified": false, + "evidence": "Reference section was left blank despite data being available" + } + ], + "user_notes_summary": { + "uncertainties": ["Used 2023 data, may be stale"], + "needs_review": [], + "workarounds": ["Fell back to text overlay for non-fillable fields"] + }, + "eval_feedback": { + "suggestions": [ + { + "assertion": "The output includes the name 'John Smith'", + "reason": "A hallucinated document that mentions the name would also pass — consider checking it appears as the primary contact with matching phone and email from the input" + }, + { + "reason": "No assertion checks whether the extracted phone numbers match the input — I observed incorrect numbers in the output that went uncaught" + } + ], + "overall": "Assertions check presence but not correctness. Consider adding content verification." + } +} +``` + +## Field Descriptions + +- **expectations**: Array of graded expectations + - **text**: The original expectation text + - **passed**: Boolean - true if expectation passes + - **evidence**: Specific quote or description supporting the verdict +- **summary**: Aggregate statistics + - **passed**: Count of passed expectations + - **failed**: Count of failed expectations + - **total**: Total expectations evaluated + - **pass_rate**: Fraction passed (0.0 to 1.0) +- **execution_metrics**: Copied from executor's metrics.json (if available) + - **output_chars**: Total character count of output files (proxy for tokens) + - **transcript_chars**: Character count of transcript +- **timing**: Wall clock timing from timing.json (if available) + - **executor_duration_seconds**: Time spent in executor subagent + - **total_duration_seconds**: Total elapsed time for the run +- **claims**: Extracted and verified claims from the output + - **claim**: The statement being verified + - **type**: "factual", "process", or "quality" + - **verified**: Boolean - whether the claim holds + - **evidence**: Supporting or contradicting evidence +- **user_notes_summary**: Issues flagged by the executor + - **uncertainties**: Things the executor wasn't sure about + - **needs_review**: Items requiring human attention + - **workarounds**: Places where the skill didn't work as expected +- **eval_feedback**: Improvement suggestions for the evals (only when warranted) + - **suggestions**: List of concrete suggestions, each with a `reason` and optionally an `assertion` it relates to + - **overall**: Brief assessment — can be "No suggestions, evals look solid" if nothing to flag + +## Guidelines + +- **Be objective**: Base verdicts on evidence, not assumptions +- **Be specific**: Quote the exact text that supports your verdict +- **Be thorough**: Check both transcript and output files +- **Be consistent**: Apply the same standard to each expectation +- **Explain failures**: Make it clear why evidence was insufficient +- **No partial credit**: Each expectation is pass or fail, not partial diff --git a/.skills/skill-creator/assets/eval_review.html b/.skills/skill-creator/assets/eval_review.html new file mode 100644 index 00000000..938ff32a --- /dev/null +++ b/.skills/skill-creator/assets/eval_review.html @@ -0,0 +1,146 @@ + + + + + + Eval Set Review - __SKILL_NAME_PLACEHOLDER__ + + + + + + +

Eval Set Review: __SKILL_NAME_PLACEHOLDER__

+

Current description: __SKILL_DESCRIPTION_PLACEHOLDER__

+ +
+ + +
+ + + + + + + + + + +
QueryShould TriggerActions
+ +

+ + + + diff --git a/.skills/skill-creator/eval-viewer/generate_review.py b/.skills/skill-creator/eval-viewer/generate_review.py new file mode 100644 index 00000000..7fa59786 --- /dev/null +++ b/.skills/skill-creator/eval-viewer/generate_review.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python3 +"""Generate and serve a review page for eval results. + +Reads the workspace directory, discovers runs (directories with outputs/), +embeds all output data into a self-contained HTML page, and serves it via +a tiny HTTP server. Feedback auto-saves to feedback.json in the workspace. + +Usage: + python generate_review.py [--port PORT] [--skill-name NAME] + python generate_review.py --previous-feedback /path/to/old/feedback.json + +No dependencies beyond the Python stdlib are required. +""" + +import argparse +import base64 +import json +import mimetypes +import os +import re +import signal +import subprocess +import sys +import time +import webbrowser +from functools import partial +from http.server import HTTPServer, BaseHTTPRequestHandler +from pathlib import Path + +# Files to exclude from output listings +METADATA_FILES = {"transcript.md", "user_notes.md", "metrics.json"} + +# Extensions we render as inline text +TEXT_EXTENSIONS = { + ".txt", ".md", ".json", ".csv", ".py", ".js", ".ts", ".tsx", ".jsx", + ".yaml", ".yml", ".xml", ".html", ".css", ".sh", ".rb", ".go", ".rs", + ".java", ".c", ".cpp", ".h", ".hpp", ".sql", ".r", ".toml", +} + +# Extensions we render as inline images +IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"} + +# MIME type overrides for common types +MIME_OVERRIDES = { + ".svg": "image/svg+xml", + ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", +} + + +def get_mime_type(path: Path) -> str: + ext = path.suffix.lower() + if ext in MIME_OVERRIDES: + return MIME_OVERRIDES[ext] + mime, _ = mimetypes.guess_type(str(path)) + return mime or "application/octet-stream" + + +def find_runs(workspace: Path) -> list[dict]: + """Recursively find directories that contain an outputs/ subdirectory.""" + runs: list[dict] = [] + _find_runs_recursive(workspace, workspace, runs) + runs.sort(key=lambda r: (r.get("eval_id", float("inf")), r["id"])) + return runs + + +def _find_runs_recursive(root: Path, current: Path, runs: list[dict]) -> None: + if not current.is_dir(): + return + + outputs_dir = current / "outputs" + if outputs_dir.is_dir(): + run = build_run(root, current) + if run: + runs.append(run) + return + + skip = {"node_modules", ".git", "__pycache__", "skill", "inputs"} + for child in sorted(current.iterdir()): + if child.is_dir() and child.name not in skip: + _find_runs_recursive(root, child, runs) + + +def build_run(root: Path, run_dir: Path) -> dict | None: + """Build a run dict with prompt, outputs, and grading data.""" + prompt = "" + eval_id = None + + # Try eval_metadata.json + for candidate in [run_dir / "eval_metadata.json", run_dir.parent / "eval_metadata.json"]: + if candidate.exists(): + try: + metadata = json.loads(candidate.read_text()) + prompt = metadata.get("prompt", "") + eval_id = metadata.get("eval_id") + except (json.JSONDecodeError, OSError): + pass + if prompt: + break + + # Fall back to transcript.md + if not prompt: + for candidate in [run_dir / "transcript.md", run_dir / "outputs" / "transcript.md"]: + if candidate.exists(): + try: + text = candidate.read_text() + match = re.search(r"## Eval Prompt\n\n([\s\S]*?)(?=\n##|$)", text) + if match: + prompt = match.group(1).strip() + except OSError: + pass + if prompt: + break + + if not prompt: + prompt = "(No prompt found)" + + run_id = str(run_dir.relative_to(root)).replace("/", "-").replace("\\", "-") + + # Collect output files + outputs_dir = run_dir / "outputs" + output_files: list[dict] = [] + if outputs_dir.is_dir(): + for f in sorted(outputs_dir.iterdir()): + if f.is_file() and f.name not in METADATA_FILES: + output_files.append(embed_file(f)) + + # Load grading if present + grading = None + for candidate in [run_dir / "grading.json", run_dir.parent / "grading.json"]: + if candidate.exists(): + try: + grading = json.loads(candidate.read_text()) + except (json.JSONDecodeError, OSError): + pass + if grading: + break + + return { + "id": run_id, + "prompt": prompt, + "eval_id": eval_id, + "outputs": output_files, + "grading": grading, + } + + +def embed_file(path: Path) -> dict: + """Read a file and return an embedded representation.""" + ext = path.suffix.lower() + mime = get_mime_type(path) + + if ext in TEXT_EXTENSIONS: + try: + content = path.read_text(errors="replace") + except OSError: + content = "(Error reading file)" + return { + "name": path.name, + "type": "text", + "content": content, + } + elif ext in IMAGE_EXTENSIONS: + try: + raw = path.read_bytes() + b64 = base64.b64encode(raw).decode("ascii") + except OSError: + return {"name": path.name, "type": "error", "content": "(Error reading file)"} + return { + "name": path.name, + "type": "image", + "mime": mime, + "data_uri": f"data:{mime};base64,{b64}", + } + elif ext == ".pdf": + try: + raw = path.read_bytes() + b64 = base64.b64encode(raw).decode("ascii") + except OSError: + return {"name": path.name, "type": "error", "content": "(Error reading file)"} + return { + "name": path.name, + "type": "pdf", + "data_uri": f"data:{mime};base64,{b64}", + } + elif ext == ".xlsx": + try: + raw = path.read_bytes() + b64 = base64.b64encode(raw).decode("ascii") + except OSError: + return {"name": path.name, "type": "error", "content": "(Error reading file)"} + return { + "name": path.name, + "type": "xlsx", + "data_b64": b64, + } + else: + # Binary / unknown — base64 download link + try: + raw = path.read_bytes() + b64 = base64.b64encode(raw).decode("ascii") + except OSError: + return {"name": path.name, "type": "error", "content": "(Error reading file)"} + return { + "name": path.name, + "type": "binary", + "mime": mime, + "data_uri": f"data:{mime};base64,{b64}", + } + + +def load_previous_iteration(workspace: Path) -> dict[str, dict]: + """Load previous iteration's feedback and outputs. + + Returns a map of run_id -> {"feedback": str, "outputs": list[dict]}. + """ + result: dict[str, dict] = {} + + # Load feedback + feedback_map: dict[str, str] = {} + feedback_path = workspace / "feedback.json" + if feedback_path.exists(): + try: + data = json.loads(feedback_path.read_text()) + feedback_map = { + r["run_id"]: r["feedback"] + for r in data.get("reviews", []) + if r.get("feedback", "").strip() + } + except (json.JSONDecodeError, OSError, KeyError): + pass + + # Load runs (to get outputs) + prev_runs = find_runs(workspace) + for run in prev_runs: + result[run["id"]] = { + "feedback": feedback_map.get(run["id"], ""), + "outputs": run.get("outputs", []), + } + + # Also add feedback for run_ids that had feedback but no matching run + for run_id, fb in feedback_map.items(): + if run_id not in result: + result[run_id] = {"feedback": fb, "outputs": []} + + return result + + +def generate_html( + runs: list[dict], + skill_name: str, + previous: dict[str, dict] | None = None, + benchmark: dict | None = None, +) -> str: + """Generate the complete standalone HTML page with embedded data.""" + template_path = Path(__file__).parent / "viewer.html" + template = template_path.read_text() + + # Build previous_feedback and previous_outputs maps for the template + previous_feedback: dict[str, str] = {} + previous_outputs: dict[str, list[dict]] = {} + if previous: + for run_id, data in previous.items(): + if data.get("feedback"): + previous_feedback[run_id] = data["feedback"] + if data.get("outputs"): + previous_outputs[run_id] = data["outputs"] + + embedded = { + "skill_name": skill_name, + "runs": runs, + "previous_feedback": previous_feedback, + "previous_outputs": previous_outputs, + } + if benchmark: + embedded["benchmark"] = benchmark + + data_json = json.dumps(embedded) + + return template.replace("/*__EMBEDDED_DATA__*/", f"const EMBEDDED_DATA = {data_json};") + + +# --------------------------------------------------------------------------- +# HTTP server (stdlib only, zero dependencies) +# --------------------------------------------------------------------------- + +def _kill_port(port: int) -> None: + """Kill any process listening on the given port.""" + try: + result = subprocess.run( + ["lsof", "-ti", f":{port}"], + capture_output=True, text=True, timeout=5, + ) + for pid_str in result.stdout.strip().split("\n"): + if pid_str.strip(): + try: + os.kill(int(pid_str.strip()), signal.SIGTERM) + except (ProcessLookupError, ValueError): + pass + if result.stdout.strip(): + time.sleep(0.5) + except subprocess.TimeoutExpired: + pass + except FileNotFoundError: + print("Note: lsof not found, cannot check if port is in use", file=sys.stderr) + +class ReviewHandler(BaseHTTPRequestHandler): + """Serves the review HTML and handles feedback saves. + + Regenerates the HTML on each page load so that refreshing the browser + picks up new eval outputs without restarting the server. + """ + + def __init__( + self, + workspace: Path, + skill_name: str, + feedback_path: Path, + previous: dict[str, dict], + benchmark_path: Path | None, + *args, + **kwargs, + ): + self.workspace = workspace + self.skill_name = skill_name + self.feedback_path = feedback_path + self.previous = previous + self.benchmark_path = benchmark_path + super().__init__(*args, **kwargs) + + def do_GET(self) -> None: + if self.path == "/" or self.path == "/index.html": + # Regenerate HTML on each request (re-scans workspace for new outputs) + runs = find_runs(self.workspace) + benchmark = None + if self.benchmark_path and self.benchmark_path.exists(): + try: + benchmark = json.loads(self.benchmark_path.read_text()) + except (json.JSONDecodeError, OSError): + pass + html = generate_html(runs, self.skill_name, self.previous, benchmark) + content = html.encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(content))) + self.end_headers() + self.wfile.write(content) + elif self.path == "/api/feedback": + data = b"{}" + if self.feedback_path.exists(): + data = self.feedback_path.read_bytes() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + else: + self.send_error(404) + + def do_POST(self) -> None: + if self.path == "/api/feedback": + length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(length) + try: + data = json.loads(body) + if not isinstance(data, dict) or "reviews" not in data: + raise ValueError("Expected JSON object with 'reviews' key") + self.feedback_path.write_text(json.dumps(data, indent=2) + "\n") + resp = b'{"ok":true}' + self.send_response(200) + except (json.JSONDecodeError, OSError, ValueError) as e: + resp = json.dumps({"error": str(e)}).encode() + self.send_response(500) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(resp))) + self.end_headers() + self.wfile.write(resp) + else: + self.send_error(404) + + def log_message(self, format: str, *args: object) -> None: + # Suppress request logging to keep terminal clean + pass + + +def main() -> None: + parser = argparse.ArgumentParser(description="Generate and serve eval review") + parser.add_argument("workspace", type=Path, help="Path to workspace directory") + parser.add_argument("--port", "-p", type=int, default=3117, help="Server port (default: 3117)") + parser.add_argument("--skill-name", "-n", type=str, default=None, help="Skill name for header") + parser.add_argument( + "--previous-workspace", type=Path, default=None, + help="Path to previous iteration's workspace (shows old outputs and feedback as context)", + ) + parser.add_argument( + "--benchmark", type=Path, default=None, + help="Path to benchmark.json to show in the Benchmark tab", + ) + parser.add_argument( + "--static", "-s", type=Path, default=None, + help="Write standalone HTML to this path instead of starting a server", + ) + args = parser.parse_args() + + workspace = args.workspace.resolve() + if not workspace.is_dir(): + print(f"Error: {workspace} is not a directory", file=sys.stderr) + sys.exit(1) + + runs = find_runs(workspace) + if not runs: + print(f"No runs found in {workspace}", file=sys.stderr) + sys.exit(1) + + skill_name = args.skill_name or workspace.name.replace("-workspace", "") + feedback_path = workspace / "feedback.json" + + previous: dict[str, dict] = {} + if args.previous_workspace: + previous = load_previous_iteration(args.previous_workspace.resolve()) + + benchmark_path = args.benchmark.resolve() if args.benchmark else None + benchmark = None + if benchmark_path and benchmark_path.exists(): + try: + benchmark = json.loads(benchmark_path.read_text()) + except (json.JSONDecodeError, OSError): + pass + + if args.static: + html = generate_html(runs, skill_name, previous, benchmark) + args.static.parent.mkdir(parents=True, exist_ok=True) + args.static.write_text(html) + print(f"\n Static viewer written to: {args.static}\n") + sys.exit(0) + + # Kill any existing process on the target port + port = args.port + _kill_port(port) + handler = partial(ReviewHandler, workspace, skill_name, feedback_path, previous, benchmark_path) + try: + server = HTTPServer(("127.0.0.1", port), handler) + except OSError: + # Port still in use after kill attempt — find a free one + server = HTTPServer(("127.0.0.1", 0), handler) + port = server.server_address[1] + + url = f"http://localhost:{port}" + print(f"\n Eval Viewer") + print(f" ─────────────────────────────────") + print(f" URL: {url}") + print(f" Workspace: {workspace}") + print(f" Feedback: {feedback_path}") + if previous: + print(f" Previous: {args.previous_workspace} ({len(previous)} runs)") + if benchmark_path: + print(f" Benchmark: {benchmark_path}") + print(f"\n Press Ctrl+C to stop.\n") + + webbrowser.open(url) + + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nStopped.") + server.server_close() + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/eval-viewer/viewer.html b/.skills/skill-creator/eval-viewer/viewer.html new file mode 100644 index 00000000..6d8e9634 --- /dev/null +++ b/.skills/skill-creator/eval-viewer/viewer.html @@ -0,0 +1,1325 @@ + + + + + + Eval Review + + + + + + + +
+
+
+

Eval Review:

+
Review each output and leave feedback below. Navigate with arrow keys or buttons. When done, copy feedback and paste into Claude Code.
+
+
+
+ + + + + +
+
+ +
+
Prompt
+
+
+
+
+ + +
+
Output
+
+
No output files found
+
+
+ + + + + + + + +
+
Your Feedback
+
+ + + +
+
+
+ + +
+ + +
+
+
No benchmark data available. Run a benchmark to see quantitative results here.
+
+
+
+ + +
+
+

Review Complete

+

Your feedback has been saved. Go back to your Claude Code session and tell Claude you're done reviewing.

+
+ +
+
+
+ + +
+ + + + diff --git a/.skills/skill-creator/references/schemas.md b/.skills/skill-creator/references/schemas.md new file mode 100644 index 00000000..b6eeaa2d --- /dev/null +++ b/.skills/skill-creator/references/schemas.md @@ -0,0 +1,430 @@ +# JSON Schemas + +This document defines the JSON schemas used by skill-creator. + +--- + +## evals.json + +Defines the evals for a skill. Located at `evals/evals.json` within the skill directory. + +```json +{ + "skill_name": "example-skill", + "evals": [ + { + "id": 1, + "prompt": "User's example prompt", + "expected_output": "Description of expected result", + "files": ["evals/files/sample1.pdf"], + "expectations": [ + "The output includes X", + "The skill used script Y" + ] + } + ] +} +``` + +**Fields:** +- `skill_name`: Name matching the skill's frontmatter +- `evals[].id`: Unique integer identifier +- `evals[].prompt`: The task to execute +- `evals[].expected_output`: Human-readable description of success +- `evals[].files`: Optional list of input file paths (relative to skill root) +- `evals[].expectations`: List of verifiable statements + +--- + +## history.json + +Tracks version progression in Improve mode. Located at workspace root. + +```json +{ + "started_at": "2026-01-15T10:30:00Z", + "skill_name": "pdf", + "current_best": "v2", + "iterations": [ + { + "version": "v0", + "parent": null, + "expectation_pass_rate": 0.65, + "grading_result": "baseline", + "is_current_best": false + }, + { + "version": "v1", + "parent": "v0", + "expectation_pass_rate": 0.75, + "grading_result": "won", + "is_current_best": false + }, + { + "version": "v2", + "parent": "v1", + "expectation_pass_rate": 0.85, + "grading_result": "won", + "is_current_best": true + } + ] +} +``` + +**Fields:** +- `started_at`: ISO timestamp of when improvement started +- `skill_name`: Name of the skill being improved +- `current_best`: Version identifier of the best performer +- `iterations[].version`: Version identifier (v0, v1, ...) +- `iterations[].parent`: Parent version this was derived from +- `iterations[].expectation_pass_rate`: Pass rate from grading +- `iterations[].grading_result`: "baseline", "won", "lost", or "tie" +- `iterations[].is_current_best`: Whether this is the current best version + +--- + +## grading.json + +Output from the grader agent. Located at `/grading.json`. + +```json +{ + "expectations": [ + { + "text": "The output includes the name 'John Smith'", + "passed": true, + "evidence": "Found in transcript Step 3: 'Extracted names: John Smith, Sarah Johnson'" + }, + { + "text": "The spreadsheet has a SUM formula in cell B10", + "passed": false, + "evidence": "No spreadsheet was created. The output was a text file." + } + ], + "summary": { + "passed": 2, + "failed": 1, + "total": 3, + "pass_rate": 0.67 + }, + "execution_metrics": { + "tool_calls": { + "Read": 5, + "Write": 2, + "Bash": 8 + }, + "total_tool_calls": 15, + "total_steps": 6, + "errors_encountered": 0, + "output_chars": 12450, + "transcript_chars": 3200 + }, + "timing": { + "executor_duration_seconds": 165.0, + "grader_duration_seconds": 26.0, + "total_duration_seconds": 191.0 + }, + "claims": [ + { + "claim": "The form has 12 fillable fields", + "type": "factual", + "verified": true, + "evidence": "Counted 12 fields in field_info.json" + } + ], + "user_notes_summary": { + "uncertainties": ["Used 2023 data, may be stale"], + "needs_review": [], + "workarounds": ["Fell back to text overlay for non-fillable fields"] + }, + "eval_feedback": { + "suggestions": [ + { + "assertion": "The output includes the name 'John Smith'", + "reason": "A hallucinated document that mentions the name would also pass" + } + ], + "overall": "Assertions check presence but not correctness." + } +} +``` + +**Fields:** +- `expectations[]`: Graded expectations with evidence +- `summary`: Aggregate pass/fail counts +- `execution_metrics`: Tool usage and output size (from executor's metrics.json) +- `timing`: Wall clock timing (from timing.json) +- `claims`: Extracted and verified claims from the output +- `user_notes_summary`: Issues flagged by the executor +- `eval_feedback`: (optional) Improvement suggestions for the evals, only present when the grader identifies issues worth raising + +--- + +## metrics.json + +Output from the executor agent. Located at `/outputs/metrics.json`. + +```json +{ + "tool_calls": { + "Read": 5, + "Write": 2, + "Bash": 8, + "Edit": 1, + "Glob": 2, + "Grep": 0 + }, + "total_tool_calls": 18, + "total_steps": 6, + "files_created": ["filled_form.pdf", "field_values.json"], + "errors_encountered": 0, + "output_chars": 12450, + "transcript_chars": 3200 +} +``` + +**Fields:** +- `tool_calls`: Count per tool type +- `total_tool_calls`: Sum of all tool calls +- `total_steps`: Number of major execution steps +- `files_created`: List of output files created +- `errors_encountered`: Number of errors during execution +- `output_chars`: Total character count of output files +- `transcript_chars`: Character count of transcript + +--- + +## timing.json + +Wall clock timing for a run. Located at `/timing.json`. + +**How to capture:** When a subagent task completes, the task notification includes `total_tokens` and `duration_ms`. Save these immediately — they are not persisted anywhere else and cannot be recovered after the fact. + +```json +{ + "total_tokens": 84852, + "duration_ms": 23332, + "total_duration_seconds": 23.3, + "executor_start": "2026-01-15T10:30:00Z", + "executor_end": "2026-01-15T10:32:45Z", + "executor_duration_seconds": 165.0, + "grader_start": "2026-01-15T10:32:46Z", + "grader_end": "2026-01-15T10:33:12Z", + "grader_duration_seconds": 26.0 +} +``` + +--- + +## benchmark.json + +Output from Benchmark mode. Located at `benchmarks//benchmark.json`. + +```json +{ + "metadata": { + "skill_name": "pdf", + "skill_path": "/path/to/pdf", + "executor_model": "claude-sonnet-4-20250514", + "analyzer_model": "most-capable-model", + "timestamp": "2026-01-15T10:30:00Z", + "evals_run": [1, 2, 3], + "runs_per_configuration": 3 + }, + + "runs": [ + { + "eval_id": 1, + "eval_name": "Ocean", + "configuration": "with_skill", + "run_number": 1, + "result": { + "pass_rate": 0.85, + "passed": 6, + "failed": 1, + "total": 7, + "time_seconds": 42.5, + "tokens": 3800, + "tool_calls": 18, + "errors": 0 + }, + "expectations": [ + {"text": "...", "passed": true, "evidence": "..."} + ], + "notes": [ + "Used 2023 data, may be stale", + "Fell back to text overlay for non-fillable fields" + ] + } + ], + + "run_summary": { + "with_skill": { + "pass_rate": {"mean": 0.85, "stddev": 0.05, "min": 0.80, "max": 0.90}, + "time_seconds": {"mean": 45.0, "stddev": 12.0, "min": 32.0, "max": 58.0}, + "tokens": {"mean": 3800, "stddev": 400, "min": 3200, "max": 4100} + }, + "without_skill": { + "pass_rate": {"mean": 0.35, "stddev": 0.08, "min": 0.28, "max": 0.45}, + "time_seconds": {"mean": 32.0, "stddev": 8.0, "min": 24.0, "max": 42.0}, + "tokens": {"mean": 2100, "stddev": 300, "min": 1800, "max": 2500} + }, + "delta": { + "pass_rate": "+0.50", + "time_seconds": "+13.0", + "tokens": "+1700" + } + }, + + "notes": [ + "Assertion 'Output is a PDF file' passes 100% in both configurations - may not differentiate skill value", + "Eval 3 shows high variance (50% ± 40%) - may be flaky or model-dependent", + "Without-skill runs consistently fail on table extraction expectations", + "Skill adds 13s average execution time but improves pass rate by 50%" + ] +} +``` + +**Fields:** +- `metadata`: Information about the benchmark run + - `skill_name`: Name of the skill + - `timestamp`: When the benchmark was run + - `evals_run`: List of eval names or IDs + - `runs_per_configuration`: Number of runs per config (e.g. 3) +- `runs[]`: Individual run results + - `eval_id`: Numeric eval identifier + - `eval_name`: Human-readable eval name (used as section header in the viewer) + - `configuration`: Must be `"with_skill"` or `"without_skill"` (the viewer uses this exact string for grouping and color coding) + - `run_number`: Integer run number (1, 2, 3...) + - `result`: Nested object with `pass_rate`, `passed`, `total`, `time_seconds`, `tokens`, `errors` +- `run_summary`: Statistical aggregates per configuration + - `with_skill` / `without_skill`: Each contains `pass_rate`, `time_seconds`, `tokens` objects with `mean` and `stddev` fields + - `delta`: Difference strings like `"+0.50"`, `"+13.0"`, `"+1700"` +- `notes`: Freeform observations from the analyzer + +**Important:** The viewer reads these field names exactly. Using `config` instead of `configuration`, or putting `pass_rate` at the top level of a run instead of nested under `result`, will cause the viewer to show empty/zero values. Always reference this schema when generating benchmark.json manually. + +--- + +## comparison.json + +Output from blind comparator. Located at `/comparison-N.json`. + +```json +{ + "winner": "A", + "reasoning": "Output A provides a complete solution with proper formatting and all required fields. Output B is missing the date field and has formatting inconsistencies.", + "rubric": { + "A": { + "content": { + "correctness": 5, + "completeness": 5, + "accuracy": 4 + }, + "structure": { + "organization": 4, + "formatting": 5, + "usability": 4 + }, + "content_score": 4.7, + "structure_score": 4.3, + "overall_score": 9.0 + }, + "B": { + "content": { + "correctness": 3, + "completeness": 2, + "accuracy": 3 + }, + "structure": { + "organization": 3, + "formatting": 2, + "usability": 3 + }, + "content_score": 2.7, + "structure_score": 2.7, + "overall_score": 5.4 + } + }, + "output_quality": { + "A": { + "score": 9, + "strengths": ["Complete solution", "Well-formatted", "All fields present"], + "weaknesses": ["Minor style inconsistency in header"] + }, + "B": { + "score": 5, + "strengths": ["Readable output", "Correct basic structure"], + "weaknesses": ["Missing date field", "Formatting inconsistencies", "Partial data extraction"] + } + }, + "expectation_results": { + "A": { + "passed": 4, + "total": 5, + "pass_rate": 0.80, + "details": [ + {"text": "Output includes name", "passed": true} + ] + }, + "B": { + "passed": 3, + "total": 5, + "pass_rate": 0.60, + "details": [ + {"text": "Output includes name", "passed": true} + ] + } + } +} +``` + +--- + +## analysis.json + +Output from post-hoc analyzer. Located at `/analysis.json`. + +```json +{ + "comparison_summary": { + "winner": "A", + "winner_skill": "path/to/winner/skill", + "loser_skill": "path/to/loser/skill", + "comparator_reasoning": "Brief summary of why comparator chose winner" + }, + "winner_strengths": [ + "Clear step-by-step instructions for handling multi-page documents", + "Included validation script that caught formatting errors" + ], + "loser_weaknesses": [ + "Vague instruction 'process the document appropriately' led to inconsistent behavior", + "No script for validation, agent had to improvise" + ], + "instruction_following": { + "winner": { + "score": 9, + "issues": ["Minor: skipped optional logging step"] + }, + "loser": { + "score": 6, + "issues": [ + "Did not use the skill's formatting template", + "Invented own approach instead of following step 3" + ] + } + }, + "improvement_suggestions": [ + { + "priority": "high", + "category": "instructions", + "suggestion": "Replace 'process the document appropriately' with explicit steps", + "expected_impact": "Would eliminate ambiguity that caused inconsistent behavior" + } + ], + "transcript_insights": { + "winner_execution_pattern": "Read skill -> Followed 5-step process -> Used validation script", + "loser_execution_pattern": "Read skill -> Unclear on approach -> Tried 3 different methods" + } +} +``` diff --git a/.skills/skill-creator/scripts/__init__.py b/.skills/skill-creator/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.skills/skill-creator/scripts/aggregate_benchmark.py b/.skills/skill-creator/scripts/aggregate_benchmark.py new file mode 100644 index 00000000..3e66e8c1 --- /dev/null +++ b/.skills/skill-creator/scripts/aggregate_benchmark.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python3 +""" +Aggregate individual run results into benchmark summary statistics. + +Reads grading.json files from run directories and produces: +- run_summary with mean, stddev, min, max for each metric +- delta between with_skill and without_skill configurations + +Usage: + python aggregate_benchmark.py + +Example: + python aggregate_benchmark.py benchmarks/2026-01-15T10-30-00/ + +The script supports two directory layouts: + + Workspace layout (from skill-creator iterations): + / + └── eval-N/ + ├── with_skill/ + │ ├── run-1/grading.json + │ └── run-2/grading.json + └── without_skill/ + ├── run-1/grading.json + └── run-2/grading.json + + Legacy layout (with runs/ subdirectory): + / + └── runs/ + └── eval-N/ + ├── with_skill/ + │ └── run-1/grading.json + └── without_skill/ + └── run-1/grading.json +""" + +import argparse +import json +import math +import sys +from datetime import datetime, timezone +from pathlib import Path + + +def calculate_stats(values: list[float]) -> dict: + """Calculate mean, stddev, min, max for a list of values.""" + if not values: + return {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0} + + n = len(values) + mean = sum(values) / n + + if n > 1: + variance = sum((x - mean) ** 2 for x in values) / (n - 1) + stddev = math.sqrt(variance) + else: + stddev = 0.0 + + return { + "mean": round(mean, 4), + "stddev": round(stddev, 4), + "min": round(min(values), 4), + "max": round(max(values), 4) + } + + +def load_run_results(benchmark_dir: Path) -> dict: + """ + Load all run results from a benchmark directory. + + Returns dict keyed by config name (e.g. "with_skill"/"without_skill", + or "new_skill"/"old_skill"), each containing a list of run results. + """ + # Support both layouts: eval dirs directly under benchmark_dir, or under runs/ + runs_dir = benchmark_dir / "runs" + if runs_dir.exists(): + search_dir = runs_dir + elif list(benchmark_dir.glob("eval-*")): + search_dir = benchmark_dir + else: + print(f"No eval directories found in {benchmark_dir} or {benchmark_dir / 'runs'}") + return {} + + results: dict[str, list] = {} + + for eval_idx, eval_dir in enumerate(sorted(search_dir.glob("eval-*"))): + metadata_path = eval_dir / "eval_metadata.json" + if metadata_path.exists(): + try: + with open(metadata_path) as mf: + eval_id = json.load(mf).get("eval_id", eval_idx) + except (json.JSONDecodeError, OSError): + eval_id = eval_idx + else: + try: + eval_id = int(eval_dir.name.split("-")[1]) + except ValueError: + eval_id = eval_idx + + # Discover config directories dynamically rather than hardcoding names + for config_dir in sorted(eval_dir.iterdir()): + if not config_dir.is_dir(): + continue + # Skip non-config directories (inputs, outputs, etc.) + if not list(config_dir.glob("run-*")): + continue + config = config_dir.name + if config not in results: + results[config] = [] + + for run_dir in sorted(config_dir.glob("run-*")): + run_number = int(run_dir.name.split("-")[1]) + grading_file = run_dir / "grading.json" + + if not grading_file.exists(): + print(f"Warning: grading.json not found in {run_dir}") + continue + + try: + with open(grading_file) as f: + grading = json.load(f) + except json.JSONDecodeError as e: + print(f"Warning: Invalid JSON in {grading_file}: {e}") + continue + + # Extract metrics + result = { + "eval_id": eval_id, + "run_number": run_number, + "pass_rate": grading.get("summary", {}).get("pass_rate", 0.0), + "passed": grading.get("summary", {}).get("passed", 0), + "failed": grading.get("summary", {}).get("failed", 0), + "total": grading.get("summary", {}).get("total", 0), + } + + # Extract timing — check grading.json first, then sibling timing.json + timing = grading.get("timing", {}) + result["time_seconds"] = timing.get("total_duration_seconds", 0.0) + timing_file = run_dir / "timing.json" + if result["time_seconds"] == 0.0 and timing_file.exists(): + try: + with open(timing_file) as tf: + timing_data = json.load(tf) + result["time_seconds"] = timing_data.get("total_duration_seconds", 0.0) + result["tokens"] = timing_data.get("total_tokens", 0) + except json.JSONDecodeError: + pass + + # Extract metrics if available + metrics = grading.get("execution_metrics", {}) + result["tool_calls"] = metrics.get("total_tool_calls", 0) + if not result.get("tokens"): + result["tokens"] = metrics.get("output_chars", 0) + result["errors"] = metrics.get("errors_encountered", 0) + + # Extract expectations — viewer requires fields: text, passed, evidence + raw_expectations = grading.get("expectations", []) + for exp in raw_expectations: + if "text" not in exp or "passed" not in exp: + print(f"Warning: expectation in {grading_file} missing required fields (text, passed, evidence): {exp}") + result["expectations"] = raw_expectations + + # Extract notes from user_notes_summary + notes_summary = grading.get("user_notes_summary", {}) + notes = [] + notes.extend(notes_summary.get("uncertainties", [])) + notes.extend(notes_summary.get("needs_review", [])) + notes.extend(notes_summary.get("workarounds", [])) + result["notes"] = notes + + results[config].append(result) + + return results + + +def aggregate_results(results: dict) -> dict: + """ + Aggregate run results into summary statistics. + + Returns run_summary with stats for each configuration and delta. + """ + run_summary = {} + configs = list(results.keys()) + + for config in configs: + runs = results.get(config, []) + + if not runs: + run_summary[config] = { + "pass_rate": {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0}, + "time_seconds": {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0}, + "tokens": {"mean": 0, "stddev": 0, "min": 0, "max": 0} + } + continue + + pass_rates = [r["pass_rate"] for r in runs] + times = [r["time_seconds"] for r in runs] + tokens = [r.get("tokens", 0) for r in runs] + + run_summary[config] = { + "pass_rate": calculate_stats(pass_rates), + "time_seconds": calculate_stats(times), + "tokens": calculate_stats(tokens) + } + + # Calculate delta between the first two configs (if two exist) + if len(configs) >= 2: + primary = run_summary.get(configs[0], {}) + baseline = run_summary.get(configs[1], {}) + else: + primary = run_summary.get(configs[0], {}) if configs else {} + baseline = {} + + delta_pass_rate = primary.get("pass_rate", {}).get("mean", 0) - baseline.get("pass_rate", {}).get("mean", 0) + delta_time = primary.get("time_seconds", {}).get("mean", 0) - baseline.get("time_seconds", {}).get("mean", 0) + delta_tokens = primary.get("tokens", {}).get("mean", 0) - baseline.get("tokens", {}).get("mean", 0) + + run_summary["delta"] = { + "pass_rate": f"{delta_pass_rate:+.2f}", + "time_seconds": f"{delta_time:+.1f}", + "tokens": f"{delta_tokens:+.0f}" + } + + return run_summary + + +def generate_benchmark(benchmark_dir: Path, skill_name: str = "", skill_path: str = "") -> dict: + """ + Generate complete benchmark.json from run results. + """ + results = load_run_results(benchmark_dir) + run_summary = aggregate_results(results) + + # Build runs array for benchmark.json + runs = [] + for config in results: + for result in results[config]: + runs.append({ + "eval_id": result["eval_id"], + "configuration": config, + "run_number": result["run_number"], + "result": { + "pass_rate": result["pass_rate"], + "passed": result["passed"], + "failed": result["failed"], + "total": result["total"], + "time_seconds": result["time_seconds"], + "tokens": result.get("tokens", 0), + "tool_calls": result.get("tool_calls", 0), + "errors": result.get("errors", 0) + }, + "expectations": result["expectations"], + "notes": result["notes"] + }) + + # Determine eval IDs from results + eval_ids = sorted(set( + r["eval_id"] + for config in results.values() + for r in config + )) + + benchmark = { + "metadata": { + "skill_name": skill_name or "", + "skill_path": skill_path or "", + "executor_model": "", + "analyzer_model": "", + "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "evals_run": eval_ids, + "runs_per_configuration": 3 + }, + "runs": runs, + "run_summary": run_summary, + "notes": [] # To be filled by analyzer + } + + return benchmark + + +def generate_markdown(benchmark: dict) -> str: + """Generate human-readable benchmark.md from benchmark data.""" + metadata = benchmark["metadata"] + run_summary = benchmark["run_summary"] + + # Determine config names (excluding "delta") + configs = [k for k in run_summary if k != "delta"] + config_a = configs[0] if len(configs) >= 1 else "config_a" + config_b = configs[1] if len(configs) >= 2 else "config_b" + label_a = config_a.replace("_", " ").title() + label_b = config_b.replace("_", " ").title() + + lines = [ + f"# Skill Benchmark: {metadata['skill_name']}", + "", + f"**Model**: {metadata['executor_model']}", + f"**Date**: {metadata['timestamp']}", + f"**Evals**: {', '.join(map(str, metadata['evals_run']))} ({metadata['runs_per_configuration']} runs each per configuration)", + "", + "## Summary", + "", + f"| Metric | {label_a} | {label_b} | Delta |", + "|--------|------------|---------------|-------|", + ] + + a_summary = run_summary.get(config_a, {}) + b_summary = run_summary.get(config_b, {}) + delta = run_summary.get("delta", {}) + + # Format pass rate + a_pr = a_summary.get("pass_rate", {}) + b_pr = b_summary.get("pass_rate", {}) + lines.append(f"| Pass Rate | {a_pr.get('mean', 0)*100:.0f}% ± {a_pr.get('stddev', 0)*100:.0f}% | {b_pr.get('mean', 0)*100:.0f}% ± {b_pr.get('stddev', 0)*100:.0f}% | {delta.get('pass_rate', '—')} |") + + # Format time + a_time = a_summary.get("time_seconds", {}) + b_time = b_summary.get("time_seconds", {}) + lines.append(f"| Time | {a_time.get('mean', 0):.1f}s ± {a_time.get('stddev', 0):.1f}s | {b_time.get('mean', 0):.1f}s ± {b_time.get('stddev', 0):.1f}s | {delta.get('time_seconds', '—')}s |") + + # Format tokens + a_tokens = a_summary.get("tokens", {}) + b_tokens = b_summary.get("tokens", {}) + lines.append(f"| Tokens | {a_tokens.get('mean', 0):.0f} ± {a_tokens.get('stddev', 0):.0f} | {b_tokens.get('mean', 0):.0f} ± {b_tokens.get('stddev', 0):.0f} | {delta.get('tokens', '—')} |") + + # Notes section + if benchmark.get("notes"): + lines.extend([ + "", + "## Notes", + "" + ]) + for note in benchmark["notes"]: + lines.append(f"- {note}") + + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser( + description="Aggregate benchmark run results into summary statistics" + ) + parser.add_argument( + "benchmark_dir", + type=Path, + help="Path to the benchmark directory" + ) + parser.add_argument( + "--skill-name", + default="", + help="Name of the skill being benchmarked" + ) + parser.add_argument( + "--skill-path", + default="", + help="Path to the skill being benchmarked" + ) + parser.add_argument( + "--output", "-o", + type=Path, + help="Output path for benchmark.json (default: /benchmark.json)" + ) + + args = parser.parse_args() + + if not args.benchmark_dir.exists(): + print(f"Directory not found: {args.benchmark_dir}") + sys.exit(1) + + # Generate benchmark + benchmark = generate_benchmark(args.benchmark_dir, args.skill_name, args.skill_path) + + # Determine output paths + output_json = args.output or (args.benchmark_dir / "benchmark.json") + output_md = output_json.with_suffix(".md") + + # Write benchmark.json + with open(output_json, "w") as f: + json.dump(benchmark, f, indent=2) + print(f"Generated: {output_json}") + + # Write benchmark.md + markdown = generate_markdown(benchmark) + with open(output_md, "w") as f: + f.write(markdown) + print(f"Generated: {output_md}") + + # Print summary + run_summary = benchmark["run_summary"] + configs = [k for k in run_summary if k != "delta"] + delta = run_summary.get("delta", {}) + + print(f"\nSummary:") + for config in configs: + pr = run_summary[config]["pass_rate"]["mean"] + label = config.replace("_", " ").title() + print(f" {label}: {pr*100:.1f}% pass rate") + print(f" Delta: {delta.get('pass_rate', '—')}") + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/generate_report.py b/.skills/skill-creator/scripts/generate_report.py new file mode 100644 index 00000000..959e30a0 --- /dev/null +++ b/.skills/skill-creator/scripts/generate_report.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +"""Generate an HTML report from run_loop.py output. + +Takes the JSON output from run_loop.py and generates a visual HTML report +showing each description attempt with check/x for each test case. +Distinguishes between train and test queries. +""" + +import argparse +import html +import json +import sys +from pathlib import Path + + +def generate_html(data: dict, auto_refresh: bool = False, skill_name: str = "") -> str: + """Generate HTML report from loop output data. If auto_refresh is True, adds a meta refresh tag.""" + history = data.get("history", []) + holdout = data.get("holdout", 0) + title_prefix = html.escape(skill_name + " \u2014 ") if skill_name else "" + + # Get all unique queries from train and test sets, with should_trigger info + train_queries: list[dict] = [] + test_queries: list[dict] = [] + if history: + for r in history[0].get("train_results", history[0].get("results", [])): + train_queries.append({"query": r["query"], "should_trigger": r.get("should_trigger", True)}) + if history[0].get("test_results"): + for r in history[0].get("test_results", []): + test_queries.append({"query": r["query"], "should_trigger": r.get("should_trigger", True)}) + + refresh_tag = ' \n' if auto_refresh else "" + + html_parts = [""" + + + +""" + refresh_tag + """ """ + title_prefix + """Skill Description Optimization + + + + + + +

""" + title_prefix + """Skill Description Optimization

+
+ Optimizing your skill's description. This page updates automatically as Claude tests different versions of your skill's description. Each row is an iteration — a new description attempt. The columns show test queries: green checkmarks mean the skill triggered correctly (or correctly didn't trigger), red crosses mean it got it wrong. The "Train" score shows performance on queries used to improve the description; the "Test" score shows performance on held-out queries the optimizer hasn't seen. When it's done, Claude will apply the best-performing description to your skill. +
+"""] + + # Summary section + best_test_score = data.get('best_test_score') + best_train_score = data.get('best_train_score') + html_parts.append(f""" +
+

Original: {html.escape(data.get('original_description', 'N/A'))}

+

Best: {html.escape(data.get('best_description', 'N/A'))}

+

Best Score: {data.get('best_score', 'N/A')} {'(test)' if best_test_score else '(train)'}

+

Iterations: {data.get('iterations_run', 0)} | Train: {data.get('train_size', '?')} | Test: {data.get('test_size', '?')}

+
+""") + + # Legend + html_parts.append(""" +
+ Query columns: + Should trigger + Should NOT trigger + Train + Test +
+""") + + # Table header + html_parts.append(""" +
+ + + + + + + +""") + + # Add column headers for train queries + for qinfo in train_queries: + polarity = "positive-col" if qinfo["should_trigger"] else "negative-col" + html_parts.append(f' \n') + + # Add column headers for test queries (different color) + for qinfo in test_queries: + polarity = "positive-col" if qinfo["should_trigger"] else "negative-col" + html_parts.append(f' \n') + + html_parts.append(""" + + +""") + + # Find best iteration for highlighting + if test_queries: + best_iter = max(history, key=lambda h: h.get("test_passed") or 0).get("iteration") + else: + best_iter = max(history, key=lambda h: h.get("train_passed", h.get("passed", 0))).get("iteration") + + # Add rows for each iteration + for h in history: + iteration = h.get("iteration", "?") + train_passed = h.get("train_passed", h.get("passed", 0)) + train_total = h.get("train_total", h.get("total", 0)) + test_passed = h.get("test_passed") + test_total = h.get("test_total") + description = h.get("description", "") + train_results = h.get("train_results", h.get("results", [])) + test_results = h.get("test_results", []) + + # Create lookups for results by query + train_by_query = {r["query"]: r for r in train_results} + test_by_query = {r["query"]: r for r in test_results} if test_results else {} + + # Compute aggregate correct/total runs across all retries + def aggregate_runs(results: list[dict]) -> tuple[int, int]: + correct = 0 + total = 0 + for r in results: + runs = r.get("runs", 0) + triggers = r.get("triggers", 0) + total += runs + if r.get("should_trigger", True): + correct += triggers + else: + correct += runs - triggers + return correct, total + + train_correct, train_runs = aggregate_runs(train_results) + test_correct, test_runs = aggregate_runs(test_results) + + # Determine score classes + def score_class(correct: int, total: int) -> str: + if total > 0: + ratio = correct / total + if ratio >= 0.8: + return "score-good" + elif ratio >= 0.5: + return "score-ok" + return "score-bad" + + train_class = score_class(train_correct, train_runs) + test_class = score_class(test_correct, test_runs) + + row_class = "best-row" if iteration == best_iter else "" + + html_parts.append(f""" + + + + +""") + + # Add result for each train query + for qinfo in train_queries: + r = train_by_query.get(qinfo["query"], {}) + did_pass = r.get("pass", False) + triggers = r.get("triggers", 0) + runs = r.get("runs", 0) + + icon = "✓" if did_pass else "✗" + css_class = "pass" if did_pass else "fail" + + html_parts.append(f' \n') + + # Add result for each test query (with different background) + for qinfo in test_queries: + r = test_by_query.get(qinfo["query"], {}) + did_pass = r.get("pass", False) + triggers = r.get("triggers", 0) + runs = r.get("runs", 0) + + icon = "✓" if did_pass else "✗" + css_class = "pass" if did_pass else "fail" + + html_parts.append(f' \n') + + html_parts.append(" \n") + + html_parts.append(""" +
IterTrainTestDescription{html.escape(qinfo["query"])}{html.escape(qinfo["query"])}
{iteration}{train_correct}/{train_runs}{test_correct}/{test_runs}{html.escape(description)}{icon}{triggers}/{runs}{icon}{triggers}/{runs}
+
+""") + + html_parts.append(""" + + +""") + + return "".join(html_parts) + + +def main(): + parser = argparse.ArgumentParser(description="Generate HTML report from run_loop output") + parser.add_argument("input", help="Path to JSON output from run_loop.py (or - for stdin)") + parser.add_argument("-o", "--output", default=None, help="Output HTML file (default: stdout)") + parser.add_argument("--skill-name", default="", help="Skill name to include in the report title") + args = parser.parse_args() + + if args.input == "-": + data = json.load(sys.stdin) + else: + data = json.loads(Path(args.input).read_text()) + + html_output = generate_html(data, skill_name=args.skill_name) + + if args.output: + Path(args.output).write_text(html_output) + print(f"Report written to {args.output}", file=sys.stderr) + else: + print(html_output) + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/improve_description.py b/.skills/skill-creator/scripts/improve_description.py new file mode 100644 index 00000000..a270777b --- /dev/null +++ b/.skills/skill-creator/scripts/improve_description.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +"""Improve a skill description based on eval results. + +Takes eval results (from run_eval.py) and generates an improved description +using Claude with extended thinking. +""" + +import argparse +import json +import re +import sys +from pathlib import Path + +import anthropic + +from scripts.utils import parse_skill_md + + +def improve_description( + client: anthropic.Anthropic, + skill_name: str, + skill_content: str, + current_description: str, + eval_results: dict, + history: list[dict], + model: str, + test_results: dict | None = None, + log_dir: Path | None = None, + iteration: int | None = None, +) -> str: + """Call Claude to improve the description based on eval results.""" + failed_triggers = [ + r for r in eval_results["results"] + if r["should_trigger"] and not r["pass"] + ] + false_triggers = [ + r for r in eval_results["results"] + if not r["should_trigger"] and not r["pass"] + ] + + # Build scores summary + train_score = f"{eval_results['summary']['passed']}/{eval_results['summary']['total']}" + if test_results: + test_score = f"{test_results['summary']['passed']}/{test_results['summary']['total']}" + scores_summary = f"Train: {train_score}, Test: {test_score}" + else: + scores_summary = f"Train: {train_score}" + + prompt = f"""You are optimizing a skill description for a Claude Code skill called "{skill_name}". A "skill" is sort of like a prompt, but with progressive disclosure -- there's a title and description that Claude sees when deciding whether to use the skill, and then if it does use the skill, it reads the .md file which has lots more details and potentially links to other resources in the skill folder like helper files and scripts and additional documentation or examples. + +The description appears in Claude's "available_skills" list. When a user sends a query, Claude decides whether to invoke the skill based solely on the title and on this description. Your goal is to write a description that triggers for relevant queries, and doesn't trigger for irrelevant ones. + +Here's the current description: + +"{current_description}" + + +Current scores ({scores_summary}): + +""" + if failed_triggers: + prompt += "FAILED TO TRIGGER (should have triggered but didn't):\n" + for r in failed_triggers: + prompt += f' - "{r["query"]}" (triggered {r["triggers"]}/{r["runs"]} times)\n' + prompt += "\n" + + if false_triggers: + prompt += "FALSE TRIGGERS (triggered but shouldn't have):\n" + for r in false_triggers: + prompt += f' - "{r["query"]}" (triggered {r["triggers"]}/{r["runs"]} times)\n' + prompt += "\n" + + if history: + prompt += "PREVIOUS ATTEMPTS (do NOT repeat these — try something structurally different):\n\n" + for h in history: + train_s = f"{h.get('train_passed', h.get('passed', 0))}/{h.get('train_total', h.get('total', 0))}" + test_s = f"{h.get('test_passed', '?')}/{h.get('test_total', '?')}" if h.get('test_passed') is not None else None + score_str = f"train={train_s}" + (f", test={test_s}" if test_s else "") + prompt += f'\n' + prompt += f'Description: "{h["description"]}"\n' + if "results" in h: + prompt += "Train results:\n" + for r in h["results"]: + status = "PASS" if r["pass"] else "FAIL" + prompt += f' [{status}] "{r["query"][:80]}" (triggered {r["triggers"]}/{r["runs"]})\n' + if h.get("note"): + prompt += f'Note: {h["note"]}\n' + prompt += "\n\n" + + prompt += f""" + +Skill content (for context on what the skill does): + +{skill_content} + + +Based on the failures, write a new and improved description that is more likely to trigger correctly. When I say "based on the failures", it's a bit of a tricky line to walk because we don't want to overfit to the specific cases you're seeing. So what I DON'T want you to do is produce an ever-expanding list of specific queries that this skill should or shouldn't trigger for. Instead, try to generalize from the failures to broader categories of user intent and situations where this skill would be useful or not useful. The reason for this is twofold: + +1. Avoid overfitting +2. The list might get loooong and it's injected into ALL queries and there might be a lot of skills, so we don't want to blow too much space on any given description. + +Concretely, your description should not be more than about 100-200 words, even if that comes at the cost of accuracy. + +Here are some tips that we've found to work well in writing these descriptions: +- The skill should be phrased in the imperative -- "Use this skill for" rather than "this skill does" +- The skill description should focus on the user's intent, what they are trying to achieve, vs. the implementation details of how the skill works. +- The description competes with other skills for Claude's attention — make it distinctive and immediately recognizable. +- If you're getting lots of failures after repeated attempts, change things up. Try different sentence structures or wordings. + +I'd encourage you to be creative and mix up the style in different iterations since you'll have multiple opportunities to try different approaches and we'll just grab the highest-scoring one at the end. + +Please respond with only the new description text in tags, nothing else.""" + + response = client.messages.create( + model=model, + max_tokens=16000, + thinking={ + "type": "enabled", + "budget_tokens": 10000, + }, + messages=[{"role": "user", "content": prompt}], + ) + + # Extract thinking and text from response + thinking_text = "" + text = "" + for block in response.content: + if block.type == "thinking": + thinking_text = block.thinking + elif block.type == "text": + text = block.text + + # Parse out the tags + match = re.search(r"(.*?)", text, re.DOTALL) + description = match.group(1).strip().strip('"') if match else text.strip().strip('"') + + # Log the transcript + transcript: dict = { + "iteration": iteration, + "prompt": prompt, + "thinking": thinking_text, + "response": text, + "parsed_description": description, + "char_count": len(description), + "over_limit": len(description) > 1024, + } + + # If over 1024 chars, ask the model to shorten it + if len(description) > 1024: + shorten_prompt = f"Your description is {len(description)} characters, which exceeds the hard 1024 character limit. Please rewrite it to be under 1024 characters while preserving the most important trigger words and intent coverage. Respond with only the new description in tags." + shorten_response = client.messages.create( + model=model, + max_tokens=16000, + thinking={ + "type": "enabled", + "budget_tokens": 10000, + }, + messages=[ + {"role": "user", "content": prompt}, + {"role": "assistant", "content": text}, + {"role": "user", "content": shorten_prompt}, + ], + ) + + shorten_thinking = "" + shorten_text = "" + for block in shorten_response.content: + if block.type == "thinking": + shorten_thinking = block.thinking + elif block.type == "text": + shorten_text = block.text + + match = re.search(r"(.*?)", shorten_text, re.DOTALL) + shortened = match.group(1).strip().strip('"') if match else shorten_text.strip().strip('"') + + transcript["rewrite_prompt"] = shorten_prompt + transcript["rewrite_thinking"] = shorten_thinking + transcript["rewrite_response"] = shorten_text + transcript["rewrite_description"] = shortened + transcript["rewrite_char_count"] = len(shortened) + description = shortened + + transcript["final_description"] = description + + if log_dir: + log_dir.mkdir(parents=True, exist_ok=True) + log_file = log_dir / f"improve_iter_{iteration or 'unknown'}.json" + log_file.write_text(json.dumps(transcript, indent=2)) + + return description + + +def main(): + parser = argparse.ArgumentParser(description="Improve a skill description based on eval results") + parser.add_argument("--eval-results", required=True, help="Path to eval results JSON (from run_eval.py)") + parser.add_argument("--skill-path", required=True, help="Path to skill directory") + parser.add_argument("--history", default=None, help="Path to history JSON (previous attempts)") + parser.add_argument("--model", required=True, help="Model for improvement") + parser.add_argument("--verbose", action="store_true", help="Print thinking to stderr") + args = parser.parse_args() + + skill_path = Path(args.skill_path) + if not (skill_path / "SKILL.md").exists(): + print(f"Error: No SKILL.md found at {skill_path}", file=sys.stderr) + sys.exit(1) + + eval_results = json.loads(Path(args.eval_results).read_text()) + history = [] + if args.history: + history = json.loads(Path(args.history).read_text()) + + name, _, content = parse_skill_md(skill_path) + current_description = eval_results["description"] + + if args.verbose: + print(f"Current: {current_description}", file=sys.stderr) + print(f"Score: {eval_results['summary']['passed']}/{eval_results['summary']['total']}", file=sys.stderr) + + client = anthropic.Anthropic() + new_description = improve_description( + client=client, + skill_name=name, + skill_content=content, + current_description=current_description, + eval_results=eval_results, + history=history, + model=args.model, + ) + + if args.verbose: + print(f"Improved: {new_description}", file=sys.stderr) + + # Output as JSON with both the new description and updated history + output = { + "description": new_description, + "history": history + [{ + "description": current_description, + "passed": eval_results["summary"]["passed"], + "failed": eval_results["summary"]["failed"], + "total": eval_results["summary"]["total"], + "results": eval_results["results"], + }], + } + print(json.dumps(output, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/package_skill.py b/.skills/skill-creator/scripts/package_skill.py new file mode 100644 index 00000000..f48eac44 --- /dev/null +++ b/.skills/skill-creator/scripts/package_skill.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +""" +Skill Packager - Creates a distributable .skill file of a skill folder + +Usage: + python utils/package_skill.py [output-directory] + +Example: + python utils/package_skill.py skills/public/my-skill + python utils/package_skill.py skills/public/my-skill ./dist +""" + +import fnmatch +import sys +import zipfile +from pathlib import Path +from scripts.quick_validate import validate_skill + +# Patterns to exclude when packaging skills. +EXCLUDE_DIRS = {"__pycache__", "node_modules"} +EXCLUDE_GLOBS = {"*.pyc"} +EXCLUDE_FILES = {".DS_Store"} +# Directories excluded only at the skill root (not when nested deeper). +ROOT_EXCLUDE_DIRS = {"evals"} + + +def should_exclude(rel_path: Path) -> bool: + """Check if a path should be excluded from packaging.""" + parts = rel_path.parts + if any(part in EXCLUDE_DIRS for part in parts): + return True + # rel_path is relative to skill_path.parent, so parts[0] is the skill + # folder name and parts[1] (if present) is the first subdir. + if len(parts) > 1 and parts[1] in ROOT_EXCLUDE_DIRS: + return True + name = rel_path.name + if name in EXCLUDE_FILES: + return True + return any(fnmatch.fnmatch(name, pat) for pat in EXCLUDE_GLOBS) + + +def package_skill(skill_path, output_dir=None): + """ + Package a skill folder into a .skill file. + + Args: + skill_path: Path to the skill folder + output_dir: Optional output directory for the .skill file (defaults to current directory) + + Returns: + Path to the created .skill file, or None if error + """ + skill_path = Path(skill_path).resolve() + + # Validate skill folder exists + if not skill_path.exists(): + print(f"❌ Error: Skill folder not found: {skill_path}") + return None + + if not skill_path.is_dir(): + print(f"❌ Error: Path is not a directory: {skill_path}") + return None + + # Validate SKILL.md exists + skill_md = skill_path / "SKILL.md" + if not skill_md.exists(): + print(f"❌ Error: SKILL.md not found in {skill_path}") + return None + + # Run validation before packaging + print("🔍 Validating skill...") + valid, message = validate_skill(skill_path) + if not valid: + print(f"❌ Validation failed: {message}") + print(" Please fix the validation errors before packaging.") + return None + print(f"✅ {message}\n") + + # Determine output location + skill_name = skill_path.name + if output_dir: + output_path = Path(output_dir).resolve() + output_path.mkdir(parents=True, exist_ok=True) + else: + output_path = Path.cwd() + + skill_filename = output_path / f"{skill_name}.skill" + + # Create the .skill file (zip format) + try: + with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: + # Walk through the skill directory, excluding build artifacts + for file_path in skill_path.rglob('*'): + if not file_path.is_file(): + continue + arcname = file_path.relative_to(skill_path.parent) + if should_exclude(arcname): + print(f" Skipped: {arcname}") + continue + zipf.write(file_path, arcname) + print(f" Added: {arcname}") + + print(f"\n✅ Successfully packaged skill to: {skill_filename}") + return skill_filename + + except Exception as e: + print(f"❌ Error creating .skill file: {e}") + return None + + +def main(): + if len(sys.argv) < 2: + print("Usage: python utils/package_skill.py [output-directory]") + print("\nExample:") + print(" python utils/package_skill.py skills/public/my-skill") + print(" python utils/package_skill.py skills/public/my-skill ./dist") + sys.exit(1) + + skill_path = sys.argv[1] + output_dir = sys.argv[2] if len(sys.argv) > 2 else None + + print(f"📦 Packaging skill: {skill_path}") + if output_dir: + print(f" Output directory: {output_dir}") + print() + + result = package_skill(skill_path, output_dir) + + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/quick_validate.py b/.skills/skill-creator/scripts/quick_validate.py new file mode 100644 index 00000000..ed8e1ddd --- /dev/null +++ b/.skills/skill-creator/scripts/quick_validate.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +""" +Quick validation script for skills - minimal version +""" + +import sys +import os +import re +import yaml +from pathlib import Path + +def validate_skill(skill_path): + """Basic validation of a skill""" + skill_path = Path(skill_path) + + # Check SKILL.md exists + skill_md = skill_path / 'SKILL.md' + if not skill_md.exists(): + return False, "SKILL.md not found" + + # Read and validate frontmatter + content = skill_md.read_text() + if not content.startswith('---'): + return False, "No YAML frontmatter found" + + # Extract frontmatter + match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not match: + return False, "Invalid frontmatter format" + + frontmatter_text = match.group(1) + + # Parse YAML frontmatter + try: + frontmatter = yaml.safe_load(frontmatter_text) + if not isinstance(frontmatter, dict): + return False, "Frontmatter must be a YAML dictionary" + except yaml.YAMLError as e: + return False, f"Invalid YAML in frontmatter: {e}" + + # Define allowed properties + ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata', 'compatibility'} + + # Check for unexpected properties (excluding nested keys under metadata) + unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES + if unexpected_keys: + return False, ( + f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. " + f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}" + ) + + # Check required fields + if 'name' not in frontmatter: + return False, "Missing 'name' in frontmatter" + if 'description' not in frontmatter: + return False, "Missing 'description' in frontmatter" + + # Extract name for validation + name = frontmatter.get('name', '') + if not isinstance(name, str): + return False, f"Name must be a string, got {type(name).__name__}" + name = name.strip() + if name: + # Check naming convention (kebab-case: lowercase with hyphens) + if not re.match(r'^[a-z0-9-]+$', name): + return False, f"Name '{name}' should be kebab-case (lowercase letters, digits, and hyphens only)" + if name.startswith('-') or name.endswith('-') or '--' in name: + return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens" + # Check name length (max 64 characters per spec) + if len(name) > 64: + return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters." + + # Extract and validate description + description = frontmatter.get('description', '') + if not isinstance(description, str): + return False, f"Description must be a string, got {type(description).__name__}" + description = description.strip() + if description: + # Check for angle brackets + if '<' in description or '>' in description: + return False, "Description cannot contain angle brackets (< or >)" + # Check description length (max 1024 characters per spec) + if len(description) > 1024: + return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters." + + # Validate compatibility field if present (optional) + compatibility = frontmatter.get('compatibility', '') + if compatibility: + if not isinstance(compatibility, str): + return False, f"Compatibility must be a string, got {type(compatibility).__name__}" + if len(compatibility) > 500: + return False, f"Compatibility is too long ({len(compatibility)} characters). Maximum is 500 characters." + + return True, "Skill is valid!" + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python quick_validate.py ") + sys.exit(1) + + valid, message = validate_skill(sys.argv[1]) + print(message) + sys.exit(0 if valid else 1) \ No newline at end of file diff --git a/.skills/skill-creator/scripts/run_eval.py b/.skills/skill-creator/scripts/run_eval.py new file mode 100644 index 00000000..e58c70be --- /dev/null +++ b/.skills/skill-creator/scripts/run_eval.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +"""Run trigger evaluation for a skill description. + +Tests whether a skill's description causes Claude to trigger (read the skill) +for a set of queries. Outputs results as JSON. +""" + +import argparse +import json +import os +import select +import subprocess +import sys +import time +import uuid +from concurrent.futures import ProcessPoolExecutor, as_completed +from pathlib import Path + +from scripts.utils import parse_skill_md + + +def find_project_root() -> Path: + """Find the project root by walking up from cwd looking for .claude/. + + Mimics how Claude Code discovers its project root, so the command file + we create ends up where claude -p will look for it. + """ + current = Path.cwd() + for parent in [current, *current.parents]: + if (parent / ".claude").is_dir(): + return parent + return current + + +def run_single_query( + query: str, + skill_name: str, + skill_description: str, + timeout: int, + project_root: str, + model: str | None = None, +) -> bool: + """Run a single query and return whether the skill was triggered. + + Creates a command file in .claude/commands/ so it appears in Claude's + available_skills list, then runs `claude -p` with the raw query. + Uses --include-partial-messages to detect triggering early from + stream events (content_block_start) rather than waiting for the + full assistant message, which only arrives after tool execution. + """ + unique_id = uuid.uuid4().hex[:8] + clean_name = f"{skill_name}-skill-{unique_id}" + project_commands_dir = Path(project_root) / ".claude" / "commands" + command_file = project_commands_dir / f"{clean_name}.md" + + try: + project_commands_dir.mkdir(parents=True, exist_ok=True) + # Use YAML block scalar to avoid breaking on quotes in description + indented_desc = "\n ".join(skill_description.split("\n")) + command_content = ( + f"---\n" + f"description: |\n" + f" {indented_desc}\n" + f"---\n\n" + f"# {skill_name}\n\n" + f"This skill handles: {skill_description}\n" + ) + command_file.write_text(command_content) + + cmd = [ + "claude", + "-p", query, + "--output-format", "stream-json", + "--verbose", + "--include-partial-messages", + ] + if model: + cmd.extend(["--model", model]) + + # Remove CLAUDECODE env var to allow nesting claude -p inside a + # Claude Code session. The guard is for interactive terminal conflicts; + # programmatic subprocess usage is safe. + env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"} + + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + cwd=project_root, + env=env, + ) + + triggered = False + start_time = time.time() + buffer = "" + # Track state for stream event detection + pending_tool_name = None + accumulated_json = "" + + try: + while time.time() - start_time < timeout: + if process.poll() is not None: + remaining = process.stdout.read() + if remaining: + buffer += remaining.decode("utf-8", errors="replace") + break + + ready, _, _ = select.select([process.stdout], [], [], 1.0) + if not ready: + continue + + chunk = os.read(process.stdout.fileno(), 8192) + if not chunk: + break + buffer += chunk.decode("utf-8", errors="replace") + + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.strip() + if not line: + continue + + try: + event = json.loads(line) + except json.JSONDecodeError: + continue + + # Early detection via stream events + if event.get("type") == "stream_event": + se = event.get("event", {}) + se_type = se.get("type", "") + + if se_type == "content_block_start": + cb = se.get("content_block", {}) + if cb.get("type") == "tool_use": + tool_name = cb.get("name", "") + if tool_name in ("Skill", "Read"): + pending_tool_name = tool_name + accumulated_json = "" + else: + return False + + elif se_type == "content_block_delta" and pending_tool_name: + delta = se.get("delta", {}) + if delta.get("type") == "input_json_delta": + accumulated_json += delta.get("partial_json", "") + if clean_name in accumulated_json: + return True + + elif se_type in ("content_block_stop", "message_stop"): + if pending_tool_name: + return clean_name in accumulated_json + if se_type == "message_stop": + return False + + # Fallback: full assistant message + elif event.get("type") == "assistant": + message = event.get("message", {}) + for content_item in message.get("content", []): + if content_item.get("type") != "tool_use": + continue + tool_name = content_item.get("name", "") + tool_input = content_item.get("input", {}) + if tool_name == "Skill" and clean_name in tool_input.get("skill", ""): + triggered = True + elif tool_name == "Read" and clean_name in tool_input.get("file_path", ""): + triggered = True + return triggered + + elif event.get("type") == "result": + return triggered + finally: + # Clean up process on any exit path (return, exception, timeout) + if process.poll() is None: + process.kill() + process.wait() + + return triggered + finally: + if command_file.exists(): + command_file.unlink() + + +def run_eval( + eval_set: list[dict], + skill_name: str, + description: str, + num_workers: int, + timeout: int, + project_root: Path, + runs_per_query: int = 1, + trigger_threshold: float = 0.5, + model: str | None = None, +) -> dict: + """Run the full eval set and return results.""" + results = [] + + with ProcessPoolExecutor(max_workers=num_workers) as executor: + future_to_info = {} + for item in eval_set: + for run_idx in range(runs_per_query): + future = executor.submit( + run_single_query, + item["query"], + skill_name, + description, + timeout, + str(project_root), + model, + ) + future_to_info[future] = (item, run_idx) + + query_triggers: dict[str, list[bool]] = {} + query_items: dict[str, dict] = {} + for future in as_completed(future_to_info): + item, _ = future_to_info[future] + query = item["query"] + query_items[query] = item + if query not in query_triggers: + query_triggers[query] = [] + try: + query_triggers[query].append(future.result()) + except Exception as e: + print(f"Warning: query failed: {e}", file=sys.stderr) + query_triggers[query].append(False) + + for query, triggers in query_triggers.items(): + item = query_items[query] + trigger_rate = sum(triggers) / len(triggers) + should_trigger = item["should_trigger"] + if should_trigger: + did_pass = trigger_rate >= trigger_threshold + else: + did_pass = trigger_rate < trigger_threshold + results.append({ + "query": query, + "should_trigger": should_trigger, + "trigger_rate": trigger_rate, + "triggers": sum(triggers), + "runs": len(triggers), + "pass": did_pass, + }) + + passed = sum(1 for r in results if r["pass"]) + total = len(results) + + return { + "skill_name": skill_name, + "description": description, + "results": results, + "summary": { + "total": total, + "passed": passed, + "failed": total - passed, + }, + } + + +def main(): + parser = argparse.ArgumentParser(description="Run trigger evaluation for a skill description") + parser.add_argument("--eval-set", required=True, help="Path to eval set JSON file") + parser.add_argument("--skill-path", required=True, help="Path to skill directory") + parser.add_argument("--description", default=None, help="Override description to test") + parser.add_argument("--num-workers", type=int, default=10, help="Number of parallel workers") + parser.add_argument("--timeout", type=int, default=30, help="Timeout per query in seconds") + parser.add_argument("--runs-per-query", type=int, default=3, help="Number of runs per query") + parser.add_argument("--trigger-threshold", type=float, default=0.5, help="Trigger rate threshold") + parser.add_argument("--model", default=None, help="Model to use for claude -p (default: user's configured model)") + parser.add_argument("--verbose", action="store_true", help="Print progress to stderr") + args = parser.parse_args() + + eval_set = json.loads(Path(args.eval_set).read_text()) + skill_path = Path(args.skill_path) + + if not (skill_path / "SKILL.md").exists(): + print(f"Error: No SKILL.md found at {skill_path}", file=sys.stderr) + sys.exit(1) + + name, original_description, content = parse_skill_md(skill_path) + description = args.description or original_description + project_root = find_project_root() + + if args.verbose: + print(f"Evaluating: {description}", file=sys.stderr) + + output = run_eval( + eval_set=eval_set, + skill_name=name, + description=description, + num_workers=args.num_workers, + timeout=args.timeout, + project_root=project_root, + runs_per_query=args.runs_per_query, + trigger_threshold=args.trigger_threshold, + model=args.model, + ) + + if args.verbose: + summary = output["summary"] + print(f"Results: {summary['passed']}/{summary['total']} passed", file=sys.stderr) + for r in output["results"]: + status = "PASS" if r["pass"] else "FAIL" + rate_str = f"{r['triggers']}/{r['runs']}" + print(f" [{status}] rate={rate_str} expected={r['should_trigger']}: {r['query'][:70]}", file=sys.stderr) + + print(json.dumps(output, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/run_loop.py b/.skills/skill-creator/scripts/run_loop.py new file mode 100644 index 00000000..36f9b4e0 --- /dev/null +++ b/.skills/skill-creator/scripts/run_loop.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +"""Run the eval + improve loop until all pass or max iterations reached. + +Combines run_eval.py and improve_description.py in a loop, tracking history +and returning the best description found. Supports train/test split to prevent +overfitting. +""" + +import argparse +import json +import random +import sys +import tempfile +import time +import webbrowser +from pathlib import Path + +import anthropic + +from scripts.generate_report import generate_html +from scripts.improve_description import improve_description +from scripts.run_eval import find_project_root, run_eval +from scripts.utils import parse_skill_md + + +def split_eval_set(eval_set: list[dict], holdout: float, seed: int = 42) -> tuple[list[dict], list[dict]]: + """Split eval set into train and test sets, stratified by should_trigger.""" + random.seed(seed) + + # Separate by should_trigger + trigger = [e for e in eval_set if e["should_trigger"]] + no_trigger = [e for e in eval_set if not e["should_trigger"]] + + # Shuffle each group + random.shuffle(trigger) + random.shuffle(no_trigger) + + # Calculate split points + n_trigger_test = max(1, int(len(trigger) * holdout)) + n_no_trigger_test = max(1, int(len(no_trigger) * holdout)) + + # Split + test_set = trigger[:n_trigger_test] + no_trigger[:n_no_trigger_test] + train_set = trigger[n_trigger_test:] + no_trigger[n_no_trigger_test:] + + return train_set, test_set + + +def run_loop( + eval_set: list[dict], + skill_path: Path, + description_override: str | None, + num_workers: int, + timeout: int, + max_iterations: int, + runs_per_query: int, + trigger_threshold: float, + holdout: float, + model: str, + verbose: bool, + live_report_path: Path | None = None, + log_dir: Path | None = None, +) -> dict: + """Run the eval + improvement loop.""" + project_root = find_project_root() + name, original_description, content = parse_skill_md(skill_path) + current_description = description_override or original_description + + # Split into train/test if holdout > 0 + if holdout > 0: + train_set, test_set = split_eval_set(eval_set, holdout) + if verbose: + print(f"Split: {len(train_set)} train, {len(test_set)} test (holdout={holdout})", file=sys.stderr) + else: + train_set = eval_set + test_set = [] + + client = anthropic.Anthropic() + history = [] + exit_reason = "unknown" + + for iteration in range(1, max_iterations + 1): + if verbose: + print(f"\n{'='*60}", file=sys.stderr) + print(f"Iteration {iteration}/{max_iterations}", file=sys.stderr) + print(f"Description: {current_description}", file=sys.stderr) + print(f"{'='*60}", file=sys.stderr) + + # Evaluate train + test together in one batch for parallelism + all_queries = train_set + test_set + t0 = time.time() + all_results = run_eval( + eval_set=all_queries, + skill_name=name, + description=current_description, + num_workers=num_workers, + timeout=timeout, + project_root=project_root, + runs_per_query=runs_per_query, + trigger_threshold=trigger_threshold, + model=model, + ) + eval_elapsed = time.time() - t0 + + # Split results back into train/test by matching queries + train_queries_set = {q["query"] for q in train_set} + train_result_list = [r for r in all_results["results"] if r["query"] in train_queries_set] + test_result_list = [r for r in all_results["results"] if r["query"] not in train_queries_set] + + train_passed = sum(1 for r in train_result_list if r["pass"]) + train_total = len(train_result_list) + train_summary = {"passed": train_passed, "failed": train_total - train_passed, "total": train_total} + train_results = {"results": train_result_list, "summary": train_summary} + + if test_set: + test_passed = sum(1 for r in test_result_list if r["pass"]) + test_total = len(test_result_list) + test_summary = {"passed": test_passed, "failed": test_total - test_passed, "total": test_total} + test_results = {"results": test_result_list, "summary": test_summary} + else: + test_results = None + test_summary = None + + history.append({ + "iteration": iteration, + "description": current_description, + "train_passed": train_summary["passed"], + "train_failed": train_summary["failed"], + "train_total": train_summary["total"], + "train_results": train_results["results"], + "test_passed": test_summary["passed"] if test_summary else None, + "test_failed": test_summary["failed"] if test_summary else None, + "test_total": test_summary["total"] if test_summary else None, + "test_results": test_results["results"] if test_results else None, + # For backward compat with report generator + "passed": train_summary["passed"], + "failed": train_summary["failed"], + "total": train_summary["total"], + "results": train_results["results"], + }) + + # Write live report if path provided + if live_report_path: + partial_output = { + "original_description": original_description, + "best_description": current_description, + "best_score": "in progress", + "iterations_run": len(history), + "holdout": holdout, + "train_size": len(train_set), + "test_size": len(test_set), + "history": history, + } + live_report_path.write_text(generate_html(partial_output, auto_refresh=True, skill_name=name)) + + if verbose: + def print_eval_stats(label, results, elapsed): + pos = [r for r in results if r["should_trigger"]] + neg = [r for r in results if not r["should_trigger"]] + tp = sum(r["triggers"] for r in pos) + pos_runs = sum(r["runs"] for r in pos) + fn = pos_runs - tp + fp = sum(r["triggers"] for r in neg) + neg_runs = sum(r["runs"] for r in neg) + tn = neg_runs - fp + total = tp + tn + fp + fn + precision = tp / (tp + fp) if (tp + fp) > 0 else 1.0 + recall = tp / (tp + fn) if (tp + fn) > 0 else 1.0 + accuracy = (tp + tn) / total if total > 0 else 0.0 + print(f"{label}: {tp+tn}/{total} correct, precision={precision:.0%} recall={recall:.0%} accuracy={accuracy:.0%} ({elapsed:.1f}s)", file=sys.stderr) + for r in results: + status = "PASS" if r["pass"] else "FAIL" + rate_str = f"{r['triggers']}/{r['runs']}" + print(f" [{status}] rate={rate_str} expected={r['should_trigger']}: {r['query'][:60]}", file=sys.stderr) + + print_eval_stats("Train", train_results["results"], eval_elapsed) + if test_summary: + print_eval_stats("Test ", test_results["results"], 0) + + if train_summary["failed"] == 0: + exit_reason = f"all_passed (iteration {iteration})" + if verbose: + print(f"\nAll train queries passed on iteration {iteration}!", file=sys.stderr) + break + + if iteration == max_iterations: + exit_reason = f"max_iterations ({max_iterations})" + if verbose: + print(f"\nMax iterations reached ({max_iterations}).", file=sys.stderr) + break + + # Improve the description based on train results + if verbose: + print(f"\nImproving description...", file=sys.stderr) + + t0 = time.time() + # Strip test scores from history so improvement model can't see them + blinded_history = [ + {k: v for k, v in h.items() if not k.startswith("test_")} + for h in history + ] + new_description = improve_description( + client=client, + skill_name=name, + skill_content=content, + current_description=current_description, + eval_results=train_results, + history=blinded_history, + model=model, + log_dir=log_dir, + iteration=iteration, + ) + improve_elapsed = time.time() - t0 + + if verbose: + print(f"Proposed ({improve_elapsed:.1f}s): {new_description}", file=sys.stderr) + + current_description = new_description + + # Find the best iteration by TEST score (or train if no test set) + if test_set: + best = max(history, key=lambda h: h["test_passed"] or 0) + best_score = f"{best['test_passed']}/{best['test_total']}" + else: + best = max(history, key=lambda h: h["train_passed"]) + best_score = f"{best['train_passed']}/{best['train_total']}" + + if verbose: + print(f"\nExit reason: {exit_reason}", file=sys.stderr) + print(f"Best score: {best_score} (iteration {best['iteration']})", file=sys.stderr) + + return { + "exit_reason": exit_reason, + "original_description": original_description, + "best_description": best["description"], + "best_score": best_score, + "best_train_score": f"{best['train_passed']}/{best['train_total']}", + "best_test_score": f"{best['test_passed']}/{best['test_total']}" if test_set else None, + "final_description": current_description, + "iterations_run": len(history), + "holdout": holdout, + "train_size": len(train_set), + "test_size": len(test_set), + "history": history, + } + + +def main(): + parser = argparse.ArgumentParser(description="Run eval + improve loop") + parser.add_argument("--eval-set", required=True, help="Path to eval set JSON file") + parser.add_argument("--skill-path", required=True, help="Path to skill directory") + parser.add_argument("--description", default=None, help="Override starting description") + parser.add_argument("--num-workers", type=int, default=10, help="Number of parallel workers") + parser.add_argument("--timeout", type=int, default=30, help="Timeout per query in seconds") + parser.add_argument("--max-iterations", type=int, default=5, help="Max improvement iterations") + parser.add_argument("--runs-per-query", type=int, default=3, help="Number of runs per query") + parser.add_argument("--trigger-threshold", type=float, default=0.5, help="Trigger rate threshold") + parser.add_argument("--holdout", type=float, default=0.4, help="Fraction of eval set to hold out for testing (0 to disable)") + parser.add_argument("--model", required=True, help="Model for improvement") + parser.add_argument("--verbose", action="store_true", help="Print progress to stderr") + parser.add_argument("--report", default="auto", help="Generate HTML report at this path (default: 'auto' for temp file, 'none' to disable)") + parser.add_argument("--results-dir", default=None, help="Save all outputs (results.json, report.html, log.txt) to a timestamped subdirectory here") + args = parser.parse_args() + + eval_set = json.loads(Path(args.eval_set).read_text()) + skill_path = Path(args.skill_path) + + if not (skill_path / "SKILL.md").exists(): + print(f"Error: No SKILL.md found at {skill_path}", file=sys.stderr) + sys.exit(1) + + name, _, _ = parse_skill_md(skill_path) + + # Set up live report path + if args.report != "none": + if args.report == "auto": + timestamp = time.strftime("%Y%m%d_%H%M%S") + live_report_path = Path(tempfile.gettempdir()) / f"skill_description_report_{skill_path.name}_{timestamp}.html" + else: + live_report_path = Path(args.report) + # Open the report immediately so the user can watch + live_report_path.write_text("

Starting optimization loop...

") + webbrowser.open(str(live_report_path)) + else: + live_report_path = None + + # Determine output directory (create before run_loop so logs can be written) + if args.results_dir: + timestamp = time.strftime("%Y-%m-%d_%H%M%S") + results_dir = Path(args.results_dir) / timestamp + results_dir.mkdir(parents=True, exist_ok=True) + else: + results_dir = None + + log_dir = results_dir / "logs" if results_dir else None + + output = run_loop( + eval_set=eval_set, + skill_path=skill_path, + description_override=args.description, + num_workers=args.num_workers, + timeout=args.timeout, + max_iterations=args.max_iterations, + runs_per_query=args.runs_per_query, + trigger_threshold=args.trigger_threshold, + holdout=args.holdout, + model=args.model, + verbose=args.verbose, + live_report_path=live_report_path, + log_dir=log_dir, + ) + + # Save JSON output + json_output = json.dumps(output, indent=2) + print(json_output) + if results_dir: + (results_dir / "results.json").write_text(json_output) + + # Write final HTML report (without auto-refresh) + if live_report_path: + live_report_path.write_text(generate_html(output, auto_refresh=False, skill_name=name)) + print(f"\nReport: {live_report_path}", file=sys.stderr) + + if results_dir and live_report_path: + (results_dir / "report.html").write_text(generate_html(output, auto_refresh=False, skill_name=name)) + + if results_dir: + print(f"Results saved to: {results_dir}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/.skills/skill-creator/scripts/utils.py b/.skills/skill-creator/scripts/utils.py new file mode 100644 index 00000000..51b6a07d --- /dev/null +++ b/.skills/skill-creator/scripts/utils.py @@ -0,0 +1,47 @@ +"""Shared utilities for skill-creator scripts.""" + +from pathlib import Path + + + +def parse_skill_md(skill_path: Path) -> tuple[str, str, str]: + """Parse a SKILL.md file, returning (name, description, full_content).""" + content = (skill_path / "SKILL.md").read_text() + lines = content.split("\n") + + if lines[0].strip() != "---": + raise ValueError("SKILL.md missing frontmatter (no opening ---)") + + end_idx = None + for i, line in enumerate(lines[1:], start=1): + if line.strip() == "---": + end_idx = i + break + + if end_idx is None: + raise ValueError("SKILL.md missing frontmatter (no closing ---)") + + name = "" + description = "" + frontmatter_lines = lines[1:end_idx] + i = 0 + while i < len(frontmatter_lines): + line = frontmatter_lines[i] + if line.startswith("name:"): + name = line[len("name:"):].strip().strip('"').strip("'") + elif line.startswith("description:"): + value = line[len("description:"):].strip() + # Handle YAML multiline indicators (>, |, >-, |-) + if value in (">", "|", ">-", "|-"): + continuation_lines: list[str] = [] + i += 1 + while i < len(frontmatter_lines) and (frontmatter_lines[i].startswith(" ") or frontmatter_lines[i].startswith("\t")): + continuation_lines.append(frontmatter_lines[i].strip()) + i += 1 + description = " ".join(continuation_lines) + continue + else: + description = value.strip('"').strip("'") + i += 1 + + return name, description, content diff --git a/.skills/writing-skills/SKILL.md b/.skills/writing-skills/SKILL.md new file mode 100644 index 00000000..514e2c4f --- /dev/null +++ b/.skills/writing-skills/SKILL.md @@ -0,0 +1,97 @@ +--- +name: writing-skills +description: "Use when creating, updating, or improving agent skills." +--- + +# Writing Skills (Excellence) + +Dispatcher for skill creation excellence. Use the decision tree below to find the right template and standards. + +## Quick Decision Tree + +### What do you need to do? + +1. **Create a NEW skill:** + - Is it simple (single file, <200 lines)? -> [Tier 1 Architecture](references/tier-1-simple/README.md) + - Is it complex (multi-concept, 200-1000 lines)? -> [Tier 2 Architecture](references/tier-2-expanded/README.md) + - Is it a massive platform (10+ products, AWS, Convex)? -> [Tier 3 Architecture](references/tier-3-platform/README.md) + +2. **Improve an EXISTING skill:** + - Fix "it's too long" -> [Modularize (Tier 3)](references/templates/tier-3-platform.md) + - Fix "AI ignores rules" -> [Anti-Rationalization](references/anti-rationalization/README.md) + - Fix "users can't find it" -> [CSO (Search Optimization)](references/cso/README.md) + +3. **Verify Compliance:** + - Check metadata/naming -> [Standards](references/standards/README.md) + - Add tests -> [Testing Guide](references/testing/README.md) + +## Component Index + +| Component | Purpose | +|-----------|---------| +| **[CSO](references/cso/README.md)** | "SEO for LLMs". How to write descriptions that trigger. | +| **[Standards](references/standards/README.md)** | File naming, YAML frontmatter, directory structure. | +| **[Anti-Rationalization](references/anti-rationalization/README.md)**| How to write rules that agents won't ignore. | +| **[Testing](references/testing/README.md)** | How to ensure your skill actually works. | + +## Templates + +- [Technique Skill](references/templates/technique.md) (How-to) +- [Reference Skill](references/templates/reference.md) (Docs) +- [Discipline Skill](references/templates/discipline.md) (Rules) +- [Pattern Skill](references/templates/pattern.md) (Design Patterns) + +## When to Use +- Creating a NEW skill from scratch +- Improving an EXISTING skill that agents ignore +- Debugging why a skill isn't being triggered +- Standardizing skills across a team + +## How It Works + +1. **Identify goal** -> Use decision tree above +2. **Select template** -> From `references/templates/` +3. **Apply CSO** -> Optimize description for discovery +4. **Add anti-rationalization** -> For discipline skills +5. **Test** -> RED-GREEN-REFACTOR cycle + +## Quick Example + +```yaml +--- +name: my-technique +description: Use when [specific symptom occurs]. +metadata: + category: technique + triggers: error-text, symptom, tool-name +--- + +# My Technique + +## When to Use +- [Symptom A] +- [Error message] +``` + +## Common Mistakes + +| Mistake | Fix | +|---------|-----| +| Description summarizes workflow | Use "Use when..." triggers only | +| No `metadata.triggers` | Add 3+ keywords | +| Generic name ("helper") | Use gerund (`creating-skills`) | +| Long monolithic SKILL.md | Split into `references/` | + +See [gotchas.md](gotchas.md) for more. + +## Pre-Deploy Checklist + +Before deploying any skill: + +- [ ] `name` field matches directory name exactly +- [ ] `SKILL.md` filename is ALL CAPS +- [ ] Description starts with "Use when..." +- [ ] `metadata.triggers` has 3+ keywords +- [ ] Total lines < 500 (use `references/` for more) +- [ ] No `@` force-loading in cross-references +- [ ] Tested with real scenarios diff --git a/.skills/writing-skills/examples.md b/.skills/writing-skills/examples.md new file mode 100644 index 00000000..3f0ee681 --- /dev/null +++ b/.skills/writing-skills/examples.md @@ -0,0 +1,236 @@ +# Skill Templates & Examples + +Complete, copy-paste templates for each skill type. + +--- + +## Template: Technique Skill + +For how-to guides that teach a specific method. + +```markdown +--- +name: technique-name +description: >- + Use when [specific symptom]. +metadata: + category: technique + triggers: error-text, symptom, tool-name +--- + +# Technique Name + +## Overview + +[1-2 sentence core principle] + +## When to Use + +- [Symptom A] +- [Symptom B] +- [Error message text] + +**NOT for:** +- [When to avoid] + +## The Problem + +\`\`\`javascript +// Bad example +function badCode() { + // problematic pattern +} +\`\`\` + +## The Solution + +\`\`\`javascript +// Good example +function goodCode() { + // improved pattern +} +\`\`\` + +## Step-by-Step + +1. [First step] +2. [Second step] +3. [Final step] + +## Quick Reference + +| Scenario | Approach | +|----------|----------| +| Case A | Solution A | +| Case B | Solution B | + +## Common Mistakes + +**Mistake 1:** [Description] +- Wrong: \`bad code\` +- Right: \`good code\` +``` + +--- + +## Template: Reference Skill + +For documentation, APIs, and lookup tables. + +```markdown +--- +name: reference-name +description: >- + Use when working with [domain]. +metadata: + category: reference + triggers: tool, api, specific-terms +--- + +# Reference Name + +## Quick Reference + +| Command | Purpose | +|---------|---------| +| \`cmd1\` | Does X | +| \`cmd2\` | Does Y | + +## Common Patterns + +**Pattern A:** +\`\`\`bash +example command +\`\`\` + +**Pattern B:** +\`\`\`bash +another example +\`\`\` + +## Detailed Docs + +For more options, run \`--help\` or see: +- patterns.md +- [examples.md](examples.md) +``` + +--- + +## Template: Discipline Skill + +For rules that agents must follow. Requires anti-rationalization techniques. + +```markdown +--- +name: discipline-name +description: >- + Use when [BEFORE violation]. +metadata: + category: discipline + triggers: new feature, code change, implementation +--- + +# Rule Name + +## Iron Law + +**[SINGLE SENTENCE ABSOLUTE RULE]** + +Violating the letter IS violating the spirit. + +## The Rule + +1. ALWAYS [step 1] +2. NEVER [step 2] +3. [Step 3] + +## Violations + +[Action before rule]? **Delete it. Start over.** + +**No exceptions:** +- Don't keep it as "reference" +- Don't "adapt" it +- Delete means delete + +## Common Rationalizations + +| Excuse | Reality | +|--------|---------| +| "Too simple" | Simple code breaks. Rule takes 30 seconds. | +| "I'll do it after" | After = never. Do it now. | +| "Spirit not ritual" | The ritual IS the spirit. | + +## Red Flags - STOP + +- [Flag 1] +- [Flag 2] +- "This is different because..." + +**All mean:** Delete. Start over. + +## Valid Exceptions + +- [Exception 1] +- [Exception 2] + +**Everything else:** Follow the rule. +``` + +--- + +## Template: Pattern Skill + +For mental models and design patterns. + +```markdown +--- +name: pattern-name +description: >- + Use when [recognizable symptom]. +metadata: + category: pattern + triggers: complexity, hard-to-follow, nested +--- + +# Pattern Name + +## The Pattern + +[1-2 sentence core idea] + +## Recognition Signs + +- [Sign that pattern applies] +- [Another sign] +- [Code smell] + +## Before + +\`\`\`typescript +// Complex/problematic +function before() { + // nested, confusing +} +\`\`\` + +## After + +\`\`\`typescript +// Clean/improved +function after() { + // flat, clear +} +\`\`\` + +## When NOT to Use + +- [Over-engineering case] +- [Simple case that doesn't need it] + +## Impact + +**Before:** [Problem metric] +**After:** [Improved metric] +``` diff --git a/.skills/writing-skills/gotchas.md b/.skills/writing-skills/gotchas.md new file mode 100644 index 00000000..df687322 --- /dev/null +++ b/.skills/writing-skills/gotchas.md @@ -0,0 +1,175 @@ +--- +description: Common pitfalls and tribal knowledge for skill creation. +metadata: + tags: [gotchas, troubleshooting, mistakes] +--- + +# Skill Writing Gotchas + +Tribal knowledge to avoid common mistakes. + +## YAML Frontmatter + +### Invalid Syntax + +```yaml +# BAD: Mixed list and map +metadata: + references: + triggers: a, b, c + - item1 + - item2 + +# GOOD: Consistent structure +metadata: + triggers: a, b, c + references: + - item1 + - item2 +``` + +### Multiline Description + +```yaml +# BAD: Line breaks create parsing errors +description: Use when creating skills. + Also for updating. + +# GOOD: Use YAML multiline syntax +description: >- + Use when creating or updating skills. + Triggers: new skill, update skill +``` + +## Naming + +### Directory Must Match `name` Field + +``` +# BAD +directory: my-skill/ +name: mySkill # Mismatch! + +# GOOD +directory: my-skill/ +name: my-skill # Exact match +``` + +### SKILL.md Must Be ALL CAPS + +``` +# BAD +skill.md +Skill.md + +# GOOD +SKILL.md +``` + +## Discovery + +### Description = Triggers, NOT Workflow + +```yaml +# BAD: Agent reads this and skips the full skill +description: Analyzes code, finds bugs, suggests fixes + +# GOOD: Agent reads full skill to understand workflow +description: Use when debugging errors or reviewing code quality +``` + +### Pre-Violation Triggers for Discipline Skills + +```yaml +# BAD: Triggers AFTER violation +description: Use when you forgot to write tests + +# GOOD: Triggers BEFORE violation +description: Use when implementing any feature, before writing code +``` + +## Token Efficiency + +### Skill Loaded Every Conversation = Token Drain + +- Frequently-loaded skills: <200 words +- All others: <500 words +- Move details to `references/` files + +### Don't Duplicate CLI Help + +```markdown +# BAD: 50 lines documenting all flags + +# GOOD: One line +Run `mytool --help` for all options. +``` + +## Anti-Rationalization (Discipline Skills Only) + +### Agents Are Smart at Finding Loopholes + +```markdown +# BAD: Trust agents will "get the spirit" +Write test before code. + +# GOOD: Close every loophole explicitly +Write test before code. + +**No exceptions:** +- Don't keep code as "reference" +- Don't "adapt" existing code +- Delete means delete +``` + +### Build Rationalization Table + +Every excuse from baseline testing goes in the table: + +| Excuse | Reality | +|--------|---------| +| "Too simple to test" | Simple code breaks. Test takes 30 seconds. | +| "I'll test after" | Tests-after prove nothing immediately. | + +## Cross-References + +### Keep References One Level Deep + +```markdown +# BAD: Nested chain (A -> B -> C) +See [patterns.md] -> which links to [advanced.md] -> which links to [deep.md] + +# GOOD: Flat (A -> B, A -> C) +See [patterns.md] and [advanced.md] +``` + +### Never Force-Load with @ + +```markdown +# BAD: Burns context immediately +@skills/my-skill/SKILL.md + +# GOOD: Agent loads when needed +See [my-skill] for details. +``` + +## Tier Selection + +### Don't Overthink Tier Choice + +```markdown +# BAD: Starting with Tier 3 "just in case" +# Result: Wasted effort, empty reference files + +# GOOD: Start with Tier 1, upgrade when needed +# Can always add references/ later +``` + +### Signals You Need to Upgrade + +| Signal | Action | +|--------|--------| +| SKILL.md > 200 lines | -> Tier 2 | +| 3+ related sub-topics | -> Tier 2 | +| 10+ products/services | -> Tier 3 | +| "I need X" vs "I want Y" | -> Tier 3 decision trees | diff --git a/.skills/writing-skills/persuasion-principles.md b/.skills/writing-skills/persuasion-principles.md new file mode 100644 index 00000000..311e57d4 --- /dev/null +++ b/.skills/writing-skills/persuasion-principles.md @@ -0,0 +1,86 @@ +# Persuasion Principles for Skill Design + +## Overview + +LLMs respond to the same persuasion principles as humans. Understanding this psychology helps you design more effective skills - not to manipulate, but to ensure critical practices are followed even under pressure. + +**Research foundation:** Meincke et al. (2025) tested 7 persuasion principles with N=28,000 AI conversations. Persuasion techniques more than doubled compliance rates (33% to 72%, p < .001). + +## The Seven Principles + +### 1. Authority +**What it is:** Deference to expertise, credentials, or official sources. + +**How it works in skills:** +- Imperative language: "YOU MUST", "Never", "Always" +- Non-negotiable framing: "No exceptions" +- Eliminates decision fatigue and rationalization + +**When to use:** +- Discipline-enforcing skills (TDD, verification requirements) +- Safety-critical practices +- Established best practices + +### 2. Commitment +**What it is:** Consistency with prior actions, statements, or public declarations. + +**How it works in skills:** +- Require announcements: "Announce skill usage" +- Force explicit choices: "Choose A, B, or C" +- Use tracking: TodoWrite for checklists + +### 3. Scarcity +**What it is:** Urgency from time limits or limited availability. + +**How it works in skills:** +- Time-bound requirements: "Before proceeding" +- Sequential dependencies: "Immediately after X" +- Prevents procrastination + +### 4. Social Proof +**What it is:** Conformity to what others do or what's considered normal. + +**How it works in skills:** +- Universal patterns: "Every time", "Always" +- Failure modes: "X without Y = failure" +- Establishes norms + +### 5. Unity +**What it is:** Shared identity, "we-ness", in-group belonging. + +**How it works in skills:** +- Collaborative language: "our codebase", "we're colleagues" +- Shared goals: "we both want quality" + +### 6. Reciprocity +**What it is:** Obligation to return benefits received. +- Use sparingly - can feel manipulative +- Rarely needed in skills + +### 7. Liking +**What it is:** Preference for cooperating with those we like. +- **DON'T USE for compliance** +- Conflicts with honest feedback culture + +## Principle Combinations by Skill Type + +| Skill Type | Use | Avoid | +|------------|-----|-------| +| Discipline-enforcing | Authority + Commitment + Social Proof | Liking, Reciprocity | +| Guidance/technique | Moderate Authority + Unity | Heavy authority | +| Collaborative | Unity + Commitment | Authority, Liking | +| Reference | Clarity only | All persuasion | + +## Ethical Use + +**Legitimate:** +- Ensuring critical practices are followed +- Creating effective documentation +- Preventing predictable failures + +**The test:** Would this technique serve the user's genuine interests if they fully understood it? + +## Research Citations + +**Cialdini, R. B. (2021).** *Influence: The Psychology of Persuasion (New and Expanded).* Harper Business. +**Meincke, L., et al. (2025).** Call Me A Jerk: Persuading AI to Comply with Objectionable Requests. University of Pennsylvania. diff --git a/.skills/writing-skills/references/anti-rationalization/README.md b/.skills/writing-skills/references/anti-rationalization/README.md new file mode 100644 index 00000000..92ee74e4 --- /dev/null +++ b/.skills/writing-skills/references/anti-rationalization/README.md @@ -0,0 +1,85 @@ +# Anti-Rationalization Guide + +Techniques for bulletproofing skills against agent rationalization. + +## The Problem + +Discipline-enforcing skills face a unique challenge: smart agents under pressure will find loopholes. + +## Technique 1: Close Every Loophole Explicitly + +Don't just state the rule - forbid specific workarounds. + +### Bad Example +```markdown +Write code before test? Delete it. +``` + +### Good Example +```markdown +Write code before test? Delete it. Start over. + +**No exceptions**: +- Don't keep it as "reference" +- Don't "adapt" it while writing tests +- Don't look at it +- Delete means delete +``` + +## Technique 2: Address "Spirit vs Letter" Arguments + +Add foundational principle early: + +```markdown +**Violating the letter of the rules is violating the spirit of the rules.** +``` + +## Technique 3: Build Rationalization Table + +| Excuse | Reality | +|--------|---------| +| "Too simple to test" | Simple code breaks. Test takes 30 seconds. | +| "I'll test after" | Tests passing immediately prove nothing. | +| "Spirit not ritual" | The letter IS the spirit. | + +## Technique 4: Create Red Flags List + +```markdown +## Red Flags - STOP and Start Over +- Code before test +- "I already manually tested it" +- "This is different because..." + +**All of these mean**: Delete code. Start over. +``` + +## Technique 5: Use Strong Language + +```markdown +# Weak (invites rationalization) +You should write tests first. + +# Strong (no wiggle room) +ALWAYS write test first. +NEVER write code before test. +``` + +## Technique 6: Provide Escape Hatch for Legitimate Cases + +```markdown +## When NOT to Use +- Spike solutions (throwaway exploratory code) +- One-time scripts deleting in 1 hour + +**Everything else**: Follow the rule. No exceptions. +``` + +## Complete Bulletproofing Checklist + +- [ ] Forbidden each specific workaround explicitly? +- [ ] Added "spirit vs letter" principle? +- [ ] Built rationalization table from baseline tests? +- [ ] Created red flags list? +- [ ] Used strong language (ALWAYS/NEVER)? +- [ ] Provided explicit escape hatch? +- [ ] Description includes pre-violation symptoms? diff --git a/.skills/writing-skills/references/cso/README.md b/.skills/writing-skills/references/cso/README.md new file mode 100644 index 00000000..f8b6bd31 --- /dev/null +++ b/.skills/writing-skills/references/cso/README.md @@ -0,0 +1,90 @@ +# CSO Guide - Claude Search Optimization + +Advanced techniques for making skills discoverable by agents. + +## The Discovery Problem + +You have 100+ skills. Agent receives a task. How does it find the RIGHT skill? + +**Answer**: The `description` field. + +## Critical Rule: Description = Triggers, NOT Workflow + +### The Trap + +When description summarizes workflow, agents take a shortcut. + +**Real example that failed**: + +```yaml +# Agent did ONE review instead of TWO +description: Code review between tasks + +# Skill body had flowchart showing TWO reviews +``` + +**Why it failed**: Agent read description, thought "code review between tasks means one review", never read the flowchart. + +**Fix**: + +```yaml +# Agent now reads full skill and follows flowchart +description: Use when executing implementation plans with independent tasks +``` + +### The Pattern + +```yaml +# BAD: Workflow summary +description: Analyzes git diff, generates commit message in conventional format + +# GOOD: Trigger conditions only +description: Use when generating commit messages or reviewing staged changes +``` + +## Token Efficiency + +**Target word counts**: +- Frequently-loaded skills: <200 words total +- Other skills: <500 words + +## Keyword Strategy + +### Error Messages +Include EXACT error text users will see. + +### Symptoms +Use words users naturally say: "flaky", "hangs", "slow", "timeout", "race condition" + +### Tools & Commands +Actual names, not descriptions: "pytest", not "Python testing" + +### Synonyms +Cover multiple ways to describe same thing: timeout/hang/freeze + +## Description Template + +```yaml +description: "Use when [SPECIFIC TRIGGER]." +metadata: + triggers: [error1], [symptom2], [tool3] +``` + +## Third Person Rule + +```yaml +# BAD: First person +description: "I can help you with async tests" + +# GOOD: Third person +description: "Handles async tests with race conditions" +``` + +## Verification Checklist + +- [ ] Description starts with "Use when..."? +- [ ] Description is <500 characters? +- [ ] Description lists ONLY triggers, not workflow? +- [ ] Includes 3+ keywords (errors/symptoms/tools)? +- [ ] Third person throughout? +- [ ] Name uses gerund or verb-first format? diff --git a/.skills/writing-skills/references/standards/README.md b/.skills/writing-skills/references/standards/README.md new file mode 100644 index 00000000..31d10a81 --- /dev/null +++ b/.skills/writing-skills/references/standards/README.md @@ -0,0 +1,87 @@ +--- +description: Standards and naming rules for creating agent skills. +metadata: + tags: [standards, naming, yaml, structure] +--- + +# Skill Development Guide + +## Directory Structure + +``` +skills/ + {skill-name}/ # kebab-case, matches `name` field + SKILL.md # Required: main skill definition + references/ # Optional: supporting documentation + README.md # Sub-topic entry point + *.md # Additional files +``` + +## Naming Rules + +| Element | Rule | Example | +|---------|------|---------| +| Directory | kebab-case, 1-64 chars | `react-best-practices` | +| `SKILL.md` | ALL CAPS, exact filename | `SKILL.md` (not `skill.md`) | +| `name` field | Must match directory name | `name: react-best-practices` | + +## SKILL.md Structure + +```markdown +--- +name: {skill-name} +description: >- + Use when [trigger condition]. +metadata: + category: technique + triggers: keyword1, keyword2, error-text +--- + +# Skill Title + +Brief description of what this skill does. + +## When to Use +- Symptom or situation A +- Symptom or situation B + +## How It Works +Step-by-step instructions or reference content. + +## Examples +Concrete usage examples. + +## Common Mistakes +What to avoid and why. +``` + +## Description Best Practices + +```yaml +# BAD: Workflow summary +description: Analyzes code, finds bugs, suggests fixes + +# GOOD: Trigger conditions only +description: Use when debugging errors or reviewing code quality. +``` + +**Rules:** +- Start with "Use when..." +- Keep under 500 characters +- Use third person + +## Context Efficiency + +| Guideline | Reason | +|-----------|--------| +| Keep SKILL.md < 500 lines | Reduces context consumption | +| Put details in supporting files | Agent reads only what's needed | +| Use tables for reference data | More compact than prose | + +## Verification Checklist + +- [ ] `name` matches directory name? +- [ ] `SKILL.md` is ALL CAPS? +- [ ] Description starts with "Use when..."? +- [ ] Under 500 lines? +- [ ] Tested with real scenarios? diff --git a/.skills/writing-skills/references/standards/metadata-standard.md b/.skills/writing-skills/references/standards/metadata-standard.md new file mode 100644 index 00000000..e3fadddb --- /dev/null +++ b/.skills/writing-skills/references/standards/metadata-standard.md @@ -0,0 +1,39 @@ +# SKILL.md Metadata Standard + +Official frontmatter fields. + +## Required Fields + +```yaml +--- +name: skill-name +description: >- + Use when [trigger condition]. +--- +``` + +| Field | Rules | +|-------|-------| +| `name` | 1-64 chars, lowercase, hyphens only, must match directory name | +| `description` | 1-1024 chars, should describe when to use | + +## Optional Fields + +```yaml +--- +name: skill-name +description: Purpose and triggers. +metadata: + category: "reference" + version: "1.0.0" +--- +``` + +## Name Validation + +```regex +^[a-z0-9]+(-[a-z0-9]+)*$ +``` + +**Valid**: `my-skill`, `git-release`, `tdd` +**Invalid**: `My-Skill`, `my_skill`, `-my-skill` diff --git a/.skills/writing-skills/references/templates/discipline.md b/.skills/writing-skills/references/templates/discipline.md new file mode 100644 index 00000000..ed54e4b4 --- /dev/null +++ b/.skills/writing-skills/references/templates/discipline.md @@ -0,0 +1,54 @@ +--- +name: discipline-name +description: >- + Use when [BEFORE violation]. +metadata: + category: discipline + triggers: new feature, code change, implementation +--- + +# Rule Name + +## Iron Law + +**[SINGLE SENTENCE ABSOLUTE RULE]** + +Violating the letter IS violating the spirit. + +## The Rule + +1. ALWAYS [step 1] +2. NEVER [step 2] +3. [Step 3] + +## Violations + +[Action before rule]? **Delete it. Start over.** + +**No exceptions:** +- Don't keep it as "reference" +- Don't "adapt" it +- Delete means delete + +## Common Rationalizations + +| Excuse | Reality | +|--------|---------| +| "Too simple" | Simple code breaks. Rule takes 30 seconds. | +| "I'll do it after" | After = never. Do it now. | +| "Spirit not ritual" | The ritual IS the spirit. | + +## Red Flags - STOP + +- [Flag 1] +- [Flag 2] +- "This is different because..." + +**All mean:** Delete. Start over. + +## Valid Exceptions + +- [Exception 1] +- [Exception 2] + +**Everything else:** Follow the rule. diff --git a/.skills/writing-skills/references/templates/pattern.md b/.skills/writing-skills/references/templates/pattern.md new file mode 100644 index 00000000..777bc269 --- /dev/null +++ b/.skills/writing-skills/references/templates/pattern.md @@ -0,0 +1,48 @@ +--- +name: pattern-name +description: >- + Use when [recognizable symptom]. +metadata: + category: pattern + triggers: complexity, hard-to-follow, nested +--- + +# Pattern Name + +## The Pattern + +[1-2 sentence core idea] + +## Recognition Signs + +- [Sign that pattern applies] +- [Another sign] +- [Code smell] + +## Before + +```typescript +// Complex/problematic +function before() { + // nested, confusing +} +``` + +## After + +```typescript +// Clean/improved +function after() { + // flat, clear +} +``` + +## When NOT to Use + +- [Over-engineering case] +- [Simple case that doesn't need it] + +## Impact + +**Before:** [Problem metric] +**After:** [Improved metric] diff --git a/.skills/writing-skills/references/templates/reference.md b/.skills/writing-skills/references/templates/reference.md new file mode 100644 index 00000000..66342eee --- /dev/null +++ b/.skills/writing-skills/references/templates/reference.md @@ -0,0 +1,35 @@ +--- +name: reference-name +description: >- + Use when working with [domain]. +metadata: + category: reference + triggers: tool, api, specific-terms +--- + +# Reference Name + +## Quick Reference + +| Command | Purpose | +|---------|---------| +| `cmd1` | Does X | +| `cmd2` | Does Y | + +## Common Patterns + +**Pattern A:** +```bash +example command +``` + +**Pattern B:** +```bash +another example +``` + +## Detailed Docs + +For more options, run `--help` or see: +- patterns.md +- examples.md diff --git a/.skills/writing-skills/references/templates/technique.md b/.skills/writing-skills/references/templates/technique.md new file mode 100644 index 00000000..336c62a7 --- /dev/null +++ b/.skills/writing-skills/references/templates/technique.md @@ -0,0 +1,59 @@ +--- +name: technique-name +description: Use when [specific symptom]. +metadata: + category: technique + triggers: error-text, symptom, tool-name +--- + +# Technique Name + +## Overview + +[1-2 sentence core principle] + +## When to Use + +- [Symptom A] +- [Symptom B] +- [Error message text] + +**NOT for:** +- [When to avoid] + +## The Problem + +```javascript +// Bad example +function badCode() { + // problematic pattern +} +``` + +## The Solution + +```javascript +// Good example +function goodCode() { + // improved pattern +} +``` + +## Step-by-Step + +1. [First step] +2. [Second step] +3. [Final step] + +## Quick Reference + +| Scenario | Approach | +|----------|----------| +| Case A | Solution A | +| Case B | Solution B | + +## Common Mistakes + +**Mistake 1:** [Description] +- Wrong: `bad code` +- Right: `good code` diff --git a/.skills/writing-skills/references/templates/tier-3-platform.md b/.skills/writing-skills/references/templates/tier-3-platform.md new file mode 100644 index 00000000..4863345e --- /dev/null +++ b/.skills/writing-skills/references/templates/tier-3-platform.md @@ -0,0 +1,17 @@ +# Platform Name Skill + +Template for complex Tier 3 skills. + +## Structure + +``` +skill/ + SKILL.md # Dispatcher + references/ + topic/ + README.md # Overview + api.md # API Reference + config.md # Configuration + patterns.md # Recipes + gotchas.md # Critical Errors +``` diff --git a/.skills/writing-skills/references/testing/README.md b/.skills/writing-skills/references/testing/README.md new file mode 100644 index 00000000..e0893404 --- /dev/null +++ b/.skills/writing-skills/references/testing/README.md @@ -0,0 +1,66 @@ +# Testing Guide - TDD for Skills + +Complete methodology for testing skills using RED-GREEN-REFACTOR cycle. + +## Testing All Skill Types + +### Discipline-Enforcing Skills (rules/requirements) + +**Test with**: +- Academic questions: Do they understand the rules? +- Pressure scenarios: Do they comply under stress? +- Multiple pressures combined: time + sunk cost + exhaustion + +**Success criteria**: Agent follows rule under maximum pressure + +### Technique Skills (how-to guides) + +**Test with**: +- Application scenarios: Can they apply the technique correctly? +- Variation scenarios: Do they handle edge cases? +- Missing information tests: Do instructions have gaps? + +**Success criteria**: Agent successfully applies technique to new scenario + +### Pattern Skills (mental models) + +**Test with**: +- Recognition scenarios: Do they recognize when pattern applies? +- Counter-examples: Do they know when NOT to apply? + +**Success criteria**: Agent correctly identifies when/how to apply pattern + +### Reference Skills (documentation/APIs) + +**Test with**: +- Retrieval scenarios: Can they find the right information? +- Gap testing: Are common use cases covered? + +**Success criteria**: Agent finds and correctly applies reference information + +## Pressure Types for Testing + +| Pressure | Example | +|----------|---------| +| Time | "You have 5 minutes to complete this task" | +| Sunk cost | "You already spent 2 hours on this" | +| Authority | "Senior developer said to skip tests" | +| Exhaustion | "This is the 10th task today" | + +## Complete Test Checklist + +**Baseline (RED)**: +- [ ] Designed 3+ pressure scenarios +- [ ] Ran scenarios WITHOUT skill +- [ ] Documented verbatim agent responses + +**Implementation (GREEN)**: +- [ ] Skill addresses SPECIFIC baseline failures +- [ ] Re-ran scenarios WITH skill +- [ ] Agent complied in all scenarios + +**Bulletproofing (REFACTOR)**: +- [ ] Tested with combined pressures +- [ ] Found and documented new rationalizations +- [ ] Added explicit counters +- [ ] Re-tested until no more loopholes diff --git a/.skills/writing-skills/references/tier-1-simple/README.md b/.skills/writing-skills/references/tier-1-simple/README.md new file mode 100644 index 00000000..6f049adf --- /dev/null +++ b/.skills/writing-skills/references/tier-1-simple/README.md @@ -0,0 +1,30 @@ +--- +description: When to use Tier 1 (Simple) skill architecture. +metadata: + tags: [tier-1, simple, single-file] +--- + +# Tier 1: Simple Skills + +Single-file skills for focused, specific purposes. + +## When to Use + +- **Single concept**: One technique, one pattern, one reference +- **Under 200 lines**: Can fit comfortably in one file +- **No complex decision logic**: User knows exactly what they need +- **Frequently loaded**: Needs minimal token footprint + +## Structure + +``` +my-skill/ + SKILL.md # Everything in one file +``` + +## Checklist + +- [ ] Fits in <200 lines +- [ ] Single focused purpose +- [ ] No need for `references/` directory +- [ ] Description uses "Use when..." pattern diff --git a/.skills/writing-skills/references/tier-2-expanded/README.md b/.skills/writing-skills/references/tier-2-expanded/README.md new file mode 100644 index 00000000..9e31e8f7 --- /dev/null +++ b/.skills/writing-skills/references/tier-2-expanded/README.md @@ -0,0 +1,52 @@ +--- +description: When to use Tier 2 (Expanded) skill architecture. +metadata: + tags: [tier-2, expanded, multi-file] +--- + +# Tier 2: Expanded Skills + +Multi-file skills for complex topics with multiple sub-concepts. + +## When to Use + +- **Multiple related concepts**: Needs separation of concerns +- **200-1000 lines total**: Too big for one file +- **Needs reference files**: Patterns, examples, troubleshooting +- **Cross-linking**: Users need to navigate between sub-topics + +## Structure + +``` +my-skill/ + SKILL.md # Overview + navigation + references/ + core/ + README.md # Main concept + patterns/ + README.md # Usage patterns + troubleshooting/ + README.md # Common issues +``` + +## Progressive Disclosure + +1. **Metadata** (~100 tokens): Name + description loaded at startup +2. **SKILL.md** (<500 lines): Decision tree + index +3. **References** (as needed): Loaded only when user navigates + +## Key Differences from Tier 1 + +| Aspect | Tier 1 | Tier 2 | +|--------|--------|--------| +| Files | 1 | 5-20 | +| Total lines | <200 | 200-1000 | +| Decision logic | None | Simple tree | +| Token cost | Minimal | Medium (progressive) | + +## Checklist + +- [ ] SKILL.md has clear navigation links +- [ ] Each `references/` subdir has README.md +- [ ] No circular references between files +- [ ] Decision tree points to specific files diff --git a/.skills/writing-skills/references/tier-3-platform/README.md b/.skills/writing-skills/references/tier-3-platform/README.md new file mode 100644 index 00000000..224c709d --- /dev/null +++ b/.skills/writing-skills/references/tier-3-platform/README.md @@ -0,0 +1,51 @@ +--- +description: When to use Tier 3 (Platform) skill architecture for large platforms. +metadata: + tags: [tier-3, platform, enterprise] +--- + +# Tier 3: Platform Skills + +Enterprise-grade skills for entire platforms (AWS, Cloudflare, Convex, etc). + +## When to Use + +- **Entire platform**: 10+ products/services +- **1000+ lines total**: Would overwhelm context if monolithic +- **Complex decision logic**: Users start with "I need X" not "I want product Y" + +## The 5-File Pattern + +Each product directory has exactly 5 files: + +| File | Purpose | When to Load | +|------|---------|--------------| +| `README.md` | Overview, when to use | Always first | +| `api.md` | Runtime APIs, methods | Implementing features | +| `configuration.md` | Config, environment | Setting up | +| `patterns.md` | Common workflows | Best practices | +| `gotchas.md` | Pitfalls, limits | Debugging | + +## Decision Trees + +```markdown +Need to store data? + Simple key-value -> kv/ + Relational queries -> d1/ + Large files/blobs -> r2/ + Per-user state -> durable-objects/ +``` + +## Progressive Disclosure in Action + +- **Startup**: Only name + description (~100 tokens) +- **Activation**: SKILL.md with trees (<5000 tokens) +- **Navigation**: One product's 5 files (as needed) + +## Checklist + +- [ ] SKILL.md contains ONLY decision trees + index +- [ ] Each product has exactly 5 files +- [ ] Decision trees cover all "I need X" scenarios +- [ ] Cross-references stay one level deep +- [ ] Every product has `gotchas.md` diff --git a/.skills/writing-skills/testing-skills-with-subagents.md b/.skills/writing-skills/testing-skills-with-subagents.md new file mode 100644 index 00000000..7abecfca --- /dev/null +++ b/.skills/writing-skills/testing-skills-with-subagents.md @@ -0,0 +1,85 @@ +# Testing Skills With Subagents + +**Load this reference when:** creating or editing skills, before deployment, to verify they work under pressure and resist rationalization. + +## Overview + +**Testing skills is just TDD applied to process documentation.** + +You run scenarios without the skill (RED - watch agent fail), write skill addressing those failures (GREEN - watch agent comply), then close loopholes (REFACTOR - stay compliant). + +**Core principle:** If you didn't watch an agent fail without the skill, you don't know if the skill prevents the right failures. + +## When to Use + +Test skills that: +- Enforce discipline (TDD, testing requirements) +- Have compliance costs (time, effort, rework) +- Could be rationalized away ("just this once") +- Contradict immediate goals (speed over quality) + +Don't test: +- Pure reference skills (API docs, syntax guides) +- Skills without rules to violate +- Skills agents have no incentive to bypass + +## TDD Mapping for Skill Testing + +| TDD Phase | Skill Testing | What You Do | +|-----------|---------------|-------------| +| **RED** | Baseline test | Run scenario WITHOUT skill, watch agent fail | +| **Verify RED** | Capture rationalizations | Document exact failures verbatim | +| **GREEN** | Write skill | Address specific baseline failures | +| **Verify GREEN** | Pressure test | Run scenario WITH skill, verify compliance | +| **REFACTOR** | Plug holes | Find new rationalizations, add counters | +| **Stay GREEN** | Re-verify | Test again, ensure still compliant | + +## RED Phase: Baseline Testing (Watch It Fail) + +**Goal:** Run test WITHOUT the skill - watch agent fail, document exact failures. + +**Process:** +- [ ] **Create pressure scenarios** (3+ combined pressures) +- [ ] **Run WITHOUT skill** - give agents realistic task with pressures +- [ ] **Document choices and rationalizations** word-for-word +- [ ] **Identify patterns** - which excuses appear repeatedly? +- [ ] **Note effective pressures** - which scenarios trigger violations? + +## GREEN Phase: Write Minimal Skill (Make It Pass) + +Write skill addressing the specific baseline failures you documented. Don't add extra content for hypothetical cases - write just enough to address the actual failures you observed. + +Run same scenarios WITH skill. Agent should now comply. + +If agent still fails: skill is unclear or incomplete. Revise and re-test. + +## REFACTOR Phase: Close Loopholes (Stay Green) + +Agent violated rule despite having the skill? Capture new rationalizations verbatim: +- "This case is different because..." +- "I'm following the spirit not the letter" +- "Being pragmatic means adapting" +- "Deleting X hours is wasteful" + +**Document every excuse.** These become your rationalization table. + +## Testing Checklist (TDD for Skills) + +**RED Phase:** +- [ ] Created pressure scenarios (3+ combined pressures) +- [ ] Ran scenarios WITHOUT skill (baseline) +- [ ] Documented agent failures and rationalizations verbatim + +**GREEN Phase:** +- [ ] Wrote skill addressing specific baseline failures +- [ ] Ran scenarios WITH skill +- [ ] Agent now complies + +**REFACTOR Phase:** +- [ ] Identified NEW rationalizations from testing +- [ ] Added explicit counters for each loophole +- [ ] Updated rationalization table +- [ ] Updated red flags list +- [ ] Re-tested - agent still complies +- [ ] Meta-tested to verify clarity +- [ ] Agent follows rule under maximum pressure diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ed3590b1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,126 @@ +# AGENTS.md + +## Project +- Minecraft Console Client (MCC) is a cross-platform text/TUI client for Minecraft Java Edition. +- Primary scope: connect to servers, send chat and commands, receive text, automate gameplay/admin tasks, and extend behavior through built-in bots or runtime C# scripts. +- Secondary scope: protocol/version adaptation tooling, docs site, legacy GUI wrapper, and debug tooling. +- Decompiled server source for both the old and new MC versions in `$MCC_REPO/MinecraftOfficial/-decompiled/` + +## Build / Run +- Init submodules first: `git submodule update --init --recursive` +- Build for local development: `source tools/mcc-env.sh && mcc-build` +- Publish (matches CI shape): `source tools/mcc-env.sh && mcc-publish --rid ` +- Run/debug from source: `source tools/mcc-env.sh && mcc-debug -v 1.21.11 --file-input` +- Docs: `cd docs && npm install && npm run docs:dev` or `npm run docs:build` +- Docker: `cd Docker && docker build -t minecraft-console-client:latest .` +- Tests: no dedicated test project is present in the main solution. +- Current state: the solution builds after submodule init, but the underlying .NET build emits many analyzer and NuGet vulnerability warnings; treat them as real. +- Server roots: `tools/` helpers look for server jars under `MinecraftOfficial/downloads//` by default, but also support an external root via the `MCC_SERVERS` environment variable. +- Multi-version testing: tmux-based local server sessions are shared state. Run cross-version test matrices sequentially unless you have explicit per-version isolation. A server logging `Done` does not guarantee immediate RCON availability; retry RCON setup commands. +- Automated test configs: for repeated or matrix test runs, prefer generating a temporary MCC config per run instead of reusing the repo-root `MinecraftClient.ini`, to avoid leaking state between runs. +- For agent-driven local development, prefer `mcc-build`, `mcc-publish`, `mcc-build-clean`, `mcc-debug`, `mcc-run`, and `mcc-tui` over raw `dotnet build`, `dotnet publish`, or `dotnet run`, so worktree-local temp build routing stays active. + +## Architecture +- `Program` bootstraps console I/O, TOML config, auth/session state, MC version selection, Forge detection, then creates `McClient`. +- `McClient` is the live session runtime: TCP client, selected protocol handler, Brigadier command dispatcher, loaded bots, world/inventory/entity state, queued chat, movement/pathing, reconnect flow. +- `Protocol/` is the network/auth boundary. `ProtocolHandler` maps Minecraft versions to protocol numbers and selects either `Protocol16Handler` (1.4.6-1.6.4) or `Protocol18Handler` (1.7.2+). +- `Scripting/ChatBot` is the extension boundary. Built-in bots and `/script` C# bots share the same event/tick API. +- Main runtime flow: console input -> internal Brigadier command or server chat; packets -> protocol handler -> `McClient` state update -> bot events; `OnUpdate()` (20 TPS) drives bot ticks, delayed work, chat cooldowns, movement, and main-thread tasks. + +## Technology Stack +- Main app: C#, .NET 10, nullable enabled. +- Command system: `Brigadier.NET`. +- Config: TOML via `Samboy063.Tomlet`. +- Runtime scripting: Roslyn (`Microsoft.CodeAnalysis.CSharp`) with in-memory compilation. +- Networking/auth: custom Minecraft protocol handlers, DNS SRV lookup (`DnsClient`), Forge/session/profile-key support. +- Integrations: `DSharpPlus`, `Telegram.Bot`, `MessagePack`, `Magick.NET`, `Sentry`. +- Docs site: VuePress 2 (`docs/package.json`). +- Tooling: Docker, GitHub Actions, Python 3.10+ scripts under `tools/` for palette/version generation. +- Legacy UI: `MinecraftClientGUI` is a separate .NET Framework 4.0 WinForms wrapper, not the main runtime. + +## Version Support +Feature columns mean: +- Inventory: `/inventory` plus inventory/container bot APIs +- Movement: terrain handling, `/move`, and movement/pathing bots +- Entity: entity tracking and entity-driven bot events + +| Minecraft | Protocol path | Inventory | Movement | Entity | Notes | +| --- | --- | --- | --- | --- | --- | +| 1.4.6-1.6.4 | `Protocol16Handler` | No | No | No | Core login/chat only | +| 1.7.2-1.7.10 | `Protocol18Handler` | No | Yes | No | Pre-1.8 special case | +| 1.8-1.9.4 | `Protocol18Handler` | Partial / docs conflict | Yes | Yes | Runtime gates allow 1.8+, but docs still warn inventory is unsupported through 1.9 | +| 1.10-1.12.2 | `Protocol18Handler` | Yes | Yes | Yes | Pre-flattening palettes | +| 1.13-1.19.2 | `Protocol18Handler` | Yes | Yes | Yes | Flattened block/item/entity palettes | +| 1.19.3-1.20.4 | `Protocol18Handler` | Yes | Yes | Yes | Newer chat/signing and palette splits | +| 1.20.6-1.21.4 | `Protocol18Handler` | Yes | Yes | Yes | Registry-driven world/attribute handling | +| 1.21.5-1.21.8 | `Protocol18Handler` | Yes | Yes | Yes | 1.21.7/1.21.8 reuse 1.21.6 block/entity palettes in code | +| 1.21.9-1.21.10 | `Protocol18Handler` | Yes | Yes | Yes | Version tools prefer server data reports since 1.21.9 | +| 1.21.11 | `Protocol18Handler` | Yes | Yes | Yes | Own entity/item/metadata palettes; blocks reuse 1.21.9 palette | +| 26.1 | `Protocol18Handler` | Yes | Yes | Yes | Latest coded support; new Minecraft version naming scheme | + +Notes: +- Declared code range is `1.4.6` to `26.1`. +- Human docs are stale in places and sometimes stop at older ranges; prefer code when docs and code disagree. +- Movement/pathing limits called out in docs still apply: no swimming, no knockback. The `Physics/` engine adds vanilla-accurate collision and movement but some edge cases remain. + +## Module Map +### Core Runtime + +| Module | What It Owns | Important Files | +| --- | --- | --- | +| `MinecraftClient/` | Main `net10.0` runtime assembly and the best starting point. `Program.cs` owns startup, config load/writeback, CLI handling, auth/version selection, update/data-generation entrypoints, and restart/failure flow. `McClient.cs` owns the live session runtime: protocol handler ownership, command dispatch, bot lifecycle, world/inventory/entity state, queued chat, movement ticks, reconnect/disconnect logic, and the main-thread invoke queue. `Settings.cs` defines the TOML schema and runtime/internal overrides used across the app. | `Program.cs`, `McClient.cs`, `Settings.cs`, `ConsoleIO.cs`, `Command.cs`, `UpgradeHelper.cs`, `AutoTimeout.cs` | +| `MinecraftClient/Protocol/` | Network/auth/session boundary. `ProtocolHandler.cs` does DNS SRV lookup, server ping/version detection, MC-version to protocol mapping, and handler selection. `Protocol16.cs` and `Protocol18.cs` implement the packet flow for legacy and modern versions. `Protocol18Terrain.cs` decodes chunk sections/biomes into `World`. `DataTypes.cs` is the low-level reader/writer layer for VarInts, metadata, NBT-like structures, and packet fields. `Message/`, `ProfileKey/`, `Session/`, `Handlers/Forge/`, `Handlers/PacketPalettes/`, `Handlers/Packet/`, and `Handlers/StructuredComponents/` cover chat/signing, cached auth, Forge, packet IDs, packet-level parsing, and 1.20.6+ item components with versioned registries under `StructuredComponents/Registries/`. | `Protocol/ProtocolHandler.cs`, `Protocol/Handlers/Protocol16.cs`, `Protocol/Handlers/Protocol18.cs`, `Protocol/Handlers/Protocol18Terrain.cs`, `Protocol/Handlers/DataTypes.cs`, `Protocol/Message/ChatParser.cs`, `Protocol/MicrosoftAuthentication.cs`, `Protocol/MojangAPI.cs` | +| `MinecraftClient/Mapping/` | World model, terrain storage, movement logic, and versioned block/entity metadata. `World.cs` stores chunk columns, dimension data, and 1.20.6+ registry-derived dimension/attribute mappings. `Chunk*`, `Block.cs`, and `Location.cs` are the terrain primitives. `Movement.cs` contains step generation, gravity/on-ground checks, and path execution support. `Material.cs` plus `BlockPalettes/*.cs` map block-state IDs to MCC materials. `Entity.cs`, `EntityType.cs`, `EntityPalettes/*.cs`, `EntityMetadataPalette.cs`, and `EntityMetadataPalettes/*.cs` do the same for entities and metadata serializers. | `Mapping/World.cs`, `Mapping/ChunkColumn.cs`, `Mapping/Chunk.cs`, `Mapping/Block.cs`, `Mapping/Location.cs`, `Mapping/Movement.cs`, `Mapping/RaycastHelper.cs`, `Mapping/Material.cs`, `Mapping/Entity.cs`, `Mapping/EntityType.cs` | +| `MinecraftClient/Inventory/` | Inventory/container snapshots, item decoding, and versioned item registries. `Container.cs` models player inventories and server windows, including slot contents and container properties. `Item.cs` bridges older NBT-based items with 1.20.6+ structured components. `ItemType.cs` plus `ItemPalettes/*.cs` provide version-specific item ID mapping. Enchantment, effects, and villager-trade files add higher-level semantics on top of raw inventory data. | `Inventory/Container.cs`, `Inventory/ContainerType.cs`, `Inventory/Item.cs`, `Inventory/ItemMovingHelper.cs`, `Inventory/ItemType.cs`, `Inventory/ItemPalettes/*.cs`, `Inventory/EnchantmentMapping.cs`, `Inventory/VillagerTrade.cs` | +| `MinecraftClient/Physics/` | Vanilla-accurate per-tick physics engine. `PlayerPhysics.cs` mirrors vanilla `Entity.move()`, `LivingEntity.aiStep()/travel()`, and `Player.travel()` logic at 20 TPS, handling ground/air/water/lava/creative-fly travel, jumping, sprint-jump boost, climbing, sneak-edge-detection, friction, drag, gravity, slow-falling, and levitation. `CollisionDetector.cs` resolves full AABB collisions against the block world including step-up, mirroring vanilla axis-separated resolution. `BlockShapes.cs` maps block-state IDs to collision AABBs using PrismarineJS data from `BlockShapeData.json`. `Vec3d.cs` and `Aabb.cs` provide the geometric primitives. `MovementInput.cs` captures player input state. | `Physics/PlayerPhysics.cs`, `Physics/PhysicsConsts.cs`, `Physics/CollisionDetector.cs`, `Physics/BlockShapes.cs`, `Physics/BlockShapeData.json`, `Physics/Vec3d.cs`, `Physics/Aabb.cs`, `Physics/MovementInput.cs` | + +### Commands And Extensions + +| Module | What It Owns | Important Files | +| --- | --- | --- | +| `MinecraftClient/Commands/` and `MinecraftClient/CommandHandler/` | Internal MCC command system built on Brigadier. Commands are discovered by reflection from `MinecraftClient.Commands` in `McClient.LoadCommands()`. Each file in `Commands/` registers one internal command. `ArgumentType/*.cs` provides typed Brigadier arguments and completion sources for accounts, bots, items, locations, scripts, inventories, and more. `Patch/*.cs` carries MCC-specific Brigadier extensions, and `CmdResult.cs` is the command execution result object. | `Command.cs`, `Commands/*.cs`, `CommandHandler/MccArguments.cs`, `CommandHandler/CmdResult.cs`, `CommandHandler/ArgumentType/*.cs`, `CommandHandler/Patch/*.cs` | +| `MinecraftClient/ChatBots/` | Built-in bots and bridges loaded from config through `McClient.RegisterBots()`. The folder mixes gameplay automation (`AutoAttack`, `AutoDig`, `AutoEat`, `AutoFishing`, `Farmer`), utility/logging bots (`ChatLog`, `PlayerListLogger`, `Alerts`), bridges (`DiscordBridge`, `TelegramBridge`, `RemoteControl`), and tooling like `ScriptScheduler`, `Map`, and `ReplayCapture`. | `ChatBots/AutoRelog.cs`, `ChatBots/Farmer.cs`, `ChatBots/FollowPlayer.cs`, `ChatBots/ItemsCollector.cs`, `ChatBots/Map.cs`, `ChatBots/RemoteControl.cs`, `ChatBots/ScriptScheduler.cs`, `ChatBots/DiscordBridge.cs`, `ChatBots/TelegramBridge.cs`, `ChatBots/ReplayCapture.cs` | +| `MinecraftClient/Scripting/` | Shared extension boundary for compiled bots and runtime C# scripts. `ChatBot.cs` is the main bot API and lifecycle surface. Built-in bots and `/script` bots use the same event model. `CSharpRunner.cs` parses `//MCCScript` files, compiles them with Roslyn, caches assemblies, and executes them through `CSharpAPI`. `DynamicRun/Builder/*` handles in-memory compilation/load-context plumbing, while `BotMovementLock.cs` coordinates movement ownership between automation pieces. | `Scripting/ChatBot.cs`, `Scripting/CSharpRunner.cs`, `Scripting/BotMovementLock.cs`, `Scripting/AssemblyResolver.cs`, `Scripting/DynamicRun/Builder/Compiler.cs`, `Scripting/DynamicRun/Builder/CompileRunner.cs` | +| `MinecraftClient/config/` | Sample runtime assets excluded from compilation. This is the examples/staging area for end-user scripts and standalone bots. `sample-script*.cs` shows supported `/script` patterns (basic, chatbot, world access, HTTP requests, tasks, PM forwarding, extended), while `config/ChatBots/*.cs` are copy/adapt examples rather than built-in bots. | `config/README.md`, `config/sample-script.cs`, `config/sample-script-with-chatbot.cs`, `config/sample-script-with-world-access.cs`, `config/sample-script-with-http-request.cs`, `config/sample-script-with-task.cs`, `config/ChatBots/*.cs` | +| `ConsoleInteractive/` | Required git submodule for richer line editing and console UI. MCC uses the submodule's `ConsoleReader`, `ConsoleWriter`, and suggestion UI from `ConsoleIO.cs` and `McClient.cs` when `BasicIO` is not enabled. | `ConsoleInteractive/README.md`, `ConsoleInteractive/ConsoleInteractive/ConsoleInteractive.sln` | + +### Support And Tooling + +| Module | What It Owns | Important Files | +| --- | --- | --- | +| `MinecraftClient/Logger/`, `MinecraftClient/Proxy/`, `MinecraftClient/Crypto/`, `MinecraftClient/Resources/`, `MinecraftClient/WinAPI/` | Support subsystems under the main app. Logging supports console/file output plus regex filtering. `ProxyHandler.cs` routes update/login/in-game traffic through HTTP or SOCKS proxies. `Crypto/` implements the stream ciphers needed for online-mode protocol encryption. `Resources/` contains UI strings, generated translation accessors, config help text, icons, and embedded Minecraft asset data. `WinAPI/` contains small Windows-only console helpers. | `Logger/FilteredLogger.cs`, `Logger/FileLogLogger.cs`, `Proxy/ProxyHandler.cs`, `Crypto/CryptoHandler.cs`, `Crypto/AesCfb8Stream.cs`, `Resources/Translations/Translations.resx`, `Resources/ConfigComments/ConfigComments.resx`, `Resources/en_us.json`, `WinAPI/ConsoleIcon.cs` | +| `docs/` | VuePress documentation site. `.vuepress/config.ts` sets bundler, theme, plugins, and redirects. `.vuepress/configs/**` holds locale and nav wiring. `guide/*.md` contains the user-facing install, usage, bot, and scripting docs. | `docs/.vuepress/config.ts`, `docs/.vuepress/configs/**`, `docs/guide/README.md`, `docs/guide/configuration.md`, `docs/guide/chat-bots.md`, `docs/guide/creating-bots.md`, `docs/guide/creating-text-script.md`, `docs/guide/ai-assisted-development.md` | +| `tools/` | Python helpers for Minecraft version adaptation and palette generation. `README.md` is the authoritative workflow. `diff_registries.py` compares versions and validates decompiled data against server reports. The `gen_*` scripts emit the versioned palette source files consumed by `Protocol/`, `Mapping/`, `Inventory/`, and `Physics/`. | `tools/README.md`, `tools/diff_registries.py`, `tools/gen_block_palette.py`, `tools/gen_item_palette.py`, `tools/gen_entity_palette.py`, `tools/gen_entity_metadata_palette.py`, `tools/gen_block_shapes.py`, `tools/gen_command_argument_registry.py` | +| `DebugTools/` | Standalone packet/proxy debugging utilities for inspecting traffic and compression behavior outside the main client runtime. | `DebugTools/MinecraftClientProxy/Program.cs`, `DebugTools/MinecraftClientProxy/PacketProxy.cs`, `DebugTools/MinecraftClientProxy/ZlibUtils.cs` | +| `MinecraftClientGUI/` | Legacy Windows GUI wrapper around the console app. WinForms shell that launches and communicates with the console executable; not part of the main `net10.0` runtime path. | `MinecraftClientGUI/Program.cs`, `MinecraftClientGUI/Form1.cs`, `MinecraftClientGUI/Form1.Designer.cs`, `MinecraftClientGUI/MinecraftClient.cs` | + +## Engineering Guidance + +Read `docs/guide/ai-assisted-development.md` before starting development work on MCC. It documents the full build-run-test loop, local server harness, repository tools, and standard workflows. + +### DO +- Keep startup/config/auth logic in `Program` and connection runtime logic in `McClient` or `Protocol/*`. +- Update version support holistically: protocol constants, version mapping, packet palette, block palette, item palette, entity palette, metadata palette, and routing switches. +- Use `tools/` and authoritative server data reports when adapting to new Minecraft versions. +- Guard optional subsystems with `GetTerrainEnabled()`, `GetInventoryEnabled()`, and `GetEntityHandlingEnabled()` before using them. +- For built-in bots, wire all pieces together: bot class, `Settings.ChatBotConfigHealper`, and `McClient.RegisterBots()`. +- Keep `Initialize()` for setup/prereq checks and `AfterGameJoined()` for sending chat or commands. +- Normalize inbound chat with `GetVerbatim()` before `IsChatMessage()` / `IsPrivateMessage()`. +- Clean up commands, plugin channels, threads, timers, and movement locks in `OnUnload()`. +- Prefer nullable-aware code, pattern matching, `ArgumentNullException.ThrowIfNull`, `Try*` APIs for expected failures, and `InvokeOnMainThread()` for cross-thread state changes. +- Use modern C# 14 features. +- Use provided skills proactively depending on the context, read their descriptions to determine when to use them. +- All user-facing text (log messages, command output, TUI labels, notifications, help text, error messages) **must** go through the translation system: add entries to `Translations.resx` + `Translations.Designer.cs`, then reference `Translations.key_name` in code. Never hardcode user-visible strings directly in `.cs` files. Use `string.Format(Translations.key, ...)` for parameterized messages. Translation keys follow dot-delimited naming: `..` (e.g. `cmd.inventory.tui_opened`, `tui.inventory.controls`). Pure technical identifiers (class names, protocol constants, color codes) are exempt. + +### DON'T +- Don't update only `MCVer2ProtocolVersion()` or only one palette file when adding a new Minecraft version. +- Don't send chat in `Initialize()`. +- Don't mutate inventory snapshots and expect server-side effects; use handler APIs/window actions. +- Don't bypass Brigadier with ad hoc command parsing. +- Never modify `ConsoleInteractive/`; treat it as an external required submodule. +- Don't start background workers when `Update()` or delayed tasks are sufficient; if you must, stop them on unload/disconnect. +- Don't leave movement locks, plugin channels, or dispatcher registrations behind. +- Don't trust older docs over current code for supported versions or feature gates. When AGENTS.md, skills, and older docs disagree, prefer current code and current tool behavior, then update the stale source. +- Don't hardcode user-facing strings (messages, labels, help text) directly in source code; always use `Translations.*` resources so the text can be localized. +- Never use "—" ("em dash"), unless specifically being instructed to do so! +- Never generate MCC config files (`.ini`) in the repo root. When `dotnet run --project MinecraftClient -- --help` is used to generate a config template, it writes `MinecraftClient.ini` to the current directory. Always run this command from a system temp directory (e.g. `mktemp -d`) or use `prepare_offline_mcc_config.sh` which already handles output routing. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..23562a5f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +Read @AGENTS.md \ No newline at end of file diff --git a/ConsoleInteractive b/ConsoleInteractive index db2be2a7..ff6d2129 160000 --- a/ConsoleInteractive +++ b/ConsoleInteractive @@ -1 +1 @@ -Subproject commit db2be2a7f8ea71c734ebeff6314fd2fdec73f4fc +Subproject commit ff6d2129e9f1e0fc6c7032741bdc42a2f0fa263e diff --git a/DebugTools/MccMcpSampleClient/MccMcpSampleClient.csproj b/DebugTools/MccMcpSampleClient/MccMcpSampleClient.csproj new file mode 100644 index 00000000..0edf5faa --- /dev/null +++ b/DebugTools/MccMcpSampleClient/MccMcpSampleClient.csproj @@ -0,0 +1,14 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/DebugTools/MccMcpSampleClient/Program.cs b/DebugTools/MccMcpSampleClient/Program.cs new file mode 100644 index 00000000..15ac2b70 --- /dev/null +++ b/DebugTools/MccMcpSampleClient/Program.cs @@ -0,0 +1,773 @@ +using System.Diagnostics; +using System.Text.Json; +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol; + +string endpoint = Environment.GetEnvironmentVariable("MCC_MCP_ENDPOINT") ?? "http://127.0.0.1:33333/mcp"; +bool useStdio = string.Equals(Environment.GetEnvironmentVariable("MCC_MCP_USE_STDIO"), "1", StringComparison.Ordinal); +string? mcpAuthToken = Environment.GetEnvironmentVariable("MCC_MCP_AUTH_TOKEN"); +string repoRoot = FindRepoRoot(); +string rconScript = Path.Combine(repoRoot, "tools", "mc-rcon.sh"); +string rconPort = Environment.GetEnvironmentVariable("MCC_RCON_PORT") ?? "25575"; +string rconPassword = Environment.GetEnvironmentVariable("MCC_RCON_PASSWORD") ?? "test123"; +bool skipSetup = string.Equals(Environment.GetEnvironmentVariable("MCC_MCP_SKIP_SETUP"), "1", StringComparison.Ordinal); +bool runLocalSetup = !useStdio && !skipSetup && IsLocalEndpoint(endpoint) && File.Exists(rconScript); +var executed = new List(); +var checks = new List(); + +try +{ + await using McpClient client = useStdio + ? await McpClient.CreateAsync(new StdioClientTransport(CreateStdioOptions())) + : await McpClient.CreateAsync(new HttpClientTransport(new HttpClientTransportOptions + { + Endpoint = new Uri(endpoint), + TransportMode = HttpTransportMode.AutoDetect, + AdditionalHeaders = string.IsNullOrWhiteSpace(mcpAuthToken) + ? null + : new Dictionary { ["Authorization"] = $"Bearer {mcpAuthToken}" } + })); + + ToolEnvelope initialWorldState = await CallSuccessAsync(client, executed, "mcc_world_state"); + JsonElement initialWorldData = RequireData(initialWorldState); + string botName = ReadString(initialWorldData, "username") ?? "MCCBot"; + Coordinate initialLocation = ReadCoordinate(initialWorldData, "location"); + + long setupBaseline = 0; + if (runLocalSetup) + { + ToolEnvelope baselineEvents = await CallSuccessAsync(client, executed, "mcc_recent_events", new Dictionary + { + ["afterId"] = 0L, + ["maxCount"] = 1 + }); + setupBaseline = ReadInt64(RequireData(baselineEvents), "latestId"); + + await PrepareWorldAsync(rconScript, rconPort, rconPassword, botName, initialLocation); + await Task.Delay(1500); + } + + ToolEnvelope worldState = await WaitForPredicateAsync( + client, + executed, + "mcc_world_state", + null, + envelope => + { + if (!envelope.Success || envelope.Data is not JsonElement data) + return false; + + return data.TryGetProperty("loadedChunkCount", out JsonElement loaded) + && loaded.TryGetInt32(out int loadedChunkCount) + && loadedChunkCount >= 0 + && HasNonNullProperty(data, "worldAge") + && HasNonNullProperty(data, "timeOfDay"); + }, + "mcc_world_state never reported chunk/time state."); + + JsonElement worldData = RequireData(worldState); + Coordinate worldLocation = ReadCoordinate(worldData, "location"); + string dimension = RequireString(worldData, "dimension"); + int loadedChunkCount = ReadInt32(worldData, "loadedChunkCount"); + int pendingChunkCount = ReadInt32(worldData, "pendingChunkCount"); + int totalChunkCount = ReadInt32(worldData, "totalChunkCount"); + double loadRatio = ReadDouble(worldData, "loadRatio"); + _ = RequireString(worldData, "host"); + _ = ReadInt32(worldData, "port"); + _ = RequireString(worldData, "username"); + _ = ReadInt32(worldData, "protocol"); + _ = ReadDouble(worldData, "tps"); + Ensure(!string.IsNullOrWhiteSpace(dimension), "mcc_world_state returned an empty dimension."); + Ensure(loadedChunkCount + pendingChunkCount == totalChunkCount, "mcc_world_state chunk counters are inconsistent."); + Ensure(loadRatio is >= 0 and <= 1, "mcc_world_state loadRatio is out of range."); + Ensure(HasNonNullProperty(worldData, "worldAge"), "mcc_world_state.worldAge is null."); + Ensure(HasNonNullProperty(worldData, "timeOfDay"), "mcc_world_state.timeOfDay is null."); + if (runLocalSetup || useStdio) + { + Ensure(HasNonNullProperty(worldData, "rainLevel"), "mcc_world_state.rainLevel is null after setup."); + Ensure(HasNonNullProperty(worldData, "thunderLevel"), "mcc_world_state.thunderLevel is null after setup."); + } + checks.Add("mcc_world_state"); + + ToolEnvelope chunkStatus = await CallSuccessAsync(client, executed, "mcc_chunk_status"); + JsonElement chunkData = RequireData(chunkStatus); + JsonElement chunk = RequireProperty(chunkData, "chunk"); + _ = ReadInt32(chunk, "x"); + _ = ReadInt32(chunk, "z"); + Ensure(ReadBoolean(chunkData, "loaded"), "mcc_chunk_status reported the current chunk as unloaded."); + Ensure(ReadInt32(chunkData, "loadedChunkCount") + ReadInt32(chunkData, "pendingChunkCount") == ReadInt32(chunkData, "totalChunkCount"), + "mcc_chunk_status chunk counters are inconsistent."); + checks.Add("mcc_chunk_status"); + + await CallSuccessAsync(client, executed, "mcc_look_direction", new Dictionary { ["direction"] = "Down" }); + ToolEnvelope raycast = await CallSuccessAsync(client, executed, "mcc_raycast_block", new Dictionary + { + ["maxDistance"] = 8.0, + ["includeNeighbors"] = true + }); + JsonElement raycastData = RequireData(raycast); + Ensure(ReadBoolean(raycastData, "hit"), "mcc_raycast_block did not report a hit after looking down."); + JsonElement raycastBlock = RequireProperty(raycastData, "block"); + Ensure(!string.Equals(RequireString(raycastBlock, "material"), "Air", StringComparison.OrdinalIgnoreCase), + "mcc_raycast_block hit Air instead of a solid block."); + Ensure(RequireProperty(raycastData, "neighbors").ValueKind == JsonValueKind.Object, + "mcc_raycast_block did not include neighbors when requested."); + checks.Add("mcc_raycast_block"); + + ToolEnvelope pathPreview = await CallSuccessAsync(client, executed, "mcc_path_preview", new Dictionary + { + ["x"] = Math.Floor(worldLocation.X) + 2, + ["y"] = worldLocation.Y, + ["z"] = Math.Floor(worldLocation.Z), + ["allowUnsafe"] = false, + ["timeoutMs"] = 2000, + ["maxWaypoints"] = 32 + }); + JsonElement pathData = RequireData(pathPreview); + Ensure(ReadBoolean(pathData, "pathFound"), "mcc_path_preview did not find a path to a nearby target."); + Ensure(RequireProperty(pathData, "waypoints").GetArrayLength() > 0, "mcc_path_preview returned no waypoints."); + checks.Add("mcc_path_preview"); + + ToolEnvelope stoneSearch = await WaitForPredicateAsync( + client, + executed, + "mcc_inventory_search", + new Dictionary + { + ["query"] = "Stone", + ["maxCount"] = 20, + ["exactMatch"] = true, + ["includeContainers"] = false + }, + envelope => envelope.Success && envelope.Data is JsonElement data && ReadInt32(data, "count") > 0, + "mcc_inventory_search never found Stone in the player inventory."); + Ensure(ContainsItemType(RequireData(stoneSearch), "Stone"), "mcc_inventory_search results did not include Stone."); + + ToolEnvelope swordSearch = await WaitForPredicateAsync( + client, + executed, + "mcc_inventory_search", + new Dictionary + { + ["query"] = "DiamondSword", + ["maxCount"] = 20, + ["exactMatch"] = true, + ["includeContainers"] = false + }, + envelope => envelope.Success && envelope.Data is JsonElement data && ReadInt32(data, "count") > 0, + "mcc_inventory_search never found DiamondSword in the player inventory."); + Ensure(ContainsItemType(RequireData(swordSearch), "DiamondSword"), "mcc_inventory_search results did not include DiamondSword."); + checks.Add("mcc_inventory_search"); + + ToolEnvelope selectItem = await CallSuccessAsync(client, executed, "mcc_select_item", new Dictionary + { + ["itemType"] = "DiamondSword", + ["preferLowestSlot"] = true + }); + JsonElement selectData = RequireData(selectItem); + int selectedSlot = ReadInt32(selectData, "selectedSlot"); + + ToolEnvelope playerStats = await CallSuccessAsync(client, executed, "mcc_player_stats"); + JsonElement playerStatsData = RequireData(playerStats); + Ensure(ReadInt32(playerStatsData, "currentSlot") == selectedSlot, "mcc_select_item did not update mcc_player_stats.currentSlot."); + _ = ReadInt32(playerStatsData, "playerEntityId"); + _ = ReadInt32(playerStatsData, "level"); + _ = ReadInt32(playerStatsData, "totalExperience"); + _ = ReadCoordinate(playerStatsData, "location"); + checks.Add("mcc_select_item"); + checks.Add("mcc_player_stats"); + + ToolEnvelope playersDetailed = await CallSuccessAsync(client, executed, "mcc_players_detailed", new Dictionary + { + ["includeSelf"] = true, + ["includeCoordinates"] = true + }); + JsonElement playersData = RequireData(playersDetailed); + JsonElement selfPlayer = FindPlayer(RequireProperty(playersData, "players"), botName); + _ = RequireString(selfPlayer, "uuid"); + _ = ReadInt32(selfPlayer, "ping"); + _ = ReadInt32(selfPlayer, "entityId"); + _ = ReadDouble(selfPlayer, "x"); + _ = ReadDouble(selfPlayer, "y"); + _ = ReadDouble(selfPlayer, "z"); + checks.Add("mcc_players_detailed"); + + ToolEnvelope statusEffects = await CallSuccessAsync(client, executed, "mcc_status_effects"); + Ensure(RequireProperty(RequireData(statusEffects), "effects").ValueKind == JsonValueKind.Array, + "mcc_status_effects.effects is not an array."); + checks.Add("mcc_status_effects"); + + ToolEnvelope animation = await CallSuccessAsync(client, executed, "mcc_animation", new Dictionary + { + ["hand"] = "MainHand" + }); + Ensure(ReadBoolean(RequireData(animation), "success"), "mcc_animation did not report success."); + + ToolEnvelope sneakOn = await CallSuccessAsync(client, executed, "mcc_toggle_sneak", new Dictionary { ["enabled"] = true }); + Ensure(ReadBoolean(RequireData(sneakOn), "enabled"), "mcc_toggle_sneak(true) did not report enabled=true."); + + ToolEnvelope sprintOn = await CallSuccessAsync(client, executed, "mcc_toggle_sprint", new Dictionary { ["enabled"] = true }); + Ensure(ReadBoolean(RequireData(sprintOn), "enabled"), "mcc_toggle_sprint(true) did not report enabled=true."); + + await CallSuccessAsync(client, executed, "mcc_look_angles", new Dictionary + { + ["yaw"] = 45.0f, + ["pitch"] = -15.0f + }); + ToolEnvelope updatedStats = await CallSuccessAsync(client, executed, "mcc_player_stats"); + JsonElement updatedStatsData = RequireData(updatedStats); + Ensure(Math.Abs(ReadDouble(updatedStatsData, "yaw") - 45.0) < 0.01, "mcc_look_angles did not update yaw."); + Ensure(Math.Abs(ReadDouble(updatedStatsData, "pitch") - (-15.0)) < 0.01, "mcc_look_angles did not update pitch."); + checks.Add("mcc_animation"); + checks.Add("mcc_toggle_sneak"); + checks.Add("mcc_toggle_sprint"); + checks.Add("mcc_look_direction"); + checks.Add("mcc_look_angles"); + + ToolEnvelope nearestEntity = await WaitForPredicateAsync( + client, + executed, + "mcc_entity_nearest", + new Dictionary + { + ["typeFilter"] = "ArmorStand", + ["radius"] = 16.0, + ["includePlayers"] = false + }, + envelope => envelope.Success, + "mcc_entity_nearest never found a nearby ArmorStand."); + JsonElement nearestData = RequireData(nearestEntity); + int entityId = ReadInt32(nearestData, "id"); + Ensure(string.Equals(RequireString(nearestData, "type"), "ArmorStand", StringComparison.OrdinalIgnoreCase), + "mcc_entity_nearest did not return an ArmorStand."); + + ToolEnvelope attackEntity = await CallSuccessAsync(client, executed, "mcc_entity_attack", new Dictionary + { + ["entityId"] = entityId + }); + Ensure(ReadBoolean(RequireData(attackEntity), "success"), "mcc_entity_attack did not report success."); + checks.Add("mcc_entity_nearest"); + checks.Add("mcc_entity_attack"); + + long recentSetupAfterId = runLocalSetup ? setupBaseline : 0; + if (runLocalSetup || useStdio) + { + ToolEnvelope setupEvents = await WaitForRecentEventTypesAsync( + client, + executed, + recentSetupAfterId, + "weather_rain", + "title", + "actionbar"); + JsonElement setupEventsData = RequireData(setupEvents); + Ensure(GetEventTypes(setupEventsData).Contains("weather_rain", StringComparer.OrdinalIgnoreCase), "mcc_recent_events did not include weather_rain."); + Ensure(GetEventTypes(setupEventsData).Contains("title", StringComparer.OrdinalIgnoreCase), "mcc_recent_events did not include title."); + Ensure(GetEventTypes(setupEventsData).Contains("actionbar", StringComparer.OrdinalIgnoreCase), "mcc_recent_events did not include actionbar."); + } + + ToolEnvelope actionbarEvents = await CallSuccessAsync(client, executed, "mcc_recent_events", new Dictionary + { + ["afterId"] = 0L, + ["maxCount"] = 20, + ["typeFilter"] = "actionbar" + }); + JsonElement actionbarData = RequireData(actionbarEvents); + Ensure(ReadInt32(actionbarData, "count") > 0, "mcc_recent_events typeFilter=actionbar returned no events."); + Ensure(AllEventsMatchType(actionbarData, "actionbar"), "mcc_recent_events typeFilter returned mixed event types."); + + long inventoryBaseline = ReadInt64(actionbarData, "latestId"); + int chestX = (int)Math.Floor(worldLocation.X) + 2; + int chestY = (int)Math.Floor(worldLocation.Y); + int chestZ = (int)Math.Floor(worldLocation.Z); + ToolEnvelope openContainer = await WaitForPredicateAsync( + client, + executed, + "mcc_container_open_at", + new Dictionary + { + ["x"] = chestX, + ["y"] = chestY, + ["z"] = chestZ, + ["timeoutMs"] = 3000, + ["closeCurrent"] = true + }, + envelope => envelope.Success, + "mcc_open_container_at never opened the nearby chest."); + JsonElement inventoryInfo = RequireProperty(RequireData(openContainer), "inventory"); + int openedInventoryId = ReadInt32(inventoryInfo, "id"); + ToolEnvelope closeContainer = await CallSuccessAsync(client, executed, "mcc_container_close", new Dictionary + { + ["inventoryId"] = openedInventoryId, + ["timeoutMs"] = 3000 + }); + Ensure(ReadBoolean(RequireData(closeContainer), "closed"), "mcc_close_container did not close the chest."); + + ToolEnvelope inventoryEvents = await WaitForRecentEventTypesAsync( + client, + executed, + inventoryBaseline, + "inventory_open", + "inventory_close"); + JsonElement inventoryEventsData = RequireData(inventoryEvents); + Ensure(GetEventTypes(inventoryEventsData).Contains("inventory_open", StringComparer.OrdinalIgnoreCase), "mcc_recent_events did not include inventory_open."); + Ensure(GetEventTypes(inventoryEventsData).Contains("inventory_close", StringComparer.OrdinalIgnoreCase), "mcc_recent_events did not include inventory_close."); + checks.Add("mcc_recent_events"); + + if (runLocalSetup) + { + long deathBaseline = ReadInt64(inventoryEventsData, "latestId"); + await RunRconCommandAsync(rconScript, rconPort, rconPassword, $"kill {botName}"); + ToolEnvelope deathEvents = await WaitForRecentEventTypesAsync(client, executed, deathBaseline, "death"); + Ensure(GetEventTypes(RequireData(deathEvents)).Contains("death", StringComparer.OrdinalIgnoreCase), + "mcc_recent_events never reported death after the RCON kill."); + + long respawnBaseline = ReadInt64(RequireData(deathEvents), "latestId"); + ToolEnvelope respawn = await CallSuccessAsync(client, executed, "mcc_respawn"); + Ensure(ReadBoolean(RequireData(respawn), "success"), "mcc_respawn did not report success."); + ToolEnvelope respawnEvents = await WaitForRecentEventTypesAsync(client, executed, respawnBaseline, "respawn"); + Ensure(GetEventTypes(RequireData(respawnEvents)).Contains("respawn", StringComparer.OrdinalIgnoreCase), + "mcc_recent_events never reported respawn after mcc_respawn."); + checks.Add("mcc_respawn"); + } + else + { + long respawnBaseline = ReadInt64(RequireData(inventoryEvents), "latestId"); + ToolEnvelope respawn = await CallSuccessAsync(client, executed, "mcc_respawn"); + Ensure(ReadBoolean(RequireData(respawn), "success"), "mcc_respawn did not report success."); + ToolEnvelope respawnEvents = await WaitForRecentEventTypesAsync(client, executed, respawnBaseline, "respawn"); + Ensure(GetEventTypes(RequireData(respawnEvents)).Contains("respawn", StringComparer.OrdinalIgnoreCase), + "mcc_recent_events never reported respawn after mcc_respawn."); + checks.Add("mcc_respawn"); + } + + ToolEnvelope loadedBots = await CallSuccessAsync(client, executed, "mcc_loaded_bots"); + JsonElement bots = RequireProperty(RequireData(loadedBots), "bots"); + Ensure(ContainsBot(bots, "McpServer"), "mcc_loaded_bots did not include McpServer."); + checks.Add("mcc_loaded_bots"); + + ToolEnvelope disconnect = await CallSuccessAsync(client, executed, "mcc_disconnect"); + Ensure(ReadBoolean(RequireData(disconnect), "disconnecting"), "mcc_disconnect did not report disconnecting=true."); + checks.Add("mcc_disconnect"); + + if (!useStdio) + { + await AssertDisconnectStopsEndpointAsync(client, executed); + } + + Console.WriteLine(JsonSerializer.Serialize(new + { + success = true, + endpoint, + useStdio, + runLocalSetup, + checks, + executed + }, new JsonSerializerOptions { WriteIndented = true })); + + Environment.ExitCode = 0; +} +catch (Exception ex) +{ + Console.WriteLine(JsonSerializer.Serialize(new + { + success = false, + endpoint, + useStdio, + runLocalSetup, + error = ex.Message, + checks, + executed + }, new JsonSerializerOptions { WriteIndented = true })); + Environment.ExitCode = 1; +} + +static async Task PrepareWorldAsync(string rconScript, string rconPort, string rconPassword, string botName, Coordinate location) +{ + string[] commands = + [ + $"op {botName}", + $"gamemode creative {botName}", + $"tp {botName} 0 80 0", + $"item replace entity {botName} hotbar.0 with minecraft:stone 32", + $"item replace entity {botName} hotbar.1 with minecraft:diamond_sword 1", + $"execute as {botName} at @s run setblock ~2 ~ ~ minecraft:chest", + $"execute as {botName} at @s run summon minecraft:armor_stand ~2 ~ ~1", + "weather clear", + "weather rain", + $"title {botName} title {{\"text\":\"mcp_title\"}}", + $"title {botName} actionbar {{\"text\":\"mcp_actionbar\"}}" + ]; + + foreach (string command in commands) + { + await RunRconCommandAsync(rconScript, rconPort, rconPassword, command); + } +} + +static async Task RunRconCommandAsync(string rconScript, string rconPort, string rconPassword, string command) +{ + ProcessStartInfo startInfo = new("bash") + { + RedirectStandardOutput = true, + RedirectStandardError = true + }; + startInfo.ArgumentList.Add(rconScript); + startInfo.ArgumentList.Add(command); + startInfo.ArgumentList.Add(rconPort); + startInfo.ArgumentList.Add(rconPassword); + + using Process process = Process.Start(startInfo) ?? throw new InvalidOperationException("Failed to start mc-rcon.sh."); + string stdout = await process.StandardOutput.ReadToEndAsync(); + string stderr = await process.StandardError.ReadToEndAsync(); + await process.WaitForExitAsync(); + if (process.ExitCode != 0) + { + throw new InvalidOperationException( + $"RCON command failed ({command}): {(string.IsNullOrWhiteSpace(stderr) ? stdout : stderr).Trim()}"); + } +} + +static async Task CallSuccessAsync( + McpClient client, + List executed, + string toolName, + IReadOnlyDictionary? args = null) +{ + ToolEnvelope envelope = await CallToolAsync(client, executed, toolName, args); + if (!envelope.Success) + { + throw new InvalidOperationException( + $"{toolName} failed with errorCode={envelope.ErrorCode ?? ""} message={envelope.Message ?? ""}."); + } + + return envelope; +} + +static async Task WaitForPredicateAsync( + McpClient client, + List executed, + string toolName, + IReadOnlyDictionary? args, + Func predicate, + string failureMessage, + int maxAttempts = 12, + int delayMs = 400) +{ + ToolEnvelope? lastEnvelope = null; + for (int attempt = 0; attempt < maxAttempts; attempt++) + { + ToolEnvelope envelope = await CallToolAsync(client, executed, toolName, args); + lastEnvelope = envelope; + if (predicate(envelope)) + return envelope; + + await Task.Delay(delayMs); + } + + throw new InvalidOperationException( + $"{failureMessage} Last result: success={lastEnvelope?.Success}, errorCode={lastEnvelope?.ErrorCode ?? ""}."); +} + +static async Task WaitForRecentEventTypesAsync( + McpClient client, + List executed, + long afterId, + params string[] expectedTypes) +{ + HashSet expected = expectedTypes.ToHashSet(StringComparer.OrdinalIgnoreCase); + ToolEnvelope? lastEnvelope = null; + + for (int attempt = 0; attempt < 12; attempt++) + { + ToolEnvelope envelope = await CallSuccessAsync(client, executed, "mcc_recent_events", new Dictionary + { + ["afterId"] = afterId, + ["maxCount"] = 100 + }); + lastEnvelope = envelope; + JsonElement data = RequireData(envelope); + HashSet actual = GetEventTypes(data); + if (expected.All(actual.Contains)) + return envelope; + + await Task.Delay(400); + } + + throw new InvalidOperationException( + $"mcc_recent_events never reported: {string.Join(", ", expectedTypes)} after event id {afterId}. Last latestId={ReadInt64(RequireData(lastEnvelope!), "latestId")}."); +} + +static async Task CallToolAsync( + McpClient client, + List executed, + string toolName, + IReadOnlyDictionary? args = null) +{ + CallToolResult result = await client.CallToolAsync(toolName, args); + string responseJson = ExtractResponseJson(result); + JsonElement root = JsonDocument.Parse(responseJson).RootElement.Clone(); + JsonElement? data = root.TryGetProperty("data", out JsonElement dataElement) ? dataElement.Clone() : null; + bool success = root.TryGetProperty("success", out JsonElement successElement) + && successElement.ValueKind == JsonValueKind.True; + string? errorCode = ReadString(root, "errorCode"); + string? message = ReadString(root, "message"); + + executed.Add(new + { + tool = toolName, + arguments = args, + isError = result.IsError, + success, + errorCode, + message, + response = root + }); + + return new ToolEnvelope(toolName, result.IsError ?? false, success, errorCode, message, root, data); +} + +static async Task AssertDisconnectStopsEndpointAsync(McpClient client, List executed) +{ + for (int attempt = 0; attempt < 15; attempt++) + { + try + { + await CallToolAsync(client, executed, "mcc_world_state"); + } + catch + { + return; + } + + await Task.Delay(300); + } + + throw new InvalidOperationException("The MCP endpoint still responded after mcc_disconnect."); +} + +static string ExtractResponseJson(CallToolResult result) +{ + if (result.Content is null) + throw new InvalidOperationException("Tool response did not contain any content blocks."); + + foreach (ContentBlock content in result.Content) + { + if (content is TextContentBlock text && !string.IsNullOrWhiteSpace(text.Text)) + return text.Text; + } + + throw new InvalidOperationException("Tool response did not contain a text payload."); +} + +static JsonElement RequireData(ToolEnvelope envelope) +{ + if (envelope.Data is JsonElement data) + return data; + + throw new InvalidOperationException($"{envelope.ToolName} returned no data payload."); +} + +static JsonElement RequireProperty(JsonElement element, string propertyName) +{ + if (element.TryGetProperty(propertyName, out JsonElement property)) + return property; + + throw new InvalidOperationException($"Missing required property '{propertyName}'."); +} + +static string RequireString(JsonElement element, string propertyName) +{ + string? value = ReadString(element, propertyName); + if (!string.IsNullOrWhiteSpace(value)) + return value; + + throw new InvalidOperationException($"Property '{propertyName}' is missing or empty."); +} + +static string? ReadString(JsonElement element, string propertyName) +{ + return element.TryGetProperty(propertyName, out JsonElement property) && property.ValueKind == JsonValueKind.String + ? property.GetString() + : null; +} + +static int ReadInt32(JsonElement element, string propertyName) +{ + JsonElement property = RequireProperty(element, propertyName); + if (property.TryGetInt32(out int value)) + return value; + + throw new InvalidOperationException($"Property '{propertyName}' is not an Int32."); +} + +static long ReadInt64(JsonElement element, string propertyName) +{ + JsonElement property = RequireProperty(element, propertyName); + if (property.TryGetInt64(out long value)) + return value; + + throw new InvalidOperationException($"Property '{propertyName}' is not an Int64."); +} + +static double ReadDouble(JsonElement element, string propertyName) +{ + JsonElement property = RequireProperty(element, propertyName); + if (property.TryGetDouble(out double value)) + return value; + + throw new InvalidOperationException($"Property '{propertyName}' is not a Double."); +} + +static bool ReadBoolean(JsonElement element, string propertyName) +{ + JsonElement property = RequireProperty(element, propertyName); + return property.ValueKind switch + { + JsonValueKind.True => true, + JsonValueKind.False => false, + _ => throw new InvalidOperationException($"Property '{propertyName}' is not a Boolean.") + }; +} + +static bool HasNonNullProperty(JsonElement element, string propertyName) +{ + return element.TryGetProperty(propertyName, out JsonElement property) && property.ValueKind != JsonValueKind.Null; +} + +static Coordinate ReadCoordinate(JsonElement element, string propertyName) +{ + JsonElement coordinate = RequireProperty(element, propertyName); + return new Coordinate( + ReadDouble(coordinate, "x"), + ReadDouble(coordinate, "y"), + ReadDouble(coordinate, "z")); +} + +static JsonElement FindPlayer(JsonElement players, string playerName) +{ + foreach (JsonElement player in players.EnumerateArray()) + { + string? name = ReadString(player, "name"); + if (string.Equals(name, playerName, StringComparison.OrdinalIgnoreCase)) + return player; + } + + throw new InvalidOperationException($"Could not find player '{playerName}' in mcc_players_detailed."); +} + +static bool ContainsItemType(JsonElement searchData, string itemType) +{ + JsonElement matches = RequireProperty(searchData, "matches"); + foreach (JsonElement match in matches.EnumerateArray()) + { + if (string.Equals(ReadString(match, "itemType"), itemType, StringComparison.OrdinalIgnoreCase)) + return true; + } + + return false; +} + +static bool ContainsBot(JsonElement bots, string botName) +{ + foreach (JsonElement bot in bots.EnumerateArray()) + { + if (string.Equals(ReadString(bot, "name"), botName, StringComparison.OrdinalIgnoreCase)) + return true; + } + + return false; +} + +static HashSet GetEventTypes(JsonElement recentEventsData) +{ + JsonElement events = RequireProperty(recentEventsData, "events"); + return events.EnumerateArray() + .Select(entry => RequireString(entry, "type")) + .ToHashSet(StringComparer.OrdinalIgnoreCase); +} + +static bool AllEventsMatchType(JsonElement recentEventsData, string type) +{ + JsonElement events = RequireProperty(recentEventsData, "events"); + foreach (JsonElement entry in events.EnumerateArray()) + { + if (!string.Equals(RequireString(entry, "type"), type, StringComparison.OrdinalIgnoreCase)) + return false; + } + + return true; +} + +static void Ensure(bool condition, string message) +{ + if (!condition) + throw new InvalidOperationException(message); +} + +static bool IsLocalEndpoint(string endpoint) +{ + if (!Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri)) + return false; + + return string.Equals(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Host, "127.0.0.1", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Host, "::1", StringComparison.OrdinalIgnoreCase); +} + +static string FindRepoRoot() +{ + string current = Directory.GetCurrentDirectory(); + DirectoryInfo? directory = new(current); + + while (directory is not null) + { + if (File.Exists(Path.Combine(directory.FullName, "MinecraftClient.sln"))) + return directory.FullName; + + directory = directory.Parent; + } + + return current; +} + +static StdioClientTransportOptions CreateStdioOptions() +{ + string? stdioBin = Environment.GetEnvironmentVariable("MCC_MCP_STDIO_BIN"); + if (!string.IsNullOrWhiteSpace(stdioBin)) + { + return new StdioClientTransportOptions + { + Name = "MCC MCP Stdio Harness", + Command = stdioBin, + Arguments = [], + ShutdownTimeout = TimeSpan.FromSeconds(5) + }; + } + + return new StdioClientTransportOptions + { + Name = "MCC MCP Stdio Harness", + Command = "dotnet", + Arguments = + [ + "run", + "--project", + "DebugTools/MccMcpStdioHarness", + "-c", + "Release", + "--no-build" + ], + ShutdownTimeout = TimeSpan.FromSeconds(5) + }; +} + +internal readonly record struct Coordinate(double X, double Y, double Z); + +internal sealed record ToolEnvelope( + string ToolName, + bool IsError, + bool Success, + string? ErrorCode, + string? Message, + JsonElement Root, + JsonElement? Data); diff --git a/DebugTools/MccMcpStdioHarness/MccMcpStdioHarness.csproj b/DebugTools/MccMcpStdioHarness/MccMcpStdioHarness.csproj new file mode 100644 index 00000000..5bbe1b4e --- /dev/null +++ b/DebugTools/MccMcpStdioHarness/MccMcpStdioHarness.csproj @@ -0,0 +1,18 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + diff --git a/DebugTools/MccMcpStdioHarness/Program.cs b/DebugTools/MccMcpStdioHarness/Program.cs new file mode 100644 index 00000000..1b90ddbb --- /dev/null +++ b/DebugTools/MccMcpStdioHarness/Program.cs @@ -0,0 +1,984 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using MinecraftClient.Mcp; +using ModelContextProtocol.Server; + +var builder = Host.CreateApplicationBuilder(args); +builder.Logging.AddConsole(options => +{ + options.LogToStandardErrorThreshold = LogLevel.Trace; +}); + +builder.Services.AddSingleton(new MccMcpConfig()); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddMcpServer() + .WithStdioServerTransport() + .WithTools() + .WithPrompts(); + +await builder.Build().RunAsync(); + +internal sealed class DeterministicCapabilities : IMccMcpCapabilities +{ + private static double C(double value) => Math.Round(value, 2, MidpointRounding.AwayFromZero); + + private readonly List recentEvents = []; + private long nextEventId = 1; + private double playerX = C(0.5); + private double playerY = C(80.0); + private double playerZ = C(0.5); + private float yaw; + private float pitch; + private int currentSlot = 1; + private bool sneaking; + private bool sprinting; + private float health = 20.0f; + private bool disconnecting; + + public DeterministicCapabilities() + { + AddRecentEvent("player_join", new { name = "HarnessBot" }); + AddRecentEvent("inventory_open", new { inventoryId = 1, type = "Generic_9x3", title = "Chest" }); + AddRecentEvent("weather_rain", new { level = 1.0 }); + AddRecentEvent("title", new { text = "mcp_title" }); + AddRecentEvent("actionbar", new { text = "mcp_actionbar" }); + } + + public MccMcpResult GetSessionStatus() => + MccMcpResult.Ok(new + { + connected = !disconnecting, + host = "deterministic.local", + port = 25565, + username = "HarnessBot", + location = new { x = playerX, y = playerY, z = playerZ } + }); + + public MccMcpResult GetServerInfo() => + MccMcpResult.Ok(new + { + host = "deterministic.local", + port = 25565, + tps = 20.0 + }); + + public MccMcpResult GetPlayerState() => + MccMcpResult.Ok(new + { + nickname = "HarnessBot", + username = "HarnessBot", + health, + saturation = 20, + gamemode = 1, + currentSlot, + yaw, + pitch, + location = new { x = playerX, y = playerY, z = playerZ }, + effects = new object[0] + }); + + public MccMcpResult GetWorldState() => + MccMcpResult.Ok(new + { + connected = !disconnecting, + host = "deterministic.local", + port = 25565, + username = "HarnessBot", + protocol = 769, + terrainEnabled = true, + inventoryEnabled = true, + entityHandlingEnabled = true, + location = new { x = playerX, y = playerY, z = playerZ }, + tps = 20.0, + dimension = "minecraft:overworld", + loadedChunkCount = 9, + pendingChunkCount = 0, + totalChunkCount = 9, + loadRatio = 1.0, + worldAge = 12000L, + timeOfDay = 6000L, + rainLevel = 1.0, + thunderLevel = 0.0 + }); + + public MccMcpResult GetChunkStatus(double? x, double? y, double? z) + { + double resolvedX = x ?? playerX; + double resolvedY = y ?? playerY; + double resolvedZ = z ?? playerZ; + int chunkX = (int)Math.Floor(resolvedX) >> 4; + int chunkZ = (int)Math.Floor(resolvedZ) >> 4; + + return MccMcpResult.Ok(new + { + location = new { x = C(resolvedX), y = C(resolvedY), z = C(resolvedZ) }, + chunk = new { x = chunkX, z = chunkZ }, + loaded = true, + fullyLoaded = true, + loadedChunkCount = 9, + pendingChunkCount = 0, + totalChunkCount = 9, + loadRatio = 1.0 + }); + } + + public MccMcpResult RaycastBlock(double maxDistance, bool includeNeighbors) + { + object? neighbors = includeNeighbors + ? new + { + north = new { x = 0, y = 79, z = -1, material = "Air", typeLabel = "Air" }, + south = new { x = 0, y = 79, z = 1, material = "Air", typeLabel = "Air" }, + east = new { x = 1, y = 79, z = 0, material = "Air", typeLabel = "Air" }, + west = new { x = -1, y = 79, z = 0, material = "Air", typeLabel = "Air" }, + above = new { x = 0, y = 80, z = 0, material = "Air", typeLabel = "Air" }, + below = new { x = 0, y = 78, z = 0, material = "Stone", typeLabel = "Stone" } + } + : null; + + return MccMcpResult.Ok(new + { + hit = true, + maxDistance, + playerLocation = new { x = playerX, y = playerY, z = playerZ }, + eyeLocation = new { x = playerX, y = C(playerY + 1.62), z = playerZ }, + location = new { x = 0, y = 79, z = 0 }, + block = new { material = "Stone", typeLabel = "Stone", blockId = 1, blockMeta = 0 }, + distance = 1.12, + eyeDistance = 2.03, + neighbors + }); + } + + public MccMcpResult PreviewPath(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs, int maxWaypoints) + { + object[] waypoints = + [ + new { x = playerX, y = playerY, z = playerZ }, + new { x = C((playerX + x) / 2), y = C((playerY + y) / 2), z = C((playerZ + z) / 2) }, + new { x = C(x), y = C(y), z = C(z) } + ]; + + return MccMcpResult.Ok(new + { + pathFound = true, + exactReachable = true, + target = new { x = C(x), y = C(y), z = C(z) }, + startLocation = new { x = playerX, y = playerY, z = playerZ }, + finalWaypoint = new { x = C(x), y = C(y), z = C(z) }, + finalDistance = 0.0, + waypointCount = waypoints.Length, + truncated = waypoints.Length > Math.Max(1, maxWaypoints), + waypoints = waypoints.Take(Math.Max(1, maxWaypoints)).ToArray(), + allowUnsafe, + maxOffset, + minOffset, + timeoutMs = timeoutMs <= 0 ? 5000 : timeoutMs + }); + } + + public MccMcpResult GetPlayersList() => + MccMcpResult.Ok(new + { + players = new[] { "HarnessBot", "PlayerOne" } + }); + + public MccMcpResult GetPlayersDetailed(bool includeSelf, bool includeCoordinates) + { + List players = []; + if (includeSelf) + { + players.Add(new + { + name = "HarnessBot", + uuid = Guid.Parse("11111111-1111-1111-1111-111111111111"), + ping = 5, + gamemode = 1, + listed = true, + displayName = "HarnessBot", + entityId = 1, + x = includeCoordinates ? playerX : (double?)null, + y = includeCoordinates ? playerY : (double?)null, + z = includeCoordinates ? playerZ : (double?)null + }); + } + + players.Add(new + { + name = "PlayerOne", + uuid = Guid.Parse("22222222-2222-2222-2222-222222222222"), + ping = 12, + gamemode = 1, + listed = true, + displayName = "PlayerOne", + entityId = 2, + x = includeCoordinates ? C(3.5) : (double?)null, + y = includeCoordinates ? C(80.0) : (double?)null, + z = includeCoordinates ? C(0.5) : (double?)null + }); + + return MccMcpResult.Ok(new + { + count = players.Count, + players = players.ToArray() + }); + } + + public MccMcpResult GetPlayerStats() => + MccMcpResult.Ok(new + { + health, + saturation = 20, + level = 12, + totalExperience = 245, + gamemode = 1, + playerEntityId = 1, + currentSlot, + yaw, + pitch, + sneaking, + sprinting, + location = new { x = playerX, y = playerY, z = playerZ }, + tps = 20.0 + }); + + public MccMcpResult GetStatusEffects() => + MccMcpResult.Ok(new + { + count = 0, + effects = Array.Empty() + }); + + public MccMcpResult GetRecentEvents(long afterId, int maxCount, string? typeFilter) + { + RecentEvent[] events = recentEvents + .Where(e => e.Id > afterId) + .Where(e => string.IsNullOrWhiteSpace(typeFilter) || string.Equals(e.Type, typeFilter, StringComparison.OrdinalIgnoreCase)) + .Take(Math.Max(1, maxCount)) + .ToArray(); + + return MccMcpResult.Ok(new + { + afterId, + latestId = recentEvents.Count > 0 ? recentEvents[^1].Id : 0, + count = events.Length, + events = events.Select(e => new + { + id = e.Id, + timestampUtc = e.TimestampUtc, + type = e.Type, + data = e.Data + }).ToArray() + }); + } + + public MccMcpResult GetLoadedBots() => + MccMcpResult.Ok(new + { + count = 2, + bots = new object[] + { + new { name = "McpServer", fullTypeName = "MinecraftClient.ChatBots.McpServer", isScript = false }, + new { name = "HarnessScript", fullTypeName = "MinecraftClient.ChatBots.Script", isScript = true } + } + }); + + public MccMcpResult GetChatHistory(int maxCount, bool includeJson) => + MccMcpResult.Ok(new + { + count = 2, + entries = new object[] + { + new { timestampUtc = DateTimeOffset.UtcNow.AddSeconds(-10), kind = "chat", text = " hello", sender = "PlayerOne", message = "hello", json = includeJson ? "{}" : null }, + new { timestampUtc = DateTimeOffset.UtcNow.AddSeconds(-5), kind = "system", text = "HarnessBot joined the game", sender = (string?)null, message = (string?)null, json = includeJson ? "{}" : null } + } + }); + + public MccMcpResult GetInternalCommands() => + MccMcpResult.Ok(new + { + count = 4, + commands = new[] + { + new { name = "debug", usage = "debug [on|off|state]", description = "Toggle debug or print state." }, + new { name = "move", usage = "move ", description = "Move to location." }, + new { name = "useitem", usage = "useitem [x] [y] [z]", description = "Use current held item." }, + new { name = "dig", usage = "dig [duration]", description = "Dig block at location." } + } + }); + + public MccMcpResult GetMaterialsList(string? filter, int maxCount) => + MccMcpResult.Ok(new + { + total = 3, + count = 3, + filter, + materials = new[] + { + new { name = "Air", typeLabel = "Air" }, + new { name = "GrassBlock", typeLabel = "Grass Block" }, + new { name = "OakLog", typeLabel = "Oak Log" } + } + }); + + public MccMcpResult GetBlockTypesList(string? filter, int maxCount) => + MccMcpResult.Ok(new + { + total = 3, + count = 3, + filter, + blockTypes = new[] + { + new { name = "Air", typeLabel = "Air" }, + new { name = "GrassBlock", typeLabel = "Grass Block" }, + new { name = "OakLog", typeLabel = "Oak Log" } + } + }); + + public MccMcpResult GetEntityTypesList(string? filter, int maxCount) => + MccMcpResult.Ok(new + { + total = 3, + count = 3, + filter, + entityTypes = new[] + { + new { name = "Player", typeLabel = "Player" }, + new { name = "Item", typeLabel = "Item" }, + new { name = "Villager", typeLabel = "Villager" } + } + }); + + public MccMcpResult SendChat(string text) => + MccMcpResult.Ok(new { echoed = text }); + + public MccMcpResult QuitClient() => + MccMcpResult.Ok(new { quitting = true }); + + public MccMcpResult DisconnectClient() + { + disconnecting = true; + AddRecentEvent("disconnect", new { reason = "requested", message = "Disconnect requested by test client." }); + return MccMcpResult.Ok(new { disconnecting = true }); + } + + public MccMcpResult RunInternalCommand(string command) => + MccMcpResult.Ok(new { command, status = "Done", output = "deterministic" }); + + public MccMcpResult UseItemOnHand() => + MccMcpResult.Ok(new { success = true, action = "use_item_on_hand" }); + + public MccMcpResult ChangeHotbarSlot(int slot) + { + currentSlot = slot; + return MccMcpResult.Ok(new { success = true, slot }); + } + + public MccMcpResult SelectHotbarItem(string itemType, bool preferLowestSlot) + { + currentSlot = string.Equals(itemType, "DiamondSword", StringComparison.OrdinalIgnoreCase) ? 2 : 1; + return MccMcpResult.Ok(new + { + success = true, + itemType, + inventorySlot = currentSlot - 1, + selectedSlot = currentSlot, + count = string.Equals(itemType, "DiamondSword", StringComparison.OrdinalIgnoreCase) ? 1 : 32, + preferLowestSlot + }); + } + + public MccMcpResult UseItemOnBlock(double x, double y, double z) => + MccMcpResult.Ok(new { success = true, x = C(x), y = C(y), z = C(z), action = "useitem" }); + + public MccMcpResult DigBlock(double x, double y, double z, double durationSeconds) => + MccMcpResult.Ok(new + { + success = true, + target = new { x = C(x), y = C(y), z = C(z) }, + beforeBlock = new { material = "OakLog", typeLabel = "Oak Log", blockId = 137, blockMeta = 0 }, + afterBlock = new { material = "Air", typeLabel = "Air", blockId = 0, blockMeta = 0 }, + commandAccepted = true, + changed = true, + destroyed = true, + attempts = 1, + attemptedDurationsSeconds = new[] { durationSeconds > 0 ? durationSeconds : 1.5 }, + distance = 1.5, + playerLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) } + }); + + public MccMcpResult PlaceBlock(int x, int y, int z, string face, string hand, bool lookAtBlock) => + MccMcpResult.Ok(new { success = true, x, y, z, face, hand, lookAtBlock, action = "place_block" }); + + public MccMcpResult InteractEntity(int entityId, string interaction, string hand) => + MccMcpResult.Ok(new { success = true, entityId, interaction, hand }); + + public MccMcpResult AttackEntity(int entityId) => + MccMcpResult.Ok(new { success = true, entityId, interaction = "Attack" }); + + public MccMcpResult FindNearestEntity(string? typeFilter, string? nameFilter, double radius, bool includePlayers) + { + bool wantsArmorStand = string.IsNullOrWhiteSpace(typeFilter) + || string.Equals(typeFilter, "ArmorStand", StringComparison.OrdinalIgnoreCase) + || string.Equals(typeFilter, "Armor Stand", StringComparison.OrdinalIgnoreCase); + + if (wantsArmorStand && radius >= 4.0) + { + return MccMcpResult.Ok(new + { + id = 7, + type = "ArmorStand", + typeLabel = "Armor Stand", + uuid = Guid.Parse("33333333-3333-3333-3333-333333333333"), + name = "Armor Stand", + customName = (string?)null, + x = C(2.5), + y = C(80.0), + z = C(0.5), + distance = 2.0, + health = 20.0f, + pose = "Standing", + latency = 0 + }); + } + + if (includePlayers && radius >= 3.0) + { + return MccMcpResult.Ok(new + { + id = 2, + type = "Player", + typeLabel = "Player", + uuid = Guid.Parse("22222222-2222-2222-2222-222222222222"), + name = string.IsNullOrWhiteSpace(nameFilter) ? "PlayerOne" : nameFilter, + customName = (string?)null, + x = C(3.5), + y = C(80.0), + z = C(0.5), + distance = 3.0, + health = 20.0f, + pose = "Standing", + latency = 12 + }); + } + + return MccMcpResult.Fail("invalid_state", data: new { typeFilter, nameFilter, radius, includePlayers }); + } + + public MccMcpResult ScanNearbyBlocks(int radius, int maxCount, string? materialFilter) => + MccMcpResult.Ok(new + { + center = new { x = 0, y = 79, z = 0 }, + radius, + count = 1, + blocks = new[] + { + new { x = 0, y = 79, z = 0, material = materialFilter ?? "GrassBlock", blockId = 9, blockMeta = 0, distance = 0.0 } + } + }); + + public MccMcpResult FindBlocks(string? query, int radius, int maxCount, bool exactMatch) => + MccMcpResult.Ok(new + { + center = new { x = 0, y = 79, z = 0 }, + radius, + query, + exactMatch, + count = 2, + blocks = new object[] + { + new { x = 1, y = 79, z = 0, material = "GrassBlock", typeLabel = "Grass Block", blockId = 9, blockMeta = 0, distance = 1.0 }, + new { x = 2, y = 79, z = 0, material = "Dirt", typeLabel = "Dirt", blockId = 10, blockMeta = 0, distance = 2.0 } + } + }); + + public MccMcpResult IsPlayerNearby(string? playerName, double radius, bool includeSelf) => + MccMcpResult.Ok(new + { + radius, + playerName, + includeSelf, + anyNearby = true, + count = 1, + players = new object[] + { + new + { + entityId = 1, + uuid = Guid.Empty, + name = "PlayerOne", + customName = (string?)null, + x = C(3.5), + y = C(80.0), + z = C(0.5), + distance = 3.0, + latency = 5 + } + } + }); + + public MccMcpResult LocatePlayer(string playerName, bool includeSelf) => + MccMcpResult.Ok(new + { + playerName, + matchedName = "PlayerOne", + entityId = 1, + uuid = Guid.Empty, + x = C(3.5), + y = C(80.0), + z = C(0.5), + distance = 3.0 + }); + + public MccMcpResult CanReachPosition(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs) => + MccMcpResult.Ok(new + { + reachable = true, + exactReachable = true, + target = new { x = C(x), y = C(y), z = C(z) }, + startLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) }, + finalWaypoint = new { x = C(x), y = C(y), z = C(z) }, + finalDistance = 0.0, + waypointCount = 4, + allowUnsafe, + maxOffset, + minOffset, + timeoutMs = timeoutMs <= 0 ? 5000 : timeoutMs + }); + + public MccMcpResult MoveTo(double x, double y, double z, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs) => + MccMcpResult.Ok(new + { + pathFound = true, + arrived = true, + tolerance = 1.5, + verifyWaitMs = 250, + target = new { x = C(x), y = C(y), z = C(z) }, + startLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) }, + finalLocation = new { x = C(x), y = C(y), z = C(z) }, + finalDistance = 0.0, + distanceMoved = 3.0, + allowUnsafe, + allowDirectTeleport, + maxOffset, + minOffset, + timeoutMs + }); + + public MccMcpResult MoveToPlayer(string playerName, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs) => + MccMcpResult.Ok(new + { + pathFound = true, + arrived = true, + tolerance = 1.5, + verifyWaitMs = 250, + target = new + { + playerName = "PlayerOne", + entityId = 1, + x = C(3.5), + y = C(80.0), + z = C(0.5) + }, + startLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) }, + finalLocation = new { x = C(3.5), y = C(80.0), z = C(0.5) }, + finalDistance = 0.0, + distanceMoved = 3.0, + allowUnsafe, + allowDirectTeleport, + maxOffset, + minOffset, + timeoutMs + }); + + public MccMcpResult LookAt(double x, double y, double z) => + MccMcpResult.Ok(new { looked = true, x = C(x), y = C(y), z = C(z) }); + + public MccMcpResult LookDirection(string direction) + { + switch (direction.Trim().ToLowerInvariant()) + { + case "up": + yaw = 0.0f; + pitch = -90.0f; + break; + case "down": + yaw = 0.0f; + pitch = 90.0f; + break; + case "north": + yaw = 180.0f; + pitch = 0.0f; + break; + case "south": + yaw = 0.0f; + pitch = 0.0f; + break; + case "east": + yaw = -90.0f; + pitch = 0.0f; + break; + case "west": + yaw = 90.0f; + pitch = 0.0f; + break; + } + + return MccMcpResult.Ok(new { success = true, direction, yaw, pitch }); + } + + public MccMcpResult LookAngles(float yaw, float pitch) + { + this.yaw = yaw; + this.pitch = pitch; + return MccMcpResult.Ok(new { success = true, yaw, pitch }); + } + + public MccMcpResult PlayAnimation(string hand) => + MccMcpResult.Ok(new { success = true, hand }); + + public MccMcpResult ToggleSneak(bool enabled) + { + sneaking = enabled; + return MccMcpResult.Ok(new { success = true, enabled = sneaking }); + } + + public MccMcpResult ToggleSprint(bool enabled) + { + sprinting = enabled; + return MccMcpResult.Ok(new { success = true, enabled = sprinting }); + } + + public MccMcpResult ListInventories() => + MccMcpResult.Ok(new + { + count = 2, + inventories = new object[] + { + new { id = 0, type = "PlayerInventory", title = "Player Inventory", slotCount = 46, nonEmptySlots = 1, active = false }, + new { id = 1, type = "Generic_9x3", title = "Chest", slotCount = 63, nonEmptySlots = 2, active = true } + } + }); + + public MccMcpResult GetInventorySnapshot(int inventoryId) => + MccMcpResult.Ok(new + { + id = inventoryId, + type = inventoryId == 0 ? "PlayerInventory" : "Generic_9x3", + title = inventoryId == 0 ? "Player Inventory" : "Chest", + slotCount = inventoryId == 0 ? 46 : 63, + slots = new[] + { + new { slot = 0, type = "Stone", count = 64 } + } + }); + + public MccMcpResult SearchInventories(string query, int maxCount, bool exactMatch, bool includeContainers) + { + List matches = []; + + if (query.Contains("stone", StringComparison.OrdinalIgnoreCase)) + { + matches.Add(new + { + inventoryId = 0, + inventoryType = "PlayerInventory", + inventoryTitle = "Player Inventory", + slot = 0, + itemType = "Stone", + typeLabel = "Stone", + count = 32, + isPlayerInventory = true, + hotbarSlot = 1 + }); + } + + if (query.Contains("diamond", StringComparison.OrdinalIgnoreCase) || query.Contains("sword", StringComparison.OrdinalIgnoreCase)) + { + matches.Add(new + { + inventoryId = 0, + inventoryType = "PlayerInventory", + inventoryTitle = "Player Inventory", + slot = 1, + itemType = "DiamondSword", + typeLabel = "Diamond Sword", + count = 1, + isPlayerInventory = true, + hotbarSlot = 2 + }); + } + + if (includeContainers) + { + matches.Add(new + { + inventoryId = 1, + inventoryType = "Generic_9x3", + inventoryTitle = "Chest", + slot = 0, + itemType = "Stone", + typeLabel = "Stone", + count = 16, + isPlayerInventory = false, + hotbarSlot = (int?)null + }); + } + + object[] result = matches.Take(Math.Max(1, maxCount)).ToArray(); + return MccMcpResult.Ok(new + { + query, + exactMatch, + includeContainers, + count = result.Length, + matches = result + }); + } + + public MccMcpResult OpenContainerAt(int x, int y, int z, int timeoutMs, bool closeCurrent) + { + AddRecentEvent("inventory_open", new { inventoryId = 1, type = "Generic_9x3", title = "Chest", x, y, z }); + return MccMcpResult.Ok(new + { + success = true, + openAccepted = true, + opened = true, + timeoutMs = timeoutMs <= 0 ? 5000 : timeoutMs, + x, + y, + z, + block = new { material = "Chest", typeLabel = "Chest", blockId = 0, blockMeta = 0 }, + inventory = new { id = 1, type = "Generic_9x3", title = "Chest", slotCount = 63, nonEmptySlots = 2 } + }); + } + + public MccMcpResult CloseContainer(int inventoryId, int timeoutMs) + { + int resolvedInventoryId = inventoryId <= 0 ? 1 : inventoryId; + AddRecentEvent("inventory_close", new { inventoryId = resolvedInventoryId }); + return MccMcpResult.Ok(new + { + success = true, + closed = true, + inventoryId = resolvedInventoryId, + timeoutMs = timeoutMs <= 0 ? 5000 : timeoutMs + }); + } + + public MccMcpResult InventoryWindowAction(int inventoryId, int slotId, string actionType) => + MccMcpResult.Ok(new { success = true, inventoryId, slotId, actionType }); + + public MccMcpResult DropInventoryItem(string itemType, int count, int inventoryId, bool preferStack) => + MccMcpResult.Ok(new + { + success = true, + itemType, + requestedCount = count, + droppedCount = count, + beforeCount = 64, + afterCount = Math.Max(0, 64 - count), + inventoryId, + touchedSlots = new[] { 36 }, + preferStack + }); + + public MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) => + MccMcpResult.Ok(new + { + success = true, + direction = "deposit", + itemType, + requestedCount = count, + movedCount = count, + beforePlayerCount = 64, + afterPlayerCount = Math.Max(0, 64 - count), + beforeContainerCount = 0, + afterContainerCount = count, + inventoryId = inventoryId <= 0 ? 1 : inventoryId, + containerType = "Generic_9x3", + touchedSourceSlots = new[] { 36 }, + touchedTargetSlots = new[] { 0 } + }); + + public MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) => + MccMcpResult.Ok(new + { + success = true, + direction = "withdraw", + itemType, + requestedCount = count, + movedCount = count, + beforePlayerCount = 0, + afterPlayerCount = count, + beforeContainerCount = 64, + afterContainerCount = Math.Max(0, 64 - count), + inventoryId = inventoryId <= 0 ? 1 : inventoryId, + containerType = "Generic_9x3", + touchedSourceSlots = new[] { 0 }, + touchedTargetSlots = new[] { 36 } + }); + + public MccMcpResult QueryEntities(int maxCount) => + MccMcpResult.Ok(new + { + count = 1, + entities = new[] + { + new { id = 1, type = "Player", x = C(0.5), y = C(80.0), z = C(0.5) } + } + }); + + public MccMcpResult ListEntities(int maxCount, string? typeFilter, double radius) => + MccMcpResult.Ok(new + { + totalTracked = 1, + count = 1, + entities = new[] + { + new + { + id = 1, + type = "Player", + typeLabel = "Player", + uuid = Guid.Empty, + name = "HarnessBot", + customName = (string?)null, + x = C(0.5), + y = C(80.0), + z = C(0.5), + distance = 0.0, + health = 20.0f, + pose = "Standing", + latency = 5 + } + } + }); + + public MccMcpResult GetEntityInfo(int entityId, bool includeMetadata, bool includeEquipment, bool includeEffects) => + MccMcpResult.Ok(new + { + id = entityId, + type = "Player", + typeLabel = "Player", + uuid = Guid.Empty, + name = "HarnessBot", + customName = (string?)null, + customNameVisible = false, + x = C(0.5), + y = C(80.0), + z = C(0.5), + yaw = 0.0f, + pitch = 0.0f, + health = 20.0f, + pose = "Standing", + latency = 5, + objectData = -1, + metadata = includeMetadata ? new { flags = 0 } : null, + equipment = includeEquipment ? new[] { new { slot = 0, type = "Stone", count = 1 } } : null, + activeEffects = includeEffects ? new object[0] : null + }); + + public MccMcpResult FindSigns(string text, bool exactMatch, int radius, int maxCount, bool includeBackText) => + MccMcpResult.Ok(new + { + text, + exactMatch, + radius, + includeBackText, + count = 1, + signs = new[] + { + new + { + x = 2, + y = 80, + z = 1, + material = "OakSign", + typeLabel = "Oak Sign", + distance = 1.8, + isWaxed = false, + frontText = new[] { "home", "storage" }, + backText = includeBackText ? new[] { "north wall" } : Array.Empty(), + matchedLines = new[] { text } + } + } + }); + + public MccMcpResult ListItemEntities(string? itemType, double radius, int maxCount) => + MccMcpResult.Ok(new + { + itemType = itemType ?? "OakLog", + radius, + count = 1, + items = new[] + { + new + { + entityId = 99, + itemType = "OakLog", + typeLabel = "Oak Log", + count = 3, + x = C(2.5), + y = C(80.0), + z = C(1.5), + distance = 2.24 + } + } + }); + + public MccMcpResult PickupItems(string itemType, double radius, int maxItems, bool allowUnsafe, int timeoutMs) => + MccMcpResult.Ok(new + { + itemType, + radius, + maxItems, + allowUnsafe, + timeoutMs = timeoutMs <= 0 ? 2500 : timeoutMs, + attempted = 1, + successfulPickups = 1, + collectedCount = 3, + initialInventoryCount = 0, + finalInventoryCount = 3, + remainingNearby = 0, + attempts = new object[] + { + new + { + entityId = 99, + itemType, + typeLabel = "Oak Log", + expectedCount = 3, + target = new { x = C(2.5), y = C(80.0), z = C(1.5) }, + pathFound = true, + arrived = true, + entityGone = true, + inventoryDelta = 3, + startLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) }, + finalLocation = new { x = C(2.5), y = C(80.0), z = C(1.5) }, + finalDistance = 0.0 + } + } + }); + + public MccMcpResult Respawn() + { + health = 20.0f; + AddRecentEvent("respawn", new { location = new { x = playerX, y = playerY, z = playerZ } }); + return MccMcpResult.Ok(new { success = true, respawned = true }); + } + + public MccMcpResult GetWorldBlockAt(int x, int y, int z) => + MccMcpResult.Ok(new { x, y, z, material = "Air", blockId = 0, blockMeta = 0 }); + + private void AddRecentEvent(string type, object? data) + { + recentEvents.Add(new RecentEvent(nextEventId++, DateTimeOffset.UtcNow, type, data)); + if (recentEvents.Count > 100) + recentEvents.RemoveAt(0); + } + + private sealed record RecentEvent(long Id, DateTimeOffset TimestampUtc, string Type, object? Data); +} diff --git a/DebugTools/MccMcpWebPlayground/Api/MccPlaygroundEndpoints.cs b/DebugTools/MccMcpWebPlayground/Api/MccPlaygroundEndpoints.cs new file mode 100644 index 00000000..ec3e0f83 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Api/MccPlaygroundEndpoints.cs @@ -0,0 +1,36 @@ +using DebugTools.MccMcpWebPlayground.Contracts; +using DebugTools.MccMcpWebPlayground.Harness; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace DebugTools.MccMcpWebPlayground.Api; + +public static class MccPlaygroundEndpoints +{ + public static IEndpointRouteBuilder MapMccPlaygroundEndpoints(this IEndpointRouteBuilder endpoints) + { + RouteGroupBuilder api = endpoints.MapGroup("/api"); + + api.MapGet("/health", () => Results.Ok(new { ok = true })); + + api.MapGet("/config", (IOptions options) => + { + MccWebHarnessOptions harnessOptions = options.Value; + return Results.Ok(new MccConfigResponse( + Model: harnessOptions.ResolveModel(), + OpenRouterBaseUrl: harnessOptions.ResolveOpenRouterBaseUrl(), + McpEndpoint: harnessOptions.ResolveMcpEndpoint(), + HasApiKey: harnessOptions.HasApiKeyConfigured(), + ExposeInventoryWindowAction: harnessOptions.ExposeInventoryWindowAction, + ExposeInternalCommandTool: harnessOptions.ExposeInternalCommandTool)); + }); + + api.MapPost("/chat/stream", (ChatStreamRequest request, IMccAgentRunService runService, HttpContext httpContext, CancellationToken cancellationToken) => + { + return TypedResults.ServerSentEvents(runService.StreamAsync(request, httpContext, cancellationToken)); + }) + .WithRequestTimeout("mcc-stream"); + + return endpoints; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Contracts/MccContracts.cs b/DebugTools/MccMcpWebPlayground/Contracts/MccContracts.cs new file mode 100644 index 00000000..2257e1b1 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Contracts/MccContracts.cs @@ -0,0 +1,94 @@ +using System.Text.Json.Serialization; + +namespace DebugTools.MccMcpWebPlayground.Contracts; + +public sealed class ChatStreamRequest +{ + public List? Messages { get; set; } +} + +public sealed class ChatMessage +{ + public string Role { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; +} + +public sealed record MccConfigResponse( + string? Model, + string OpenRouterBaseUrl, + string McpEndpoint, + bool HasApiKey, + bool ExposeInventoryWindowAction, + bool ExposeInternalCommandTool); + +public sealed record MccStreamEnvelope(string RunId, long Sequence, string Kind, object Data); + +public sealed record MccRunStartedData(string Model, string McpEndpoint, DateTimeOffset StartedAtUtc); + +public sealed record MccGuidanceLoadedData( + string SourceTool, + string CanonicalPromptName, + string GuidanceVersion, + MccCapabilityStatus CapabilityStatus); + +public sealed record MccStateSummaryData( + int TurnCount, + int ToolCallCount, + bool SoftFinish, + int DirectAnswerAttempts, + IReadOnlyList OpenVerification, + IReadOnlyList RecentEvidence, + string? CompactionSummary); + +public sealed record MccToolCalledData(string CallId, string Name, string ArgumentsJson, bool Advanced, bool Sensitive); + +public sealed record MccToolResultData( + string CallId, + string Name, + bool IsError, + bool Success, + string? ErrorCode, + string Summary, + string RawText, + string EvidenceId); + +public sealed record MccVerificationEventData(string ObligationId, string ToolName, string Kind, string Description); + +public sealed record MccBudgetData( + int TurnCount, + int MaxTurns, + int ToolCallCount, + int MaxToolCalls, + double ElapsedSeconds, + int MaxWallClockSeconds); + +public sealed record MccErrorData(string Code, string Message, string? Detail = null); + +public sealed record MccFinalPayload( + string Status, + string Headline, + string AnswerMarkdown, + IReadOnlyList VerifiedFacts, + IReadOnlyList OpenIssues, + IReadOnlyList EvidenceIds, + string? NextAction); + +public sealed record MccSubmitFinalArgs( + string Status, + string Headline, + string AnswerMarkdown, + IReadOnlyList VerifiedFacts, + IReadOnlyList OpenIssues, + IReadOnlyList EvidenceIds, + string? NextAction); + +public sealed record MccCapabilityStatus( + [property: JsonPropertyName("sessionStatus")] bool SessionStatus, + [property: JsonPropertyName("chatAndCommands")] bool ChatAndCommands, + [property: JsonPropertyName("movement")] bool Movement, + [property: JsonPropertyName("inventory")] bool Inventory, + [property: JsonPropertyName("entityWorld")] bool EntityWorld); + +public sealed record MccEvidenceView(string Id, string ToolName, string Summary, bool IsError); + +public sealed record MccVerificationObligationView(string Id, string ToolName, string Kind, string Description); diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccAgentRunService.cs b/DebugTools/MccMcpWebPlayground/Harness/MccAgentRunService.cs new file mode 100644 index 00000000..33259389 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccAgentRunService.cs @@ -0,0 +1,852 @@ +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Net.ServerSentEvents; +using System.Text; +using System.Text.Json; +using DebugTools.MccMcpWebPlayground.Contracts; +using DebugTools.MccMcpWebPlayground.Infrastructure.Mcp; +using DebugTools.MccMcpWebPlayground.Infrastructure.OpenRouter; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol; + +namespace DebugTools.MccMcpWebPlayground.Harness; + +public interface IMccAgentRunService +{ + IAsyncEnumerable> StreamAsync(ChatStreamRequest request, HttpContext httpContext, CancellationToken cancellationToken); +} + +public sealed class MccAgentRunService : IMccAgentRunService +{ + private readonly MccMcpSessionFactory sessionFactory; + private readonly MccGuidanceSource guidanceSource; + private readonly MccPromptComposer promptComposer; + private readonly MccContextCompressor contextCompressor; + private readonly MccFinalizer finalizer; + private readonly OpenRouterChatClient openRouterChatClient; + private readonly MccWebHarnessOptions options; + + public MccAgentRunService( + MccMcpSessionFactory sessionFactory, + MccGuidanceSource guidanceSource, + MccPromptComposer promptComposer, + MccContextCompressor contextCompressor, + MccFinalizer finalizer, + OpenRouterChatClient openRouterChatClient, + IOptions options) + { + this.sessionFactory = sessionFactory; + this.guidanceSource = guidanceSource; + this.promptComposer = promptComposer; + this.contextCompressor = contextCompressor; + this.finalizer = finalizer; + this.openRouterChatClient = openRouterChatClient; + this.options = options.Value; + } + + public async IAsyncEnumerable> StreamAsync( + ChatStreamRequest request, + HttpContext httpContext, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, httpContext.RequestAborted); + CancellationToken linkedToken = linkedCts.Token; + + string runId = Guid.NewGuid().ToString("n"); + long sequence = 0; + + string? model = options.ResolveModel(); + if (string.IsNullOrWhiteSpace(model)) + { + yield return CreateEvent(runId, ref sequence, "error", new MccErrorData("configuration_error", "OPENROUTER_MODEL or MccWebHarness:Model must be configured.")); + yield break; + } + + if (!options.HasApiKeyConfigured()) + { + yield return CreateEvent(runId, ref sequence, "error", new MccErrorData("configuration_error", "OPENROUTER_API_KEY is not set.")); + yield break; + } + + List baseConversationMessages = NormalizeConversation(request.Messages); + string userRequest = ExtractUserRequest(request.Messages); + if (string.IsNullOrWhiteSpace(userRequest)) + { + yield return CreateEvent(runId, ref sequence, "error", new MccErrorData("invalid_request", "No user message was provided.")); + yield break; + } + + await using McpClient client = await sessionFactory.CreateAsync(linkedToken); + MccGuidanceBundle guidance = await guidanceSource.LoadAsync(client, linkedToken); + + MccRunState runState = new() + { + RunId = runId, + UserRequest = userRequest, + BaseConversationMessages = baseConversationMessages, + ConfiguredModel = model, + Guidance = guidance + }; + + yield return CreateEvent(runId, ref sequence, "run_started", new MccRunStartedData(model, options.ResolveMcpEndpoint(), runState.StartedAtUtc)); + yield return CreateEvent(runId, ref sequence, "guidance_loaded", new MccGuidanceLoadedData( + guidance.SourceToolName, + guidance.CanonicalPromptName, + guidance.GuidanceVersion, + guidance.CapabilityStatus)); + + IList tools = await client.ListToolsAsync(cancellationToken: linkedToken); + MccToolCatalog catalog = MccToolPolicy.BuildCatalog(tools, options, finalizer.BuildSubmitToolSchema()); + + while (!linkedToken.IsCancellationRequested) + { + runState.TurnCount++; + contextCompressor.CompactIfNeeded(runState); + yield return CreateEvent(runId, ref sequence, "state_summary", BuildStateSummary(runState, options)); + + if (runState.IsSoftFinish(options, DateTimeOffset.UtcNow)) + { + yield return CreateEvent(runId, ref sequence, "budget", BuildBudgetData(runState)); + } + + if (runState.IsHardStop(options, DateTimeOffset.UtcNow)) + break; + + MccModelTurn? turn = null; + Exception? providerException = null; + try + { + turn = await openRouterChatClient.CreateTurnAsync( + promptComposer.Compose(runState), + catalog.ModelVisibleTools, + options, + linkedToken); + } + catch (Exception ex) + { + providerException = ex; + } + + if (providerException is not null || turn is null) + { + yield return CreateEvent(runId, ref sequence, "error", new MccErrorData("provider_error", "OpenRouter request failed.", providerException?.Message)); + yield return CreateEvent(runId, ref sequence, "final", finalizer.BuildHardStopResult(runState, options)); + yield break; + } + + runState.RoutedModel = turn.ModelId; + runState.RoutedProvider = turn.RoutedProvider; + + if (turn.ToolCalls.Count == 0) + { + runState.DirectAnswerAttempts++; + string content = string.IsNullOrWhiteSpace(turn.AssistantContent) ? "(empty assistant turn)" : turn.AssistantContent.Trim(); + runState.ToolConversationMessages.Add(new Dictionary + { + ["role"] = "assistant", + ["content"] = content + }); + + if (runState.DirectAnswerAttempts >= 4) + { + yield return CreateEvent(runId, ref sequence, "error", new MccErrorData( + "model_protocol_error", + "The model kept returning plain assistant text instead of using tools or mcc_submit_final.", + content)); + yield return CreateEvent(runId, ref sequence, "final", finalizer.BuildHardStopResult(runState, options)); + yield break; + } + + runState.ToolConversationMessages.Add(new Dictionary + { + ["role"] = "user", + ["content"] = "The previous plain assistant text was not accepted by this harness. On your next turn, you must either call the relevant MCC tools or call mcc_submit_final. Do not answer with plain assistant text again." + }); + continue; + } + + Dictionary assistantMessage = new() + { + ["role"] = "assistant", + ["content"] = turn.AssistantContent, + ["tool_calls"] = turn.ToolCalls.Select(call => new Dictionary + { + ["id"] = call.CallId, + ["type"] = "function", + ["function"] = new Dictionary + { + ["name"] = call.Name, + ["arguments"] = call.ArgumentsJson + } + }).ToArray() + }; + runState.ToolConversationMessages.Add(assistantMessage); + + foreach (MccModelToolCall toolCall in turn.ToolCalls) + { + MccToolProfile profile = MccToolPolicy.GetProfile(toolCall.Name); + yield return CreateEvent(runId, ref sequence, "tool_called", new MccToolCalledData( + toolCall.CallId, + toolCall.Name, + toolCall.ArgumentsJson, + profile.Risk == MccToolRisk.EscapeHatch, + profile.Risk == MccToolRisk.Sensitive)); + + if (toolCall.Name.Equals("mcc_submit_final", StringComparison.OrdinalIgnoreCase)) + { + MccFinalizationValidation validation = finalizer.Validate(runState, toolCall.ArgumentsJson); + if (validation.Accepted) + { + yield return CreateEvent(runId, ref sequence, "final", validation.Payload!); + yield break; + } + + string localResultText = JsonSerializer.Serialize(new + { + success = false, + errorCode = "invalid_final_submission", + message = validation.ErrorText + }); + runState.ToolConversationMessages.Add(BuildToolMessage(toolCall.CallId, localResultText)); + yield return CreateEvent(runId, ref sequence, "tool_result", new MccToolResultData( + toolCall.CallId, + toolCall.Name, + IsError: true, + Success: false, + ErrorCode: "invalid_final_submission", + Summary: validation.ErrorText ?? "Invalid final submission.", + RawText: localResultText, + EvidenceId: string.Empty)); + continue; + } + + if (MccToolPolicy.RequiresExplicitUserIntent(toolCall.Name) && !MccToolPolicy.HasExplicitUserIntent(runState.UserRequest, toolCall.Name)) + { + string localResultText = JsonSerializer.Serialize(new + { + success = false, + errorCode = "explicit_user_intent_required", + message = $"Tool '{toolCall.Name}' requires explicit user intent." + }); + runState.ToolConversationMessages.Add(BuildToolMessage(toolCall.CallId, localResultText)); + yield return CreateEvent(runId, ref sequence, "tool_result", new MccToolResultData( + toolCall.CallId, + toolCall.Name, + IsError: true, + Success: false, + ErrorCode: "explicit_user_intent_required", + Summary: $"Tool '{toolCall.Name}' requires explicit user intent.", + RawText: localResultText, + EvidenceId: string.Empty)); + continue; + } + + if (!catalog.ToolsByName.TryGetValue(toolCall.Name, out MccToolCatalogEntry? entry)) + { + string unknownToolText = JsonSerializer.Serialize(new + { + success = false, + errorCode = "unknown_tool", + message = $"Unknown tool '{toolCall.Name}'." + }); + runState.ToolConversationMessages.Add(BuildToolMessage(toolCall.CallId, unknownToolText)); + yield return CreateEvent(runId, ref sequence, "tool_result", new MccToolResultData( + toolCall.CallId, + toolCall.Name, + IsError: true, + Success: false, + ErrorCode: "unknown_tool", + Summary: $"Unknown tool '{toolCall.Name}'.", + RawText: unknownToolText, + EvidenceId: string.Empty)); + continue; + } + + CallToolResult? result = null; + Exception? toolException = null; + try + { + Dictionary arguments = MccJsonArguments.Parse(toolCall.ArgumentsJson); + result = await client.CallToolAsync(toolCall.Name, arguments, cancellationToken: linkedToken); + } + catch (Exception ex) + { + toolException = ex; + } + + if (toolException is not null || result is null) + { + string failedText = JsonSerializer.Serialize(new + { + success = false, + errorCode = "tool_call_failed", + message = toolException?.Message + }); + runState.ToolConversationMessages.Add(BuildToolMessage(toolCall.CallId, failedText)); + yield return CreateEvent(runId, ref sequence, "tool_result", new MccToolResultData( + toolCall.CallId, + toolCall.Name, + IsError: true, + Success: false, + ErrorCode: "tool_call_failed", + Summary: toolException?.Message ?? "Tool call failed.", + RawText: failedText, + EvidenceId: string.Empty)); + continue; + } + + runState.ToolCallCount++; + MccNormalizedToolResult normalized = MccMcpJson.Normalize(result); + MccEvidenceRecord evidence = CreateEvidence(runState, toolCall.Name, normalized); + runState.Evidence.Add(evidence); + runState.ToolExecutions.Add(new MccToolExecutionRecord + { + CallId = toolCall.CallId, + ToolName = toolCall.Name, + ArgumentsJson = toolCall.ArgumentsJson, + Evidence = evidence + }); + + runState.ToolConversationMessages.Add(BuildToolMessage(toolCall.CallId, normalized.Text)); + + foreach (MccVerificationObligation obligation in CreateObligations(runState, evidence, toolCall.ArgumentsJson)) + { + runState.VerificationObligations.Add(obligation); + yield return CreateEvent(runId, ref sequence, "verification_required", new MccVerificationEventData( + obligation.Id, + obligation.ToolName, + obligation.Kind, + obligation.Description)); + + if (obligation.Cleared) + { + yield return CreateEvent(runId, ref sequence, "verification_cleared", new MccVerificationEventData( + obligation.Id, + obligation.ToolName, + obligation.Kind, + obligation.Description)); + } + } + + foreach (MccVerificationObligation cleared in TryClearObligationsFromEvidence(runState, evidence)) + { + yield return CreateEvent(runId, ref sequence, "verification_cleared", new MccVerificationEventData( + cleared.Id, + cleared.ToolName, + cleared.Kind, + cleared.Description)); + } + + yield return CreateEvent(runId, ref sequence, "tool_result", new MccToolResultData( + toolCall.CallId, + toolCall.Name, + evidence.IsError, + evidence.Success, + evidence.ErrorCode, + evidence.Summary, + evidence.RawText, + evidence.Id)); + } + } + + yield return CreateEvent(runId, ref sequence, "final", finalizer.BuildHardStopResult(runState, options)); + } + + private static List NormalizeConversation(List? incoming) + { + List messages = []; + if (incoming is null) + return messages; + + foreach (ChatMessage message in incoming) + { + if (string.IsNullOrWhiteSpace(message.Role) || string.IsNullOrWhiteSpace(message.Content)) + continue; + + string role = message.Role.Trim().ToLowerInvariant(); + if (role is not ("user" or "assistant" or "system")) + continue; + + messages.Add(new Dictionary + { + ["role"] = role, + ["content"] = message.Content.Trim() + }); + } + + return messages; + } + + private static string ExtractUserRequest(List? incoming) + { + return incoming? + .LastOrDefault(message => string.Equals(message.Role, "user", StringComparison.OrdinalIgnoreCase) + && !string.IsNullOrWhiteSpace(message.Content)) + ?.Content + ?.Trim() + ?? string.Empty; + } + + private static Dictionary BuildToolMessage(string callId, string content) + { + return new Dictionary + { + ["role"] = "tool", + ["tool_call_id"] = callId, + ["content"] = content + }; + } + + private static MccStateSummaryData BuildStateSummary(MccRunState runState, MccWebHarnessOptions options) + { + return new MccStateSummaryData( + TurnCount: runState.TurnCount, + ToolCallCount: runState.ToolCallCount, + SoftFinish: runState.IsSoftFinish(options, DateTimeOffset.UtcNow), + DirectAnswerAttempts: runState.DirectAnswerAttempts, + OpenVerification: runState.OpenObligations + .Select(obligation => new MccVerificationObligationView(obligation.Id, obligation.ToolName, obligation.Kind, obligation.Description)) + .ToArray(), + RecentEvidence: runState.Evidence + .TakeLast(6) + .Select(evidence => new MccEvidenceView(evidence.Id, evidence.ToolName, evidence.Summary, evidence.IsError)) + .ToArray(), + CompactionSummary: runState.CompactionSummary); + } + + private MccBudgetData BuildBudgetData(MccRunState runState) + { + return new MccBudgetData( + TurnCount: runState.TurnCount, + MaxTurns: options.MaxTurns, + ToolCallCount: runState.ToolCallCount, + MaxToolCalls: options.MaxToolCalls, + ElapsedSeconds: (DateTimeOffset.UtcNow - runState.StartedAtUtc).TotalSeconds, + MaxWallClockSeconds: options.MaxWallClockSeconds); + } + + private static MccEvidenceRecord CreateEvidence(MccRunState runState, string toolName, MccNormalizedToolResult result) + { + string summary = SummarizeEvidence(toolName, result); + return new MccEvidenceRecord + { + Id = runState.NextEvidenceId(), + ToolName = toolName, + Summary = summary, + RawText = result.Text, + IsError = result.IsError, + Success = result.Success, + ErrorCode = result.ErrorCode, + Root = result.Root, + Data = result.Data + }; + } + + private static string SummarizeEvidence(string toolName, MccNormalizedToolResult result) + { + if (result.Data is JsonElement data) + { + if ((toolName.Equals("mcc_move_to", StringComparison.OrdinalIgnoreCase) || toolName.Equals("mcc_move_to_player", StringComparison.OrdinalIgnoreCase)) + && TryReadBool(data, "arrived", out bool arrived)) + { + return arrived + ? $"movement verified; arrived={arrived}" + : $"movement not yet verified; arrived={arrived}"; + } + + if (toolName.Equals("mcc_dig_block", StringComparison.OrdinalIgnoreCase)) + { + bool destroyed = TryReadBool(data, "destroyed", out bool destroyedValue) && destroyedValue; + bool changed = TryReadBool(data, "changed", out bool changedValue) && changedValue; + return $"dig result changed={changed} destroyed={destroyed}"; + } + + if (toolName.Equals("mcc_items_pickup", StringComparison.OrdinalIgnoreCase)) + { + int successful = TryReadInt(data, "successfulPickups", out int successfulValue) ? successfulValue : 0; + int collected = TryReadInt(data, "collectedCount", out int collectedValue) ? collectedValue : 0; + return $"pickup result successfulPickups={successful} collectedCount={collected}"; + } + + if (toolName.Equals("mcc_container_open_at", StringComparison.OrdinalIgnoreCase) + && TryReadBool(data, "opened", out bool opened)) + { + return $"container open result opened={opened}"; + } + + if (toolName is "mcc_container_deposit_item" or "mcc_container_withdraw_item" or "mcc_inventory_drop_item") + { + int moved = TryReadInt(data, "movedCount", out int movedValue) + ? movedValue + : TryReadInt(data, "droppedCount", out int droppedValue) ? droppedValue : 0; + return $"{toolName} movedCount={moved}"; + } + } + + string prefix = result.IsError ? "error" : "ok"; + return $"{prefix}: {Truncate(result.Text.Replace('\n', ' '), 180)}"; + } + + private List CreateObligations(MccRunState runState, MccEvidenceRecord evidence, string argumentsJson) + { + List obligations = []; + JsonElement metadata = ParseArgumentsToJson(argumentsJson); + + if (evidence.ToolName.Equals("mcc_move_to", StringComparison.OrdinalIgnoreCase)) + { + MccVerificationObligation obligation = new() + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "movement", + Description = "Verify final player location for the requested move target.", + SourceEvidenceId = evidence.Id, + Metadata = BuildMoveMetadata(evidence, metadata), + Cleared = IsMovementVerified(evidence) + }; + obligations.Add(obligation); + return obligations; + } + + if (evidence.ToolName.Equals("mcc_move_to_player", StringComparison.OrdinalIgnoreCase)) + { + MccVerificationObligation obligation = new() + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "movement", + Description = "Verify final proximity to the requested player target.", + SourceEvidenceId = evidence.Id, + Metadata = BuildMoveToPlayerMetadata(evidence, metadata), + Cleared = IsMovementVerified(evidence) + }; + obligations.Add(obligation); + return obligations; + } + + if (evidence.ToolName.Equals("mcc_container_open_at", StringComparison.OrdinalIgnoreCase)) + { + obligations.Add(new MccVerificationObligation + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "container", + Description = "Verify that the target container is open and active.", + SourceEvidenceId = evidence.Id, + Metadata = null, + Cleared = IsContainerOpenVerified(evidence) + }); + return obligations; + } + + if (evidence.ToolName is "mcc_container_deposit_item" or "mcc_container_withdraw_item" or "mcc_inventory_drop_item") + { + obligations.Add(new MccVerificationObligation + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "inventory", + Description = "Verify the requested inventory delta.", + SourceEvidenceId = evidence.Id, + Metadata = evidence.Data, + Cleared = IsInventoryVerified(evidence) + }); + return obligations; + } + + if (evidence.ToolName.Equals("mcc_items_pickup", StringComparison.OrdinalIgnoreCase)) + { + obligations.Add(new MccVerificationObligation + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "pickup", + Description = "Verify that the requested dropped items were picked up.", + SourceEvidenceId = evidence.Id, + Metadata = evidence.Data, + Cleared = IsPickupVerified(evidence) + }); + return obligations; + } + + if (evidence.ToolName.Equals("mcc_dig_block", StringComparison.OrdinalIgnoreCase)) + { + obligations.Add(new MccVerificationObligation + { + Id = runState.NextObligationId(), + ToolName = evidence.ToolName, + Kind = "block_change", + Description = "Verify that the target block changed state after digging.", + SourceEvidenceId = evidence.Id, + Metadata = evidence.Data, + Cleared = IsDigVerified(evidence) + }); + } + + return obligations; + } + + private List TryClearObligationsFromEvidence(MccRunState runState, MccEvidenceRecord evidence) + { + List cleared = []; + foreach (MccVerificationObligation obligation in runState.OpenObligations) + { + if (obligation.Cleared) + continue; + + if (obligation.Kind == "movement" && TryClearMovementObligation(obligation, evidence)) + { + obligation.Cleared = true; + obligation.ClearedByEvidenceId = evidence.Id; + cleared.Add(obligation); + continue; + } + + if (obligation.Kind == "block_change" && TryClearDigObligation(obligation, evidence)) + { + obligation.Cleared = true; + obligation.ClearedByEvidenceId = evidence.Id; + cleared.Add(obligation); + } + } + + return cleared; + } + + private static bool TryClearMovementObligation(MccVerificationObligation obligation, MccEvidenceRecord evidence) + { + if (evidence.ToolName.Equals("mcc_player_state", StringComparison.OrdinalIgnoreCase) + && evidence.Data is JsonElement data + && data.TryGetProperty("location", out JsonElement location) + && obligation.Metadata is JsonElement metadata) + { + if (obligation.ToolName.Equals("mcc_move_to", StringComparison.OrdinalIgnoreCase) + && metadata.TryGetProperty("x", out JsonElement targetX) + && metadata.TryGetProperty("y", out JsonElement targetY) + && metadata.TryGetProperty("z", out JsonElement targetZ)) + { + double tolerance = metadata.TryGetProperty("tolerance", out JsonElement toleranceElement) && toleranceElement.TryGetDouble(out double tol) ? tol : 1.5; + return TryReadDouble(location, "x", out double x) + && TryReadDouble(location, "y", out double y) + && TryReadDouble(location, "z", out double z) + && Distance(x, y, z, targetX.GetDouble(), targetY.GetDouble(), targetZ.GetDouble()) <= tolerance; + } + } + + if (evidence.ToolName.Equals("mcc_player_locate", StringComparison.OrdinalIgnoreCase) + && obligation.ToolName.Equals("mcc_move_to_player", StringComparison.OrdinalIgnoreCase) + && evidence.Data is JsonElement playerData + && obligation.Metadata is JsonElement playerMetadata) + { + string? expectedName = playerMetadata.TryGetProperty("playerName", out JsonElement nameElement) ? nameElement.GetString() : null; + string? matchedName = playerData.TryGetProperty("matchedName", out JsonElement matchedNameElement) ? matchedNameElement.GetString() : null; + if (!string.IsNullOrWhiteSpace(expectedName) && !string.Equals(expectedName, matchedName, StringComparison.OrdinalIgnoreCase)) + return false; + + if (TryReadDouble(playerData, "distance", out double distance)) + { + double tolerance = playerMetadata.TryGetProperty("tolerance", out JsonElement toleranceElement) && toleranceElement.TryGetDouble(out double tol) ? tol : 2.0; + return distance <= tolerance; + } + } + + return false; + } + + private static bool TryClearDigObligation(MccVerificationObligation obligation, MccEvidenceRecord evidence) + { + if (!evidence.ToolName.Equals("mcc_world_block_at", StringComparison.OrdinalIgnoreCase) + || evidence.Data is not JsonElement data + || obligation.Metadata is not JsonElement metadata) + { + return false; + } + + if (!metadata.TryGetProperty("target", out JsonElement target) + || !TryReadDouble(target, "x", out double x) + || !TryReadDouble(target, "y", out double y) + || !TryReadDouble(target, "z", out double z)) + { + return false; + } + + return TryReadInt(data, "x", out int blockX) + && TryReadInt(data, "y", out int blockY) + && TryReadInt(data, "z", out int blockZ) + && Math.Abs(blockX - x) < 0.5 + && Math.Abs(blockY - y) < 0.5 + && Math.Abs(blockZ - z) < 0.5 + && data.TryGetProperty("block", out JsonElement block) + && block.TryGetProperty("material", out JsonElement material) + && !string.Equals(material.GetString(), "Air", StringComparison.OrdinalIgnoreCase); + } + + private static bool IsMovementVerified(MccEvidenceRecord evidence) + { + if (evidence.Data is not JsonElement data) + return false; + + if (TryReadBool(data, "arrived", out bool arrived) && arrived) + return true; + + if (TryReadDouble(data, "finalDistance", out double finalDistance)) + { + double tolerance = TryReadDouble(data, "tolerance", out double tol) ? tol : 1.5; + return finalDistance <= tolerance; + } + + return false; + } + + private static bool IsContainerOpenVerified(MccEvidenceRecord evidence) + { + return evidence.Data is JsonElement data + && TryReadBool(data, "opened", out bool opened) + && opened; + } + + private static bool IsInventoryVerified(MccEvidenceRecord evidence) + { + if (evidence.Data is not JsonElement data) + return false; + + if (TryReadInt(data, "requestedCount", out int requestedCount) + && TryReadInt(data, "movedCount", out int movedCount)) + { + return movedCount == requestedCount; + } + + if (TryReadInt(data, "requestedCount", out requestedCount) + && TryReadInt(data, "droppedCount", out int droppedCount)) + { + return droppedCount == requestedCount; + } + + return evidence.Success; + } + + private static bool IsPickupVerified(MccEvidenceRecord evidence) + { + if (evidence.Data is not JsonElement data) + return false; + + return (TryReadInt(data, "successfulPickups", out int successfulPickups) && successfulPickups > 0) + || (TryReadInt(data, "collectedCount", out int collectedCount) && collectedCount > 0); + } + + private static bool IsDigVerified(MccEvidenceRecord evidence) + { + if (evidence.Data is not JsonElement data) + return false; + + return (TryReadBool(data, "destroyed", out bool destroyed) && destroyed) + || (TryReadBool(data, "changed", out bool changed) && changed); + } + + private static JsonElement? BuildMoveMetadata(MccEvidenceRecord evidence, JsonElement arguments) + { + if (evidence.Data is not JsonElement data) + return null; + + double x = TryReadDoubleFromArguments(arguments, "x", out double targetX) + ? targetX + : data.TryGetProperty("target", out JsonElement target) && TryReadDouble(target, "x", out double fromDataX) ? fromDataX : 0; + double y = TryReadDoubleFromArguments(arguments, "y", out double targetY) + ? targetY + : data.TryGetProperty("target", out target) && TryReadDouble(target, "y", out double fromDataY) ? fromDataY : 0; + double z = TryReadDoubleFromArguments(arguments, "z", out double targetZ) + ? targetZ + : data.TryGetProperty("target", out target) && TryReadDouble(target, "z", out double fromDataZ) ? fromDataZ : 0; + double tolerance = TryReadDouble(data, "tolerance", out double tol) ? tol : 1.5; + + return JsonSerializer.SerializeToElement(new + { + x, + y, + z, + tolerance + }); + } + + private static JsonElement? BuildMoveToPlayerMetadata(MccEvidenceRecord evidence, JsonElement arguments) + { + string? playerName = arguments.TryGetProperty("playerName", out JsonElement property) ? property.GetString() : null; + double tolerance = evidence.Data is JsonElement data && TryReadDouble(data, "tolerance", out double tol) ? tol : 2.0; + return JsonSerializer.SerializeToElement(new + { + playerName, + tolerance + }); + } + + private static JsonElement ParseArgumentsToJson(string argumentsJson) + { + try + { + using JsonDocument document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson); + return document.RootElement.Clone(); + } + catch + { + using JsonDocument document = JsonDocument.Parse("{}"); + return document.RootElement.Clone(); + } + } + + private static bool TryReadBool(JsonElement element, string propertyName, out bool value) + { + value = false; + return element.TryGetProperty(propertyName, out JsonElement property) + && property.ValueKind is JsonValueKind.True or JsonValueKind.False + && ((value = property.GetBoolean()) || !value || true); + } + + private static bool TryReadInt(JsonElement element, string propertyName, out int value) + { + value = 0; + return element.TryGetProperty(propertyName, out JsonElement property) && property.TryGetInt32(out value); + } + + private static bool TryReadDouble(JsonElement element, string propertyName, out double value) + { + value = 0; + return element.TryGetProperty(propertyName, out JsonElement property) && property.TryGetDouble(out value); + } + + private static bool TryReadDoubleFromArguments(JsonElement element, string propertyName, out double value) + { + value = 0; + if (!element.TryGetProperty(propertyName, out JsonElement property)) + return false; + + return property.ValueKind == JsonValueKind.Number + ? property.TryGetDouble(out value) + : property.ValueKind == JsonValueKind.String && double.TryParse(property.GetString(), out value); + } + + private static double Distance(double x1, double y1, double z1, double x2, double y2, double z2) + { + double dx = x1 - x2; + double dy = y1 - y2; + double dz = z1 - z2; + return Math.Sqrt(dx * dx + dy * dy + dz * dz); + } + + private static string Truncate(string text, int maxLength) + { + return string.IsNullOrEmpty(text) || text.Length <= maxLength ? text : text[..maxLength] + "..."; + } + + private static SseItem CreateEvent(string runId, ref long sequence, string kind, T data) + { + sequence++; + return new SseItem( + new MccStreamEnvelope(runId, sequence, kind, data!), + kind) + { + EventId = sequence.ToString(CultureInfo.InvariantCulture) + }; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccContextCompressor.cs b/DebugTools/MccMcpWebPlayground/Harness/MccContextCompressor.cs new file mode 100644 index 00000000..1d131bda --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccContextCompressor.cs @@ -0,0 +1,21 @@ +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccContextCompressor +{ + public void CompactIfNeeded(MccRunState runState) + { + if (runState.Evidence.Count <= 6) + return; + + IReadOnlyList olderEvidence = runState.Evidence + .Take(Math.Max(0, runState.Evidence.Count - 6)) + .ToArray(); + + if (olderEvidence.Count == 0) + return; + + runState.CompactionSummary = string.Join('\n', olderEvidence + .TakeLast(8) + .Select(record => $"- {record.Id} {record.ToolName}: {record.Summary}")); + } +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccFinalizer.cs b/DebugTools/MccMcpWebPlayground/Harness/MccFinalizer.cs new file mode 100644 index 00000000..88978bc6 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccFinalizer.cs @@ -0,0 +1,221 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using DebugTools.MccMcpWebPlayground.Contracts; + +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccFinalizer +{ + private static readonly string[] AllowedStatuses = ["completed", "partial", "blocked", "clarification_needed", "failed"]; + + public object BuildSubmitToolSchema() + { + return new Dictionary + { + ["type"] = "function", + ["function"] = new Dictionary + { + ["name"] = "mcc_submit_final", + ["description"] = "Submit the final result for this MCC run. Use completed only when no required verification obligations remain open.", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["additionalProperties"] = false, + ["properties"] = new JsonObject + { + ["status"] = new JsonObject + { + ["type"] = "string", + ["enum"] = new JsonArray(AllowedStatuses.Select(status => JsonValue.Create(status)).ToArray()) + }, + ["headline"] = new JsonObject { ["type"] = "string" }, + ["answerMarkdown"] = new JsonObject { ["type"] = "string" }, + ["verifiedFacts"] = new JsonObject + { + ["type"] = "array", + ["items"] = new JsonObject { ["type"] = "string" } + }, + ["openIssues"] = new JsonObject + { + ["type"] = "array", + ["items"] = new JsonObject { ["type"] = "string" } + }, + ["evidenceIds"] = new JsonObject + { + ["type"] = "array", + ["items"] = new JsonObject { ["type"] = "string" } + }, + ["nextAction"] = new JsonObject + { + ["type"] = new JsonArray("string", "null") + } + }, + ["required"] = new JsonArray("status", "headline", "answerMarkdown", "verifiedFacts", "openIssues", "evidenceIds", "nextAction") + } + } + }; + } + + public MccFinalizationValidation Validate(MccRunState runState, string argumentsJson) + { + try + { + using JsonDocument document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson); + JsonElement root = document.RootElement; + MccSubmitFinalArgs submission = new( + Status: ReadRequiredString(root, "status"), + Headline: ReadRequiredString(root, "headline"), + AnswerMarkdown: ReadRequiredString(root, "answerMarkdown"), + VerifiedFacts: ReadStringArray(root, "verifiedFacts"), + OpenIssues: ReadStringArray(root, "openIssues"), + EvidenceIds: ReadStringArray(root, "evidenceIds"), + NextAction: ReadNullableString(root, "nextAction")); + + string normalizedStatus = submission.Status.Trim().ToLowerInvariant(); + if (!AllowedStatuses.Contains(normalizedStatus, StringComparer.Ordinal)) + return MccFinalizationValidation.Reject("Invalid final status."); + + if (string.IsNullOrWhiteSpace(submission.Headline) || string.IsNullOrWhiteSpace(submission.AnswerMarkdown)) + return MccFinalizationValidation.Reject("headline and answerMarkdown are required."); + + Dictionary evidenceById = runState.Evidence.ToDictionary(record => record.Id, StringComparer.OrdinalIgnoreCase); + Dictionary evidenceAliasByCallId = new(StringComparer.OrdinalIgnoreCase); + foreach (MccToolExecutionRecord execution in runState.ToolExecutions) + { + evidenceAliasByCallId[execution.CallId] = execution.Evidence.Id; + + int suffixSeparator = execution.CallId.LastIndexOf('_'); + if (suffixSeparator >= 0 && suffixSeparator < execution.CallId.Length - 1) + evidenceAliasByCallId[execution.CallId[(suffixSeparator + 1)..]] = execution.Evidence.Id; + } + + List normalizedEvidenceIds = []; + foreach (string evidenceId in submission.EvidenceIds) + { + string normalizedEvidenceId = evidenceAliasByCallId.TryGetValue(evidenceId, out string? mappedEvidenceId) + ? mappedEvidenceId + : evidenceId; + + if (!evidenceById.ContainsKey(normalizedEvidenceId)) + return MccFinalizationValidation.Reject($"Unknown evidence id '{evidenceId}'."); + + if (!normalizedEvidenceIds.Contains(normalizedEvidenceId, StringComparer.OrdinalIgnoreCase)) + normalizedEvidenceIds.Add(normalizedEvidenceId); + } + + if (normalizedStatus == "completed" && runState.OpenObligations.Count > 0) + return MccFinalizationValidation.Reject("completed is invalid while verification obligations remain open."); + + if (!AreVerifiedFactsGrounded(submission.VerifiedFacts, normalizedEvidenceIds, evidenceById)) + return MccFinalizationValidation.Reject("verifiedFacts must be grounded in the referenced evidence."); + + return MccFinalizationValidation.Accept(new MccFinalPayload( + normalizedStatus, + submission.Headline.Trim(), + submission.AnswerMarkdown.Trim(), + submission.VerifiedFacts, + submission.OpenIssues, + normalizedEvidenceIds, + string.IsNullOrWhiteSpace(submission.NextAction) ? null : submission.NextAction.Trim())); + } + catch (Exception ex) + { + return MccFinalizationValidation.Reject($"Invalid mcc_submit_final payload: {ex.Message}"); + } + } + + public MccFinalPayload BuildHardStopResult(MccRunState runState, MccWebHarnessOptions options) + { + IReadOnlyList openIssues = runState.OpenObligations.Count > 0 + ? runState.OpenObligations.Select(obligation => obligation.Description).ToArray() + : ["The harness reached its execution budget before the run was explicitly finalized."]; + + IReadOnlyList evidenceIds = runState.Evidence.TakeLast(4).Select(record => record.Id).ToArray(); + IReadOnlyList verifiedFacts = runState.Evidence + .TakeLast(4) + .Where(record => record.Success) + .Select(record => record.Summary) + .ToArray(); + + return new MccFinalPayload( + Status: runState.OpenObligations.Count > 0 ? "partial" : "blocked", + Headline: "Run stopped before explicit completion", + AnswerMarkdown: "I could not finish the request within the current harness budget. I am returning the strongest verified state captured so far.", + VerifiedFacts: verifiedFacts, + OpenIssues: openIssues, + EvidenceIds: evidenceIds, + NextAction: "Retry with a fresh run if you want me to continue from the latest verified state."); + } + + private static bool AreVerifiedFactsGrounded( + IReadOnlyList verifiedFacts, + IReadOnlyList evidenceIds, + IReadOnlyDictionary evidenceById) + { + if (verifiedFacts.Count == 0) + return true; + + if (evidenceIds.Count == 0) + return false; + + string evidenceCorpus = string.Join(' ', evidenceIds + .Where(evidenceById.ContainsKey) + .Select(id => evidenceById[id].Summary)) + .ToLowerInvariant(); + + foreach (string fact in verifiedFacts) + { + HashSet factTokens = fact.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(token => token.Trim().Trim(',', '.', ':', ';', '!', '?', '"', '\'')) + .Where(token => token.Length >= 4) + .Select(token => token.ToLowerInvariant()) + .ToHashSet(StringComparer.Ordinal); + + if (factTokens.Count == 0) + continue; + + int matches = factTokens.Count(token => evidenceCorpus.Contains(token, StringComparison.Ordinal)); + if (matches < Math.Min(2, factTokens.Count)) + return false; + } + + return true; + } + + private static string ReadRequiredString(JsonElement root, string propertyName) + { + string? value = ReadNullableString(root, propertyName); + if (string.IsNullOrWhiteSpace(value)) + throw new InvalidOperationException($"{propertyName} is required."); + + return value.Trim(); + } + + private static string? ReadNullableString(JsonElement root, string propertyName) + { + if (!root.TryGetProperty(propertyName, out JsonElement property)) + return null; + + return property.ValueKind == JsonValueKind.Null ? null : property.GetString(); + } + + private static string[] ReadStringArray(JsonElement root, string propertyName) + { + if (!root.TryGetProperty(propertyName, out JsonElement property) || property.ValueKind != JsonValueKind.Array) + return []; + + return property.EnumerateArray() + .Where(item => item.ValueKind == JsonValueKind.String) + .Select(item => item.GetString()) + .Where(item => !string.IsNullOrWhiteSpace(item)) + .Cast() + .ToArray(); + } +} + +public sealed record MccFinalizationValidation(bool Accepted, string? ErrorText, MccFinalPayload? Payload) +{ + public static MccFinalizationValidation Accept(MccFinalPayload payload) => new(true, null, payload); + + public static MccFinalizationValidation Reject(string errorText) => new(false, errorText, null); +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccGuidanceSource.cs b/DebugTools/MccMcpWebPlayground/Harness/MccGuidanceSource.cs new file mode 100644 index 00000000..a302e3f0 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccGuidanceSource.cs @@ -0,0 +1,63 @@ +using System.Text.Json; +using DebugTools.MccMcpWebPlayground.Contracts; +using DebugTools.MccMcpWebPlayground.Infrastructure.Mcp; +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol; + +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccGuidanceSource +{ + public const string SourceToolName = "mcc_agent_guidance"; + public const string CanonicalPromptName = "mcc_operator_guide"; + + public async Task LoadAsync(McpClient client, CancellationToken cancellationToken) + { + CallToolResult result = await client.CallToolAsync(SourceToolName, new Dictionary(), cancellationToken: cancellationToken); + MccNormalizedToolResult normalized = MccMcpJson.Normalize(result); + JsonElement data = normalized.Data ?? throw new InvalidOperationException("mcc_agent_guidance did not return data."); + + string[] bestPractices = ReadStringArray(data, "bestPractices"); + string[] exampleTitles = data.TryGetProperty("exampleScenarios", out JsonElement examples) + && examples.ValueKind == JsonValueKind.Array + ? examples.EnumerateArray() + .Select(example => example.TryGetProperty("title", out JsonElement title) ? title.GetString() : null) + .Where(title => !string.IsNullOrWhiteSpace(title)) + .Cast() + .ToArray() + : []; + + MccCapabilityStatus capabilityStatus = data.TryGetProperty("capabilityStatus", out JsonElement capabilityJson) + ? JsonSerializer.Deserialize(capabilityJson.GetRawText()) ?? new MccCapabilityStatus(false, false, false, false, false) + : new MccCapabilityStatus(false, false, false, false, false); + + return new MccGuidanceBundle( + SourceToolName, + CanonicalPromptName, + SkillName: ReadString(data, "skillName") ?? "mcc-mcp-operator", + GuidanceVersion: ReadString(data, "guidanceVersion") ?? "unknown", + SystemPrompt: ReadString(data, "systemPrompt") ?? throw new InvalidOperationException("mcc_agent_guidance did not return systemPrompt."), + BestPractices: bestPractices, + ExampleScenarioTitles: exampleTitles, + CapabilityStatus: capabilityStatus); + } + + private static string? ReadString(JsonElement element, string propertyName) + { + return element.TryGetProperty(propertyName, out JsonElement property) && property.ValueKind == JsonValueKind.String + ? property.GetString() + : null; + } + + private static string[] ReadStringArray(JsonElement element, string propertyName) + { + return element.TryGetProperty(propertyName, out JsonElement property) && property.ValueKind == JsonValueKind.Array + ? property.EnumerateArray() + .Where(item => item.ValueKind == JsonValueKind.String) + .Select(item => item.GetString()) + .Where(item => !string.IsNullOrWhiteSpace(item)) + .Cast() + .ToArray() + : []; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccPromptComposer.cs b/DebugTools/MccMcpWebPlayground/Harness/MccPromptComposer.cs new file mode 100644 index 00000000..b9cb4650 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccPromptComposer.cs @@ -0,0 +1,86 @@ +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccPromptComposer +{ + private const string HarnessContract = """ +You are operating Minecraft Console Client through MCC MCP tools. + +Rules: +- Use tool results and the run-state summary as the source of truth. +- Execute tools sequentially. +- End the run only with mcc_submit_final. +- status=completed is valid only when no required verification obligations remain open. +- If the task is blocked or partial, say exactly what is verified and what remains unverified. +- Do not repeat the same failing stateful action with the same arguments. +- mcc_quit_client requires explicit user intent. +- Prefer structured high-level tools. Avoid escape hatches unless they are explicitly exposed and necessary. +"""; + + public List Compose(MccRunState runState) + { + List messages = + [ + BuildSystemMessage(HarnessContract), + BuildSystemMessage(runState.Guidance.SystemPrompt), + BuildSystemMessage(BuildStateSummary(runState)), + .. runState.BaseConversationMessages + ]; + + if (!string.IsNullOrWhiteSpace(runState.CompactionSummary)) + { + messages.Add(BuildSystemMessage($""" +Older verified evidence summary +{runState.CompactionSummary} +""")); + } + + foreach (object message in runState.ToolConversationMessages.TakeLast(12)) + messages.Add(message); + + return messages; + } + + private static Dictionary BuildSystemMessage(string text) + { + return new Dictionary + { + ["role"] = "system", + ["content"] = text + }; + } + + private static string BuildStateSummary(MccRunState runState) + { + string evidence = runState.Evidence.Count == 0 + ? "- none yet" + : string.Join('\n', runState.Evidence.TakeLast(6).Select(record => + $"- {record.Id} {record.ToolName}: {record.Summary}")); + + string obligations = runState.OpenObligations.Count == 0 + ? "- none" + : string.Join('\n', runState.OpenObligations.Select(obligation => + $"- {obligation.Id} {obligation.ToolName}/{obligation.Kind}: {obligation.Description}")); + + string bestPractices = runState.Guidance.BestPractices.Length == 0 + ? "- use verified MCC state before claiming success" + : string.Join('\n', runState.Guidance.BestPractices.Take(4).Select(item => $"- {item}")); + + return $""" +Current run state +- turnCount: {runState.TurnCount} +- toolCallCount: {runState.ToolCallCount} +- directAnswerAttempts: {runState.DirectAnswerAttempts} +- routedModel: {runState.RoutedModel ?? runState.ConfiguredModel} +- routedProvider: {runState.RoutedProvider ?? "unknown"} + +Outstanding verification +{obligations} + +Recent evidence +{evidence} + +Guidance highlights +{bestPractices} +"""; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccRunState.cs b/DebugTools/MccMcpWebPlayground/Harness/MccRunState.cs new file mode 100644 index 00000000..f226c8b3 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccRunState.cs @@ -0,0 +1,103 @@ +using System.Text.Json; +using DebugTools.MccMcpWebPlayground.Contracts; + +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccRunState +{ + private int evidenceCounter; + private int obligationCounter; + + public required string RunId { get; init; } + public required string UserRequest { get; init; } + public required List BaseConversationMessages { get; init; } + public required string ConfiguredModel { get; init; } + public required MccGuidanceBundle Guidance { get; init; } + public DateTimeOffset StartedAtUtc { get; init; } = DateTimeOffset.UtcNow; + + public List ToolConversationMessages { get; } = []; + public List Evidence { get; } = []; + public List ToolExecutions { get; } = []; + public List VerificationObligations { get; } = []; + public string? CompactionSummary { get; set; } + public string? RoutedModel { get; set; } + public string? RoutedProvider { get; set; } + public int TurnCount { get; set; } + public int ToolCallCount { get; set; } + public int DirectAnswerAttempts { get; set; } + + public string NextEvidenceId() => $"e{++evidenceCounter:0000}"; + + public string NextObligationId() => $"v{++obligationCounter:0000}"; + + public bool IsSoftFinish(MccWebHarnessOptions options, DateTimeOffset nowUtc) + { + TimeSpan elapsed = nowUtc - StartedAtUtc; + return (options.MaxTurns - TurnCount) <= options.SoftFinishRemainingTurns + || (options.MaxToolCalls - ToolCallCount) <= options.SoftFinishRemainingToolCalls + || (options.MaxWallClockSeconds - (int)elapsed.TotalSeconds) <= options.SoftFinishRemainingSeconds; + } + + public bool IsHardStop(MccWebHarnessOptions options, DateTimeOffset nowUtc) + { + TimeSpan elapsed = nowUtc - StartedAtUtc; + return TurnCount >= options.MaxTurns + || ToolCallCount >= options.MaxToolCalls + || elapsed.TotalSeconds >= options.MaxWallClockSeconds; + } + + public IReadOnlyList OpenObligations => + VerificationObligations.Where(obligation => !obligation.Cleared).ToArray(); +} + +public sealed record MccGuidanceBundle( + string SourceToolName, + string CanonicalPromptName, + string SkillName, + string GuidanceVersion, + string SystemPrompt, + string[] BestPractices, + string[] ExampleScenarioTitles, + MccCapabilityStatus CapabilityStatus); + +public sealed class MccEvidenceRecord +{ + public required string Id { get; init; } + public required string ToolName { get; init; } + public required string Summary { get; init; } + public required string RawText { get; init; } + public required bool IsError { get; init; } + public required bool Success { get; init; } + public string? ErrorCode { get; init; } + public JsonElement? Root { get; init; } + public JsonElement? Data { get; init; } +} + +public sealed class MccToolExecutionRecord +{ + public required string CallId { get; init; } + public required string ToolName { get; init; } + public required string ArgumentsJson { get; init; } + public required MccEvidenceRecord Evidence { get; init; } +} + +public sealed class MccVerificationObligation +{ + public required string Id { get; init; } + public required string ToolName { get; init; } + public required string Kind { get; init; } + public required string Description { get; init; } + public required string SourceEvidenceId { get; init; } + public JsonElement? Metadata { get; init; } + public bool Cleared { get; set; } + public string? ClearedByEvidenceId { get; set; } +} + +public sealed record MccNormalizedToolResult( + string Text, + bool IsError, + bool Success, + string? ErrorCode, + string? Message, + JsonElement? Root, + JsonElement? Data); diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccToolPolicy.cs b/DebugTools/MccMcpWebPlayground/Harness/MccToolPolicy.cs new file mode 100644 index 00000000..e8a9bd77 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccToolPolicy.cs @@ -0,0 +1,133 @@ +using System.Collections.Frozen; +using System.Text.Json.Nodes; +using ModelContextProtocol.Client; + +namespace DebugTools.MccMcpWebPlayground.Harness; + +public enum MccToolRisk +{ + ReadOnly, + Stateful, + Sensitive, + EscapeHatch +} + +public sealed record MccToolProfile( + string Name, + MccToolRisk Risk, + bool VisibleByDefault, + bool RequiresExplicitUserIntent); + +public sealed record MccToolCatalogEntry(McpClientTool Tool, MccToolProfile Profile); + +public sealed class MccToolCatalog +{ + public required Dictionary ToolsByName { get; init; } + public required IReadOnlyList ModelVisibleTools { get; init; } +} + +public static class MccToolPolicy +{ + private static readonly FrozenDictionary Profiles = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["mcc_agent_guidance"] = new("mcc_agent_guidance", MccToolRisk.ReadOnly, false, false), + ["mcc_inventory_window_action"] = new("mcc_inventory_window_action", MccToolRisk.EscapeHatch, false, false), + ["mcc_run_internal_command"] = new("mcc_run_internal_command", MccToolRisk.EscapeHatch, false, false), + ["mcc_quit_client"] = new("mcc_quit_client", MccToolRisk.Sensitive, true, true) + }.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); + + public static MccToolProfile GetProfile(string toolName) + { + return Profiles.TryGetValue(toolName, out MccToolProfile? profile) + ? profile + : new MccToolProfile(toolName, MccToolRisk.Stateful, true, false); + } + + public static MccToolCatalog BuildCatalog(IList tools, MccWebHarnessOptions options, object submitFinalTool) + { + Dictionary toolsByName = tools.ToDictionary( + tool => tool.Name, + tool => new MccToolCatalogEntry(tool, GetProfile(tool.Name)), + StringComparer.OrdinalIgnoreCase); + + List visibleTools = []; + foreach (MccToolCatalogEntry entry in toolsByName.Values.OrderBy(entry => entry.Tool.Name, StringComparer.OrdinalIgnoreCase)) + { + if (!IsVisible(entry.Profile, options)) + continue; + + visibleTools.Add(ToOpenRouterTool(entry.Tool, entry.Profile)); + } + + visibleTools.Add(submitFinalTool); + + return new MccToolCatalog + { + ToolsByName = toolsByName, + ModelVisibleTools = visibleTools + }; + } + + public static bool RequiresExplicitUserIntent(string toolName) + { + return GetProfile(toolName).RequiresExplicitUserIntent; + } + + public static bool HasExplicitUserIntent(string userRequest, string toolName) + { + if (!RequiresExplicitUserIntent(toolName)) + return true; + + string request = userRequest.Trim().ToLowerInvariant(); + return toolName.Equals("mcc_quit_client", StringComparison.OrdinalIgnoreCase) + && (request.Contains("quit mcc", StringComparison.Ordinal) + || request.Contains("close mcc", StringComparison.Ordinal) + || request.Contains("stop mcc", StringComparison.Ordinal) + || request.Contains("exit mcc", StringComparison.Ordinal) + || request.Contains("quit the client", StringComparison.Ordinal) + || request.Contains("stop the client", StringComparison.Ordinal)); + } + + private static bool IsVisible(MccToolProfile profile, MccWebHarnessOptions options) + { + if (!profile.VisibleByDefault) + { + if (profile.Name.Equals("mcc_inventory_window_action", StringComparison.OrdinalIgnoreCase)) + return options.ExposeInventoryWindowAction; + + if (profile.Name.Equals("mcc_run_internal_command", StringComparison.OrdinalIgnoreCase)) + return options.ExposeInternalCommandTool; + + return false; + } + + return true; + } + + private static object ToOpenRouterTool(McpClientTool tool, MccToolProfile profile) + { + JsonNode parameters = JsonNode.Parse(tool.JsonSchema.GetRawText()) ?? new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject() + }; + + string description = tool.Description ?? string.Empty; + if (profile.Risk == MccToolRisk.Sensitive) + description = $"{description} Requires explicit user intent."; + else if (profile.Risk == MccToolRisk.EscapeHatch) + description = $"{description} Advanced escape hatch; prefer higher-level tools first."; + + return new Dictionary + { + ["type"] = "function", + ["function"] = new Dictionary + { + ["name"] = tool.Name, + ["description"] = description, + ["parameters"] = parameters + } + }; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Harness/MccWebHarnessOptions.cs b/DebugTools/MccMcpWebPlayground/Harness/MccWebHarnessOptions.cs new file mode 100644 index 00000000..0855c471 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Harness/MccWebHarnessOptions.cs @@ -0,0 +1,58 @@ +namespace DebugTools.MccMcpWebPlayground.Harness; + +public sealed class MccWebHarnessOptions +{ + public const string SectionName = "MccWebHarness"; + + public string? Model { get; set; } + public string OpenRouterBaseUrl { get; set; } = "https://openrouter.ai/api/v1"; + public string McpEndpoint { get; set; } = "http://127.0.0.1:33333/mcp"; + public int MaxTurns { get; set; } = 48; + public int MaxToolCalls { get; set; } = 120; + public int MaxWallClockSeconds { get; set; } = 240; + public int SoftFinishRemainingTurns { get; set; } = 3; + public int SoftFinishRemainingToolCalls { get; set; } = 8; + public int SoftFinishRemainingSeconds { get; set; } = 30; + public bool RequireProviderParameters { get; set; } = true; + public bool AllowFallbacks { get; set; } + public bool DisableParallelToolCalls { get; set; } = true; + public bool ExposeInventoryWindowAction { get; set; } + public bool ExposeInternalCommandTool { get; set; } + + public string? ResolveModel() + { + return FirstNonEmpty(Environment.GetEnvironmentVariable("OPENROUTER_MODEL"), Model); + } + + public string ResolveOpenRouterBaseUrl() + { + return FirstNonEmpty(Environment.GetEnvironmentVariable("OPENROUTER_BASE_URL"), OpenRouterBaseUrl) + ?? "https://openrouter.ai/api/v1"; + } + + public string ResolveMcpEndpoint() + { + return FirstNonEmpty(Environment.GetEnvironmentVariable("MCC_MCP_ENDPOINT"), McpEndpoint) + ?? "http://127.0.0.1:33333/mcp"; + } + + public string? ResolveMcpAuthToken() + { + return Environment.GetEnvironmentVariable("MCC_MCP_AUTH_TOKEN"); + } + + public string? ResolveApiKey() + { + return Environment.GetEnvironmentVariable("OPENROUTER_API_KEY"); + } + + public bool HasApiKeyConfigured() + { + return !string.IsNullOrWhiteSpace(ResolveApiKey()); + } + + private static string? FirstNonEmpty(params string?[] candidates) + { + return candidates.FirstOrDefault(candidate => !string.IsNullOrWhiteSpace(candidate))?.Trim(); + } +} diff --git a/DebugTools/MccMcpWebPlayground/Infrastructure/Mcp/MccMcpSessionFactory.cs b/DebugTools/MccMcpWebPlayground/Infrastructure/Mcp/MccMcpSessionFactory.cs new file mode 100644 index 00000000..8775bb2c --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Infrastructure/Mcp/MccMcpSessionFactory.cs @@ -0,0 +1,200 @@ +using System.Text; +using System.Text.Json; +using System.Reflection; +using DebugTools.MccMcpWebPlayground.Harness; +using Microsoft.Extensions.Options; +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol; + +namespace DebugTools.MccMcpWebPlayground.Infrastructure.Mcp; + +public sealed class MccMcpSessionFactory +{ + private readonly MccWebHarnessOptions options; + + public MccMcpSessionFactory(IOptions options) + { + this.options = options.Value; + } + + public async Task CreateAsync(CancellationToken cancellationToken) + { + string endpoint = options.ResolveMcpEndpoint(); + string? token = options.ResolveMcpAuthToken(); + + return await McpClient.CreateAsync(new HttpClientTransport(new HttpClientTransportOptions + { + Endpoint = new Uri(endpoint), + TransportMode = HttpTransportMode.AutoDetect, + AdditionalHeaders = string.IsNullOrWhiteSpace(token) + ? null + : new Dictionary + { + ["Authorization"] = $"Bearer {token}" + } + }), cancellationToken: cancellationToken); + } +} + +public static class MccMcpJson +{ + public static MccNormalizedToolResult Normalize(CallToolResult result) + { + JsonElement? structuredRoot = TryReadStructuredContent(result); + string text = ReadToolResultText(result, structuredRoot); + try + { + using JsonDocument document = JsonDocument.Parse(text); + JsonElement parsedRoot = document.RootElement.Clone(); + JsonElement root = ShouldPreferStructuredRoot(parsedRoot, structuredRoot) + ? structuredRoot!.Value + : parsedRoot; + JsonElement? data = root.TryGetProperty("data", out JsonElement dataElement) + ? dataElement.Clone() + : ShouldTreatRootAsData(root) ? root.Clone() : structuredRoot; + bool success = root.TryGetProperty("success", out JsonElement successElement) + ? successElement.ValueKind != JsonValueKind.False + : result.IsError != true; + string? errorCode = root.TryGetProperty("errorCode", out JsonElement errorCodeElement) && errorCodeElement.ValueKind == JsonValueKind.String + ? errorCodeElement.GetString() + : null; + string? message = root.TryGetProperty("message", out JsonElement messageElement) && messageElement.ValueKind == JsonValueKind.String + ? messageElement.GetString() + : null; + bool isError = result.IsError == true || !success || !string.IsNullOrWhiteSpace(errorCode); + + return new MccNormalizedToolResult(text, isError, success, errorCode, message, root, data); + } + catch + { + bool isError = result.IsError == true; + return new MccNormalizedToolResult(text, isError, !isError, null, null, structuredRoot, structuredRoot); + } + } + + private static string ReadToolResultText(CallToolResult result, JsonElement? structuredRoot) + { + if (result.Content is null) + return structuredRoot?.GetRawText() ?? (result.IsError == true ? "{\"success\":false}" : "{\"success\":true}"); + + StringBuilder builder = new(); + foreach (ContentBlock block in result.Content) + { + if (block is TextContentBlock text && !string.IsNullOrWhiteSpace(text.Text)) + { + if (builder.Length > 0) + builder.Append('\n'); + builder.Append(text.Text); + } + } + + return builder.Length > 0 + ? builder.ToString() + : structuredRoot?.GetRawText() + ?? JsonSerializer.Serialize(new { success = result.IsError != true, isError = result.IsError }); + } + + private static JsonElement? TryReadStructuredContent(CallToolResult result) + { + PropertyInfo? property = typeof(CallToolResult).GetProperty("StructuredContent", BindingFlags.Instance | BindingFlags.Public); + if (property?.GetValue(result) is not { } value) + return null; + + return value switch + { + JsonElement json when json.ValueKind != JsonValueKind.Undefined && json.ValueKind != JsonValueKind.Null => json.Clone(), + JsonDocument document => document.RootElement.Clone(), + string text when !string.IsNullOrWhiteSpace(text) => TryParseJson(text), + _ => TrySerializeToJson(value) + }; + } + + private static JsonElement? TrySerializeToJson(object value) + { + try + { + return JsonSerializer.SerializeToElement(value); + } + catch + { + return null; + } + } + + private static JsonElement? TryParseJson(string text) + { + try + { + using JsonDocument document = JsonDocument.Parse(text); + return document.RootElement.Clone(); + } + catch + { + return null; + } + } + + private static bool ShouldPreferStructuredRoot(JsonElement parsedRoot, JsonElement? structuredRoot) + { + if (structuredRoot is null) + return false; + + if (parsedRoot.ValueKind != JsonValueKind.Object) + return true; + + return !parsedRoot.EnumerateObject().Any(property => + !property.NameEquals("success") && + !property.NameEquals("isError")); + } + + private static bool ShouldTreatRootAsData(JsonElement root) + { + if (root.ValueKind != JsonValueKind.Object) + return false; + + return root.EnumerateObject().Any(property => + !property.NameEquals("success") && + !property.NameEquals("isError") && + !property.NameEquals("errorCode") && + !property.NameEquals("message")); + } +} + +public static class MccJsonArguments +{ + public static Dictionary Parse(string rawJson) + { + try + { + using JsonDocument document = JsonDocument.Parse(string.IsNullOrWhiteSpace(rawJson) ? "{}" : rawJson); + if (document.RootElement.ValueKind != JsonValueKind.Object) + return new Dictionary(); + + Dictionary values = new(StringComparer.OrdinalIgnoreCase); + foreach (JsonProperty property in document.RootElement.EnumerateObject()) + values[property.Name] = Convert(property.Value); + return values; + } + catch + { + return new Dictionary(); + } + } + + private static object? Convert(JsonElement element) + { + return element.ValueKind switch + { + JsonValueKind.Null => null, + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Number => element.TryGetInt64(out long i64) + ? i64 + : element.TryGetDouble(out double d) ? d : element.GetRawText(), + JsonValueKind.String => element.GetString(), + JsonValueKind.Array => element.EnumerateArray().Select(Convert).ToArray(), + JsonValueKind.Object => element.EnumerateObject().ToDictionary(property => property.Name, property => Convert(property.Value)), + _ => element.GetRawText() + }; + } +} diff --git a/DebugTools/MccMcpWebPlayground/Infrastructure/OpenRouter/OpenRouterChatClient.cs b/DebugTools/MccMcpWebPlayground/Infrastructure/OpenRouter/OpenRouterChatClient.cs new file mode 100644 index 00000000..24d9a080 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Infrastructure/OpenRouter/OpenRouterChatClient.cs @@ -0,0 +1,120 @@ +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using DebugTools.MccMcpWebPlayground.Harness; + +namespace DebugTools.MccMcpWebPlayground.Infrastructure.OpenRouter; + +public sealed class OpenRouterChatClient +{ + private readonly IHttpClientFactory httpClientFactory; + + public OpenRouterChatClient(IHttpClientFactory httpClientFactory) + { + this.httpClientFactory = httpClientFactory; + } + + public async Task CreateTurnAsync( + List messages, + IReadOnlyList tools, + MccWebHarnessOptions options, + CancellationToken cancellationToken) + { + string apiKey = options.ResolveApiKey() ?? throw new InvalidOperationException("OPENROUTER_API_KEY is not configured."); + string model = options.ResolveModel() ?? throw new InvalidOperationException("Model is not configured."); + + using HttpClient client = httpClientFactory.CreateClient("openrouter"); + client.BaseAddress = new Uri(options.ResolveOpenRouterBaseUrl().TrimEnd('/') + "/"); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); + client.DefaultRequestHeaders.TryAddWithoutValidation("HTTP-Referer", "https://localhost/mcc-mcp-web-playground"); + client.DefaultRequestHeaders.TryAddWithoutValidation("X-Title", "MCC MCP Web Playground"); + + Dictionary payload = new() + { + ["model"] = model, + ["messages"] = messages, + ["tools"] = tools, + ["tool_choice"] = "auto", + ["provider"] = new Dictionary + { + ["allow_fallbacks"] = options.AllowFallbacks, + ["require_parameters"] = options.RequireProviderParameters + } + }; + + if (ShouldSendParallelToolCallsParameter(model)) + payload["parallel_tool_calls"] = !options.DisableParallelToolCalls; + + using HttpResponseMessage response = await client.PostAsync( + "chat/completions", + new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"), + cancellationToken); + + string body = await response.Content.ReadAsStringAsync(cancellationToken); + if (!response.IsSuccessStatusCode) + throw new InvalidOperationException($"OpenRouter returned HTTP {(int)response.StatusCode}: {body}"); + + using JsonDocument document = JsonDocument.Parse(body); + if (!document.RootElement.TryGetProperty("choices", out JsonElement choices) + || choices.ValueKind != JsonValueKind.Array + || choices.GetArrayLength() == 0) + { + throw new InvalidOperationException("OpenRouter did not return any choices."); + } + + JsonElement message = choices[0].GetProperty("message"); + string assistantContent = message.TryGetProperty("content", out JsonElement contentElement) + ? contentElement.GetString() ?? string.Empty + : string.Empty; + + List toolCalls = []; + if (message.TryGetProperty("tool_calls", out JsonElement toolCallsElement) && toolCallsElement.ValueKind == JsonValueKind.Array) + { + foreach (JsonElement toolCall in toolCallsElement.EnumerateArray()) + { + if (!toolCall.TryGetProperty("id", out JsonElement idElement) + || !toolCall.TryGetProperty("function", out JsonElement functionElement) + || !functionElement.TryGetProperty("name", out JsonElement nameElement)) + { + continue; + } + + toolCalls.Add(new MccModelToolCall( + CallId: idElement.GetString() ?? Guid.NewGuid().ToString("n"), + Name: nameElement.GetString() ?? string.Empty, + ArgumentsJson: functionElement.TryGetProperty("arguments", out JsonElement argumentsElement) + ? argumentsElement.GetString() ?? "{}" + : "{}")); + } + } + + string modelId = document.RootElement.TryGetProperty("model", out JsonElement modelElement) + ? modelElement.GetString() ?? model + : model; + + string? routedProvider = response.Headers.TryGetValues("x-openrouter-provider", out IEnumerable? providerValues) + ? providerValues.FirstOrDefault() + : null; + + return new MccModelTurn(modelId, routedProvider, assistantContent, toolCalls); + } + + private static bool ShouldSendParallelToolCallsParameter(string model) + { + // Some OpenRouter model families reject tool-enabled requests when the parallel_tool_calls + // parameter is present at all, even if it is explicitly set to false. The harness still + // executes all returned tool calls sequentially, so omitting the transport hint for those + // families preserves the intended runtime behavior while keeping the stricter flag for + // compatible models. + return !model.StartsWith("minimax/", StringComparison.OrdinalIgnoreCase) + && !model.StartsWith("google/gemini-", StringComparison.OrdinalIgnoreCase); + } +} + +public sealed record MccModelTurn( + string ModelId, + string? RoutedProvider, + string AssistantContent, + IReadOnlyList ToolCalls); + +public sealed record MccModelToolCall(string CallId, string Name, string ArgumentsJson); diff --git a/DebugTools/MccMcpWebPlayground/MccMcpWebPlayground.csproj b/DebugTools/MccMcpWebPlayground/MccMcpWebPlayground.csproj new file mode 100644 index 00000000..96c52c1a --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/MccMcpWebPlayground.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + true + + + + + + + diff --git a/DebugTools/MccMcpWebPlayground/Program.cs b/DebugTools/MccMcpWebPlayground/Program.cs new file mode 100644 index 00000000..17060301 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Program.cs @@ -0,0 +1,40 @@ +using DebugTools.MccMcpWebPlayground.Api; +using DebugTools.MccMcpWebPlayground.Harness; +using DebugTools.MccMcpWebPlayground.Infrastructure.Mcp; +using DebugTools.MccMcpWebPlayground.Infrastructure.OpenRouter; +using Microsoft.AspNetCore.Http.Timeouts; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOptions() + .Bind(builder.Configuration.GetSection(MccWebHarnessOptions.SectionName)); + +builder.Services.AddRequestTimeouts(options => +{ + options.AddPolicy("mcc-stream", new RequestTimeoutPolicy + { + Timeout = TimeSpan.FromMinutes(10) + }); +}); + +builder.Services.AddHttpClient("openrouter", client => +{ + client.Timeout = TimeSpan.FromMinutes(15); +}); + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); + +var app = builder.Build(); + +app.UseRequestTimeouts(); +app.UseDefaultFiles(); +app.UseStaticFiles(); +app.MapMccPlaygroundEndpoints(); + +app.Run(); diff --git a/DebugTools/MccMcpWebPlayground/Properties/launchSettings.json b/DebugTools/MccMcpWebPlayground/Properties/launchSettings.json new file mode 100644 index 00000000..3701e48f --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5295", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7104;http://localhost:5295", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/DebugTools/MccMcpWebPlayground/appsettings.Development.json b/DebugTools/MccMcpWebPlayground/appsettings.Development.json new file mode 100644 index 00000000..6cde4d27 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/appsettings.Development.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "MccWebHarness": { + "AllowFallbacks": false, + "DisableParallelToolCalls": true, + "ExposeInventoryWindowAction": false, + "ExposeInternalCommandTool": false + } +} diff --git a/DebugTools/MccMcpWebPlayground/appsettings.json b/DebugTools/MccMcpWebPlayground/appsettings.json new file mode 100644 index 00000000..869684a5 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/appsettings.json @@ -0,0 +1,24 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "MccWebHarness": { + "OpenRouterBaseUrl": "https://openrouter.ai/api/v1", + "McpEndpoint": "http://127.0.0.1:33333/mcp", + "MaxTurns": 48, + "MaxToolCalls": 120, + "MaxWallClockSeconds": 240, + "SoftFinishRemainingTurns": 3, + "SoftFinishRemainingToolCalls": 8, + "SoftFinishRemainingSeconds": 30, + "RequireProviderParameters": true, + "AllowFallbacks": false, + "DisableParallelToolCalls": true, + "ExposeInventoryWindowAction": false, + "ExposeInternalCommandTool": false + }, + "AllowedHosts": "*" +} diff --git a/DebugTools/MccMcpWebPlayground/wwwroot/app.js b/DebugTools/MccMcpWebPlayground/wwwroot/app.js new file mode 100644 index 00000000..14cd9c0e --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/wwwroot/app.js @@ -0,0 +1,310 @@ +const html = document.documentElement; +const statusEl = document.getElementById("status"); +const sendBtn = document.getElementById("send"); +const stopBtn = document.getElementById("stop"); +const clearBtn = document.getElementById("clear"); +const clearChatBtn = document.getElementById("clear-chat-btn"); +const clearToolsBtn = document.getElementById("clear-tools-btn"); +const promptEl = document.getElementById("prompt"); +const chatEl = document.getElementById("chat"); +const toolsEl = document.getElementById("tools"); +const emptyStateEl = document.getElementById("empty-state"); +const toolsEmptyStateEl = document.getElementById("tools-empty-state"); +const typingIndicatorEl = document.getElementById("typing-indicator"); +const themeToggleBtn = document.getElementById("theme-toggle"); +const themeToggleIconEl = document.getElementById("theme-toggle-icon"); + +let history = []; +let activeAssistantBody = null; +let abortController = null; + +stopBtn.disabled = true; + +loadTheme(); +loadConfig(); + +themeToggleBtn.addEventListener("click", () => { + const next = html.getAttribute("data-theme") === "dark" ? "light" : "dark"; + setTheme(next); +}); + +sendBtn.addEventListener("click", sendPrompt); +stopBtn.addEventListener("click", () => abortController?.abort()); + +clearBtn.addEventListener("click", () => { + history = []; + removeAllMessages(); + removeAllTimelineEvents(); + promptEl.value = ""; + activeAssistantBody = null; + updateEmptyStates(); +}); + +clearChatBtn.addEventListener("click", () => { + history = []; + removeAllMessages(); + promptEl.value = ""; + activeAssistantBody = null; + updateEmptyStates(); +}); + +clearToolsBtn.addEventListener("click", () => { + removeAllTimelineEvents(); + updateEmptyStates(); +}); + +promptEl.addEventListener("keydown", (event) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + sendPrompt(); + } +}); + +async function loadConfig() { + try { + const response = await fetch("/api/config"); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + const config = await response.json(); + const modelLabel = config.model ? config.model : "Model not configured"; + statusEl.textContent = config.hasApiKey ? modelLabel : `${modelLabel} / missing OPENROUTER_API_KEY`; + } catch (error) { + statusEl.textContent = `Config error: ${error.message}`; + } +} + +async function sendPrompt() { + const prompt = promptEl.value.trim(); + if (!prompt || abortController) { + return; + } + + history.push({ role: "user", content: prompt }); + addMessage("user", prompt); + promptEl.value = ""; + activeAssistantBody = addMessage("assistant", ""); + setBusy(true); + + abortController = new AbortController(); + + try { + const response = await fetch("/api/chat/stream", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ messages: history }), + signal: abortController.signal + }); + + if (!response.ok || !response.body) { + throw new Error(`HTTP ${response.status}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + let finalAssistantText = ""; + + while (true) { + const { value, done } = await reader.read(); + if (done) { + break; + } + + buffer += decoder.decode(value, { stream: true }); + buffer = parseSseChunk(buffer, (eventName, envelope) => { + addTimelineEvent(eventName, envelope); + + if (eventName === "error") { + const errorMessage = envelope.data?.message ?? "Unknown error"; + addMessage("error", errorMessage); + } + + if (eventName === "final") { + finalAssistantText = formatFinalText(envelope.data); + activeAssistantBody.textContent = finalAssistantText; + } + + if (eventName === "state_summary") { + const turnCount = envelope.data?.turnCount ?? "?"; + const toolCallCount = envelope.data?.toolCallCount ?? "?"; + statusEl.textContent = `Running turn ${turnCount}, tools ${toolCallCount}`; + } + }); + } + + if (finalAssistantText.trim().length > 0) { + history.push({ role: "assistant", content: finalAssistantText }); + } + } catch (error) { + if (error.name !== "AbortError") { + addMessage("error", `Request failed: ${error.message}`); + addTimelineEvent("error", { + kind: "error", + data: { + code: "request_failed", + message: error.message + } + }); + } + } finally { + abortController = null; + activeAssistantBody = null; + setBusy(false); + } +} + +function parseSseChunk(buffer, onEvent) { + let blockIndex; + while ((blockIndex = buffer.indexOf("\n\n")) >= 0) { + const rawBlock = buffer.slice(0, blockIndex); + buffer = buffer.slice(blockIndex + 2); + + let eventName = "message"; + let dataText = ""; + for (const line of rawBlock.split("\n")) { + if (line.startsWith("event:")) { + eventName = line.slice(6).trim(); + } else if (line.startsWith("data:")) { + dataText += line.slice(5).trim(); + } + } + + if (!dataText) { + continue; + } + + try { + onEvent(eventName, JSON.parse(dataText)); + } catch (error) { + onEvent("error", { + kind: "error", + data: { + code: "invalid_sse_payload", + message: "Failed to parse SSE payload.", + detail: dataText + } + }); + } + } + + return buffer; +} + +function addMessage(role, content) { + const wrapper = document.createElement("div"); + wrapper.className = `message ${role}`; + + const label = document.createElement("div"); + label.className = "message-label"; + label.textContent = role; + + const body = document.createElement("div"); + body.className = "message-body"; + body.textContent = content; + + wrapper.append(label, body); + chatEl.insertBefore(wrapper, typingIndicatorEl); + chatEl.scrollTop = chatEl.scrollHeight; + updateEmptyStates(); + return body; +} + +function addTimelineEvent(kind, envelope) { + const event = document.createElement("div"); + event.className = `timeline-event kind-${kind}`; + + const label = document.createElement("div"); + label.className = "timeline-label"; + label.textContent = kind.replaceAll("_", " "); + + const body = document.createElement("div"); + body.className = "timeline-body-text"; + body.textContent = JSON.stringify(envelope.data ?? envelope, null, 2); + + event.append(label, body); + toolsEl.appendChild(event); + toolsEl.scrollTop = toolsEl.scrollHeight; + updateEmptyStates(); +} + +function formatFinalText(data) { + if (!data) { + return "The run completed without a final payload."; + } + + const lines = []; + if (data.headline) { + lines.push(data.headline); + lines.push(""); + } + + if (data.answerMarkdown) { + lines.push(data.answerMarkdown); + } + + if (Array.isArray(data.verifiedFacts) && data.verifiedFacts.length > 0) { + lines.push(""); + lines.push("Verified facts:"); + for (const fact of data.verifiedFacts) { + lines.push(`- ${fact}`); + } + } + + if (Array.isArray(data.openIssues) && data.openIssues.length > 0) { + lines.push(""); + lines.push("Open issues:"); + for (const issue of data.openIssues) { + lines.push(`- ${issue}`); + } + } + + if (data.nextAction) { + lines.push(""); + lines.push(`Next action: ${data.nextAction}`); + } + + return lines.join("\n"); +} + +function setBusy(busy) { + sendBtn.disabled = busy; + stopBtn.disabled = !busy; + promptEl.disabled = busy; + typingIndicatorEl.classList.toggle("visible", busy); + statusEl.classList.toggle("busy", busy); + if (!busy) { + loadConfig(); + } else { + statusEl.textContent = "Streaming run..."; + } +} + +function removeAllMessages() { + for (const message of chatEl.querySelectorAll(".message")) { + message.remove(); + } +} + +function removeAllTimelineEvents() { + for (const event of toolsEl.querySelectorAll(".timeline-event")) { + event.remove(); + } +} + +function updateEmptyStates() { + emptyStateEl.style.display = chatEl.querySelectorAll(".message").length === 0 ? "" : "none"; + toolsEmptyStateEl.style.display = toolsEl.querySelectorAll(".timeline-event").length === 0 ? "" : "none"; +} + +function loadTheme() { + const theme = localStorage.getItem("mcc-playground-theme") || "dark"; + setTheme(theme); +} + +function setTheme(theme) { + html.setAttribute("data-theme", theme); + themeToggleIconEl.textContent = theme === "dark" ? "◎" : "◐"; + localStorage.setItem("mcc-playground-theme", theme); +} diff --git a/DebugTools/MccMcpWebPlayground/wwwroot/index.html b/DebugTools/MccMcpWebPlayground/wwwroot/index.html new file mode 100644 index 00000000..71a9d992 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/wwwroot/index.html @@ -0,0 +1,79 @@ + + + + + + MCC MCP Playground + + + + + +
+
+
+
+
MCC MCP Playground
+
Canonical guidance bootstrap, typed run state, verified completion
+
+
+
+
Booting...
+ +
+
+ +
+
+
+

Conversation

+
+ +
+
+
+
+

No messages yet.

+

Ask the harness to inspect or act through MCC's MCP server.

+
+
+ +
+
+
+ +
+
+

Run Timeline

+
+ +
+
+
+
+

No run events yet.

+

Typed SSE events will appear here as the harness runs.

+
+
+
+
+ +
+
+ +
+ +
+ + + + diff --git a/DebugTools/MccMcpWebPlayground/wwwroot/site.css b/DebugTools/MccMcpWebPlayground/wwwroot/site.css new file mode 100644 index 00000000..66621a67 --- /dev/null +++ b/DebugTools/MccMcpWebPlayground/wwwroot/site.css @@ -0,0 +1,383 @@ +:root { + --bg: #07111e; + --bg-alt: #0b1828; + --panel: rgba(10, 21, 36, 0.88); + --panel-strong: rgba(8, 18, 30, 0.96); + --border: rgba(111, 179, 255, 0.18); + --text: #dce9ff; + --text-dim: #8ca4c8; + --text-soft: #607695; + --accent: #75e7c7; + --accent-strong: #4ad3ff; + --warning: #ffcc66; + --danger: #ff7b8b; + --shadow: 0 20px 60px rgba(0, 0, 0, 0.3); +} + +[data-theme="light"] { + --bg: #edf4ff; + --bg-alt: #dfeaff; + --panel: rgba(255, 255, 255, 0.88); + --panel-strong: rgba(255, 255, 255, 0.96); + --border: rgba(28, 89, 164, 0.14); + --text: #172843; + --text-dim: #4d6383; + --text-soft: #7d90ad; + --accent: #0f936d; + --accent-strong: #006cbb; + --warning: #a56700; + --danger: #ba2741; + --shadow: 0 20px 60px rgba(61, 89, 138, 0.12); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + margin: 0; + min-height: 100%; +} + +body { + display: grid; + grid-template-rows: auto 1fr auto; + gap: 16px; + padding: 18px; + color: var(--text); + font-family: "Space Mono", monospace; + background: + radial-gradient(circle at top left, rgba(74, 211, 255, 0.12), transparent 35%), + radial-gradient(circle at right center, rgba(117, 231, 199, 0.08), transparent 40%), + linear-gradient(160deg, var(--bg), var(--bg-alt)); +} + +button, +textarea { + font: inherit; +} + +.topbar, +.panel, +.composer { + border: 1px solid var(--border); + border-radius: 18px; + background: var(--panel); + backdrop-filter: blur(18px); + box-shadow: var(--shadow); +} + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 18px 20px; +} + +.brand { + display: flex; + align-items: center; + gap: 14px; +} + +.brand-dot { + width: 12px; + height: 12px; + border-radius: 999px; + background: linear-gradient(135deg, var(--accent), var(--accent-strong)); + box-shadow: 0 0 18px rgba(117, 231, 199, 0.55); +} + +.brand-title { + font-family: "Syne", sans-serif; + font-size: 1rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.brand-title span { + color: var(--accent); +} + +.brand-subtitle { + margin-top: 4px; + color: var(--text-dim); + font-size: 0.74rem; +} + +.topbar-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.status-pill { + padding: 8px 12px; + border-radius: 999px; + border: 1px solid var(--border); + color: var(--text-dim); + font-size: 0.72rem; + white-space: nowrap; +} + +.status-pill.busy { + color: var(--accent-strong); + border-color: rgba(74, 211, 255, 0.4); +} + +.icon-button, +.ghost-button, +.primary-button { + border-radius: 12px; + border: 1px solid var(--border); + background: rgba(255, 255, 255, 0.02); + color: var(--text); + padding: 10px 14px; + cursor: pointer; + transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease; +} + +.icon-button:hover, +.ghost-button:hover, +.primary-button:hover { + transform: translateY(-1px); + border-color: rgba(117, 231, 199, 0.4); +} + +.primary-button { + background: linear-gradient(135deg, var(--accent), var(--accent-strong)); + color: #06101a; + font-weight: 700; +} + +.layout { + min-height: 0; + display: grid; + grid-template-columns: 1.2fr 0.9fr; + gap: 16px; +} + +.panel { + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} + +.panel-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 16px 18px; + border-bottom: 1px solid var(--border); +} + +.panel-header h1 { + margin: 0; + font-family: "Syne", sans-serif; + font-size: 0.82rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.panel-body { + min-height: 0; + overflow: auto; + padding: 18px; +} + +.chat-body, +.timeline-body { + display: flex; + flex-direction: column; + gap: 12px; +} + +.message, +.timeline-event { + border-radius: 16px; + border: 1px solid var(--border); + padding: 14px 16px; + background: var(--panel-strong); +} + +.message.user { + background: rgba(74, 211, 255, 0.09); +} + +.message.assistant { + background: rgba(117, 231, 199, 0.06); +} + +.message.error { + background: rgba(255, 123, 139, 0.08); + border-color: rgba(255, 123, 139, 0.2); +} + +.message-label, +.timeline-label { + margin-bottom: 8px; + color: var(--text-dim); + font-size: 0.7rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.message-body, +.timeline-body-text { + white-space: pre-wrap; + word-break: break-word; + line-height: 1.6; + font-size: 0.82rem; +} + +.timeline-event.kind-tool_called { + border-left: 4px solid var(--accent-strong); +} + +.timeline-event.kind-tool_result { + border-left: 4px solid var(--accent); +} + +.timeline-event.kind-error { + border-left: 4px solid var(--danger); +} + +.timeline-event.kind-budget { + border-left: 4px solid var(--warning); +} + +.timeline-event.kind-final { + border-left: 4px solid var(--accent); +} + +.empty-state { + padding: 30px 18px; + text-align: center; + color: var(--text-soft); + border: 1px dashed var(--border); + border-radius: 14px; +} + +.typing-indicator { + display: none; + gap: 6px; + align-items: center; + padding: 10px 4px 0; +} + +.typing-indicator.visible { + display: flex; +} + +.typing-indicator span { + width: 8px; + height: 8px; + border-radius: 999px; + background: var(--accent-strong); + animation: bounce 1s ease-in-out infinite; +} + +.typing-indicator span:nth-child(2) { + animation-delay: 0.16s; +} + +.typing-indicator span:nth-child(3) { + animation-delay: 0.32s; +} + +@keyframes bounce { + 0%, 80%, 100% { + transform: translateY(0); + opacity: 0.45; + } + 40% { + transform: translateY(-5px); + opacity: 1; + } +} + +.composer { + padding: 16px 18px; +} + +.composer-row { + display: flex; +} + +#prompt { + width: 100%; + min-height: 78px; + max-height: 240px; + resize: vertical; + border-radius: 16px; + border: 1px solid var(--border); + background: rgba(0, 0, 0, 0.12); + color: var(--text); + padding: 14px 16px; +} + +#prompt:focus { + outline: 2px solid rgba(74, 211, 255, 0.35); + border-color: rgba(74, 211, 255, 0.45); +} + +.composer-footer { + margin-top: 14px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.composer-hint { + color: var(--text-dim); + font-size: 0.74rem; +} + +kbd { + padding: 2px 6px; + border-radius: 6px; + border: 1px solid var(--border); + background: rgba(255, 255, 255, 0.03); + font-size: 0.74rem; +} + +.composer-actions { + display: flex; + gap: 10px; +} + +@media (max-width: 980px) { + .layout { + grid-template-columns: 1fr; + } + + body { + padding: 12px; + } + + .topbar, + .composer { + padding: 14px; + } + + .composer-footer { + flex-direction: column; + align-items: stretch; + } + + .composer-actions { + justify-content: stretch; + } + + .composer-actions > button { + flex: 1; + } +} diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..41d20762 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,11 @@ + + + $(DefaultItemExcludes);**/bin/**;**/obj/** + + + + $(MCC_BUILD_ROOT)/$(MSBuildProjectName)/bin/ + $(MCC_BUILD_ROOT)/$(MSBuildProjectName)/obj/ + $(BaseIntermediateOutputPath) + + diff --git a/MinecraftClient.sln b/MinecraftClient.sln index 8f0049d8..ebdf1f09 100644 --- a/MinecraftClient.sln +++ b/MinecraftClient.sln @@ -7,27 +7,81 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinecraftClient", "Minecraf EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleInteractive", "ConsoleInteractive\ConsoleInteractive\ConsoleInteractive\ConsoleInteractive.csproj", "{93DA4D71-EFAD-4493-BE21-A105AF663660}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DebugTools", "DebugTools", "{02313C6C-37F1-D66D-F235-6A4537C03113}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MccMcpStdioHarness", "DebugTools\MccMcpStdioHarness\MccMcpStdioHarness.csproj", "{F032D2BB-A0A9-4726-A58F-C02F7EA606D6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MccMcpWebPlayground", "DebugTools\MccMcpWebPlayground\MccMcpWebPlayground.csproj", "{5F620CF6-BC7D-449A-B779-2D51985059C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x64.Build.0 = Debug|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x86.Build.0 = Debug|Any CPU {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|Any CPU.Build.0 = Release|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x64.ActiveCfg = Release|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x64.Build.0 = Release|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x86.ActiveCfg = Release|Any CPU + {1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x86.Build.0 = Release|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|x64.ActiveCfg = Debug|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|x64.Build.0 = Debug|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|x86.ActiveCfg = Debug|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|x86.Build.0 = Debug|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|Any CPU.ActiveCfg = Release|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|Any CPU.Build.0 = Release|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|x64.ActiveCfg = Release|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|x64.Build.0 = Release|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|x86.ActiveCfg = Release|Any CPU + {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|x86.Build.0 = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|x64.ActiveCfg = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|x64.Build.0 = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|x86.ActiveCfg = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Debug|x86.Build.0 = Debug|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|Any CPU.Build.0 = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|x64.ActiveCfg = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|x64.Build.0 = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|x86.ActiveCfg = Release|Any CPU + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6}.Release|x86.Build.0 = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|x64.Build.0 = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Debug|x86.Build.0 = Debug|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|Any CPU.Build.0 = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|x64.ActiveCfg = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|x64.Build.0 = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|x86.ActiveCfg = Release|Any CPU + {5F620CF6-BC7D-449A-B779-2D51985059C6}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F032D2BB-A0A9-4726-A58F-C02F7EA606D6} = {02313C6C-37F1-D66D-F235-6A4537C03113} + {5F620CF6-BC7D-449A-B779-2D51985059C6} = {02313C6C-37F1-D66D-F235-6A4537C03113} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_ShowErrorsInErrorList = False SolutionGuid = {6DED60F4-9CF4-4DB3-8966-582B2EBE8487} + RESX_ShowErrorsInErrorList = False RESX_SortFileContentOnSave = False EndGlobalSection EndGlobal diff --git a/MinecraftClient/Achievement.cs b/MinecraftClient/Achievement.cs new file mode 100644 index 00000000..760e4054 --- /dev/null +++ b/MinecraftClient/Achievement.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace MinecraftClient +{ + /// + /// The type of an achievement or advancement. + /// + public enum AchievementType + { + Task, + Challenge, + Goal, + Legacy + } + + /// + /// Represents a Minecraft achievement (pre-1.12) or advancement (1.12+). + /// + /// Resource identifier, e.g. "minecraft:story/root" or "achievement.openInventory" + /// Display title (null for legacy achievements without display info) + /// Display description (null for legacy achievements without display info) + /// The frame type / achievement category + /// Whether this advancement is hidden in the UI + /// Whether all requirements have been met + /// OR-groups of criterion names; all groups must be satisfied + /// Per-criterion completion status + public record Achievement( + string Id, + string? Title, + string? Description, + AchievementType Type, + bool IsHidden, + bool IsCompleted, + IReadOnlyList> Requirements, + IReadOnlyDictionary CriteriaProgress); +} diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index 30d47189..4291e8dd 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -48,8 +48,9 @@ namespace MinecraftClient.ChatBots Delay.min = Math.Max(1.0, Delay.min); Delay.max = Math.Max(1.0, Delay.max); - Delay.min = Math.Min(int.MaxValue / 10, Delay.min); - Delay.max = Math.Min(int.MaxValue / 10, Delay.max); + double maxDelaySeconds = int.MaxValue / (double)Settings.ClientTicksPerSecond; + Delay.min = Math.Min(maxDelaySeconds, Delay.min); + Delay.max = Math.Min(maxDelaySeconds, Delay.max); if (Delay.min > Delay.max) { @@ -64,6 +65,12 @@ namespace MinecraftClient.ChatBots { public double min, max; + public Range() + { + min = 0; + max = 0; + } + public Range(int value) { min = max = value; @@ -77,7 +84,7 @@ namespace MinecraftClient.ChatBots } } - private int count, nextrun = 50; + private int count, nextrun = Settings.DoubleToTick(5.0); private bool previousSneakState = false; private readonly Random random = new(); @@ -120,7 +127,7 @@ namespace MinecraftClient.ChatBots private void DoAntiAfkStuff() { var isMovementLocked = BotMovementLock.Instance; - if (Config.Use_Terrain_Handling && GetTerrainEnabled() && isMovementLocked is {IsLocked: false}) + if (Config.Use_Terrain_Handling && GetTerrainEnabled() && isMovementLocked is { IsLocked: false }) { var currentLocation = GetCurrentLocation(); @@ -180,4 +187,4 @@ namespace MinecraftClient.ChatBots currentLocation.Z + random.Next(range * -1, range)); } } -} \ No newline at end of file +} diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 4b203497..f851ca0a 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -28,7 +28,7 @@ namespace MinecraftClient.ChatBots public PriorityType Priority = PriorityType.distance; [TomlInlineComment("$ChatBot.AutoAttack.Cooldown_Time$")] - public CooldownConfig Cooldown_Time = new(false, 1.0); + public CooldownConfig Cooldown_Time = new(); [TomlInlineComment("$ChatBot.AutoAttack.Interaction$")] public InteractType Interaction = InteractType.Attack; @@ -50,10 +50,19 @@ namespace MinecraftClient.ChatBots public void OnSettingUpdate() { - if (Cooldown_Time.Custom && Cooldown_Time.value <= 0) + if (Cooldown_Time.Custom) { - LogToConsole(BotName, Translations.bot_autoAttack_invalidcooldown); - Cooldown_Time.value = 1.0; + if (Cooldown_Time.Min <= 0) + Cooldown_Time.Min = 0.1; + if (Cooldown_Time.Max <= 0) + Cooldown_Time.Max = 0.1; + + if (Cooldown_Time.Min > Cooldown_Time.Max) + { + double temp = Cooldown_Time.Min; + Cooldown_Time.Min = Cooldown_Time.Max; + Cooldown_Time.Max = temp; + } } if (Attack_Range < 1.0) @@ -72,24 +81,16 @@ namespace MinecraftClient.ChatBots public struct CooldownConfig { public bool Custom; - public double value; + public bool RandomMode = false; + public double Min = 1.5; + public double Max = 2.5; public CooldownConfig() { Custom = false; - value = 0; - } - - public CooldownConfig(double value) - { - Custom = true; - this.value = value; - } - - public CooldownConfig(bool Override, double value) - { - this.Custom = Override; - this.value = value; + RandomMode = false; + Min = 1.5; + Max = 2.5; } } } @@ -105,14 +106,15 @@ namespace MinecraftClient.ChatBots private float health = 100; private readonly bool attackHostile = true; private readonly bool attackPassive = false; + private readonly Random _random = new(); public AutoAttack() { overrideAttackSpeed = Config.Cooldown_Time.Custom; if (Config.Cooldown_Time.Custom) { - attackCooldownSeconds = Config.Cooldown_Time.value; - attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); + attackCooldownSeconds = Config.Cooldown_Time.Min; + attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds); } attackHostile = Config.Attack_Hostile; @@ -137,6 +139,12 @@ namespace MinecraftClient.ChatBots if (attackCooldownCounter == 0) { + if (Config.Cooldown_Time.Custom && Config.Cooldown_Time.RandomMode) + { + double randomSeconds = _random.NextDouble() * (Config.Cooldown_Time.Max - Config.Cooldown_Time.Min) + Config.Cooldown_Time.Min; + attackCooldown = SecondsToAttackCooldownTicks(randomSeconds); + } + attackCooldownCounter = attackCooldown; if (entitiesToAttack.Count > 0) { @@ -177,6 +185,8 @@ namespace MinecraftClient.ChatBots InteractEntity(priorityEntity, Config.Interaction); // hit the entity! SendAnimation(Inventory.Hand.MainHand); // Arm animation } + + } } else @@ -188,6 +198,7 @@ namespace MinecraftClient.ChatBots { InteractEntity(entity.Key, Config.Interaction); // hit the entity! } + } SendAnimation(Inventory.Hand.MainHand); // Arm animation } @@ -274,7 +285,7 @@ namespace MinecraftClient.ChatBots serverTPS = GetServerTPS(); attackSpeed = prop[attackSpeedKey]; attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown - attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); + attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds); } } } @@ -288,7 +299,13 @@ namespace MinecraftClient.ChatBots serverTPS = tps; // re-calculate attack speed attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown - attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); + attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds); + } + + private static int SecondsToAttackCooldownTicks(double seconds) + { + seconds = Math.Min(int.MaxValue / (double)Settings.ClientTicksPerSecond, seconds); + return Math.Max(1, (int)Math.Truncate(seconds * Settings.ClientTicksPerSecond) + 1); } /// diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index 52d691ab..e0156941 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -106,6 +106,13 @@ namespace MinecraftClient.ChatBots { public double X, Y, Z; + public LocationConfig() + { + X = 0; + Y = 0; + Z = 0; + } + public LocationConfig(double X, double Y, double Z) { this.X = X; @@ -116,7 +123,7 @@ namespace MinecraftClient.ChatBots public enum OnFailConfig { abort, wait } - public class RecipeConfig + public record RecipeConfig { public string Name = "Recipe Name"; @@ -153,9 +160,9 @@ namespace MinecraftClient.ChatBots private Recipe? recipeInUse; private readonly List actionSteps = new(); - private int updateDebounceValue = 2; + private int updateDebounceValue = Settings.DoubleToTick(0.2); private int updateDebounce = 0; - private readonly int updateTimeoutValue = 10; + private readonly int updateTimeoutValue = Settings.ClientTicksPerSecond; private int updateTimeout = 0; private string timeoutAction = "unspecified"; @@ -234,7 +241,7 @@ namespace MinecraftClient.ChatBots /// /// Represent a crafting recipe /// - private class Recipe + private record Recipe { /// /// The results item of this recipe @@ -269,7 +276,7 @@ namespace MinecraftClient.ChatBots /// so that it can be used in crafting table public static Recipe ConvertToCraftingTable(Recipe recipe) { - if (recipe.CraftingAreaType == ContainerType.PlayerInventory && recipe.Materials != null) + if (recipe.CraftingAreaType == ContainerType.PlayerInventory && recipe.Materials is not null) { if (recipe.Materials.ContainsKey(4)) { @@ -493,7 +500,7 @@ namespace MinecraftClient.ChatBots } } - if (recipe.Materials != null) + if (recipe.Materials is not null) { foreach (KeyValuePair slot in recipe.Materials) { diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index cddb6ffd..04f3f6da 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; using MinecraftClient.Scripting; using Tomlet.Attributes; @@ -24,15 +27,18 @@ namespace MinecraftClient.ChatBots public bool Enabled = false; - [NonSerialized] [TomlInlineComment("$ChatBot.AutoDig.Auto_Tool_Switch$")] public bool Auto_Tool_Switch = false; - [NonSerialized] + [TomlInlineComment("$ChatBot.AutoDig.Apply_Efficiency_Enchantments$")] + public bool Apply_Efficiency_Enchantments = true; + + [TomlInlineComment("$ChatBot.AutoDig.Apply_Haste_Effects$")] + public bool Apply_Haste_Effects = true; + [TomlInlineComment("$ChatBot.AutoDig.Durability_Limit$")] public int Durability_Limit = 2; - [NonSerialized] [TomlInlineComment("$ChatBot.AutoDig.Drop_Low_Durability_Tools$")] public bool Drop_Low_Durability_Tools = false; @@ -64,6 +70,8 @@ namespace MinecraftClient.ChatBots public void OnSettingUpdate() { + Durability_Limit = Math.Max(0, Durability_Limit); + if (Auto_Start_Delay >= 0) Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay); @@ -85,6 +93,13 @@ namespace MinecraftClient.ChatBots { public double x, y, z; + public Coordination() + { + x = 0; + y = 0; + z = 0; + } + public Coordination(double x, double y, double z) { this.x = x; this.y = y; this.z = z; @@ -95,7 +110,7 @@ namespace MinecraftClient.ChatBots private bool inventoryEnabled; private int counter = 0; - private readonly object stateLock = new(); + private readonly Lock stateLock = new(); private State state = State.WaitJoinGame; bool AlreadyWaitting = false; @@ -217,6 +232,111 @@ namespace MinecraftClient.ChatBots } } + private static int GetLegacyMaxDamage(ItemType itemType) + { + return itemType switch + { + ItemType.WoodenPickaxe or ItemType.WoodenAxe or ItemType.WoodenShovel or ItemType.WoodenSword or ItemType.WoodenHoe => 59, + ItemType.StonePickaxe or ItemType.StoneAxe or ItemType.StoneShovel or ItemType.StoneSword or ItemType.StoneHoe => 131, + ItemType.IronPickaxe or ItemType.IronAxe or ItemType.IronShovel or ItemType.IronSword or ItemType.IronHoe => 250, + ItemType.GoldenPickaxe or ItemType.GoldenAxe or ItemType.GoldenShovel or ItemType.GoldenSword or ItemType.GoldenHoe => 32, + ItemType.DiamondPickaxe or ItemType.DiamondAxe or ItemType.DiamondShovel or ItemType.DiamondSword or ItemType.DiamondHoe => 1561, + ItemType.NetheritePickaxe or ItemType.NetheriteAxe or ItemType.NetheriteShovel or ItemType.NetheriteSword or ItemType.NetheriteHoe => 2031, + ItemType.Shears => 238, + _ => 0 + }; + } + + private static int GetMaxDamage(Item item) + { + if (item.Components is not null) + { + var maxDamageComponent = item.Components.OfType().FirstOrDefault(); + if (maxDamageComponent is not null) + return maxDamageComponent.MaxDamage; + } + + return GetLegacyMaxDamage(item.Type); + } + + private static int GetRemainingDurability(Item item) + { + int maxDamage = GetMaxDamage(item); + return maxDamage > 0 ? maxDamage - item.Damage : int.MaxValue; + } + + private bool HasEnoughDurability(Item item) + { + return Config.Durability_Limit <= 0 || GetRemainingDurability(item) >= Config.Durability_Limit; + } + + private bool IsBelowDurabilityLimit(Item? item) + { + return item is not null && Config.Durability_Limit > 0 && GetRemainingDurability(item) < Config.Durability_Limit; + } + + private static bool IsRecommendedTool(Item? item, ItemType[] recommendedTools) + { + return item is not null && recommendedTools.Contains(item.Type); + } + + private bool SwapToolIntoHand(int sourceSlot, int handSlot) + { + return WindowAction(0, sourceSlot, WindowActionType.LeftClick) + && WindowAction(0, handSlot, WindowActionType.LeftClick) + && WindowAction(0, sourceSlot, WindowActionType.LeftClick); + } + + private bool EnsureSuitableTool(Material blockType) + { + if (!inventoryEnabled || !Config.Auto_Tool_Switch) + return true; + + ItemType[] recommendedTools = Material2Tool.GetCorrectToolForBlock(blockType); + if (recommendedTools.Length == 0) + return true; + + Container container = GetPlayerInventory(); + int handSlot = 36 + GetCurrentSlot(); + container.Items.TryGetValue(handSlot, out Item? currentTool); + + if (currentTool is not null && IsRecommendedTool(currentTool, recommendedTools) && HasEnoughDurability(currentTool)) + return true; + + foreach (ItemType recommendedTool in recommendedTools) + { + foreach ((int slot, Item item) in container.Items) + { + if (slot == handSlot || item.Type != recommendedTool || !HasEnoughDurability(item)) + continue; + + if (!SwapToolIntoHand(slot, handSlot)) + return false; + + LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_switch, item.GetTypeString(), slot)); + + if (Config.Drop_Low_Durability_Tools && IsBelowDurabilityLimit(currentTool) && + WindowAction(0, slot, WindowActionType.DropItemStack)) + { + LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_drop_low_durability, currentTool!.GetTypeString(), slot)); + } + + return true; + } + } + + return !IsBelowDurabilityLimit(currentTool); + } + + private static MiningCalculator.MiningOptions GetMiningOptions() + { + return new MiningCalculator.MiningOptions + { + ApplyEfficiencyEnchantments = Config.Apply_Efficiency_Enchantments, + ApplyHasteEffects = Config.Apply_Haste_Effects + }; + } + public override void Update() { lock (stateLock) @@ -285,7 +405,10 @@ namespace MinecraftClient.ChatBots if (Config.Mode == Configs.ModeType.lookat || (Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc))) { - if (DigBlock(blockLoc, lookAtBlock: false)) + if (!EnsureSuitableTool(block.Type)) + return false; + + if (DigBlock(blockLoc, Direction.Down, lookAtBlock: false, miningOptions: GetMiningOptions())) { currentDig = blockLoc; if (Config.Log_Block_Dig) @@ -346,7 +469,10 @@ namespace MinecraftClient.ChatBots if (minDistance <= 6.0) { - if (DigBlock(target, lookAtBlock: true)) + if (!EnsureSuitableTool(targetBlock.Type)) + return false; + + if (DigBlock(target, Direction.Down, lookAtBlock: true, miningOptions: GetMiningOptions())) { currentDig = target; if (Config.Log_Block_Dig) @@ -380,7 +506,10 @@ namespace MinecraftClient.ChatBots ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))) { - if (DigBlock(blockLoc, lookAtBlock: true)) + if (!EnsureSuitableTool(block.Type)) + return false; + + if (DigBlock(blockLoc, Direction.Down, lookAtBlock: true, miningOptions: GetMiningOptions())) { currentDig = blockLoc; if (Config.Log_Block_Dig) diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 36263a86..5a7c56cb 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -41,7 +41,7 @@ namespace MinecraftClient.ChatBots } private int updateDebounce = 0; - private readonly int updateDebounceValue = 2; + private readonly int updateDebounceValue = Settings.DoubleToTick(0.2); private int inventoryUpdated = -1; public override void Initialize() diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 09ee140f..cf381561 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; @@ -61,6 +62,21 @@ namespace MinecraftClient.ChatBots [TomlInlineComment("$ChatBot.AutoFishing.Hook_Threshold$")] public double Hook_Threshold = 0.2; + [TomlInlineComment("$ChatBot.AutoFishing.Enable_Velocity_Detection$")] + public bool Enable_Velocity_Detection = true; + + [TomlInlineComment("$ChatBot.AutoFishing.Velocity_Hook_Threshold$")] + public double Velocity_Hook_Threshold = -0.2; + + [TomlInlineComment("$ChatBot.AutoFishing.Enable_Sound_Detection$")] + public bool Enable_Sound_Detection = true; + + [TomlInlineComment("$ChatBot.AutoFishing.Sound_Distance$")] + public double Sound_Distance = 5.0; + + [TomlInlineComment("$ChatBot.AutoFishing.Detection_Warmup$")] + public double Detection_Warmup = 1.0; + [TomlInlineComment("$ChatBot.AutoFishing.Log_Fish_Bobber$")] public bool Log_Fish_Bobber = false; @@ -96,6 +112,15 @@ namespace MinecraftClient.ChatBots if (Hook_Threshold < 0) Hook_Threshold = -Hook_Threshold; + + if (Velocity_Hook_Threshold > 0) + Velocity_Hook_Threshold = -Velocity_Hook_Threshold; + + if (Sound_Distance < 0) + Sound_Distance = -Sound_Distance; + + if (Detection_Warmup < 0) + Detection_Warmup = 0; } public struct LocationConfig @@ -103,6 +128,12 @@ namespace MinecraftClient.ChatBots public Coordination? XYZ; public Facing? facing; + public LocationConfig() + { + XYZ = null; + facing = null; + } + public LocationConfig(double yaw, double pitch) { this.XYZ = null; @@ -125,6 +156,13 @@ namespace MinecraftClient.ChatBots { public double x, y, z; + public Coordination() + { + x = 0; + y = 0; + z = 0; + } + public Coordination(double x, double y, double z) { this.x = x; this.y = y; this.z = z; @@ -135,6 +173,12 @@ namespace MinecraftClient.ChatBots { public double yaw, pitch; + public Facing() + { + yaw = 0; + pitch = 0; + } + public Facing(double yaw, double pitch) { this.yaw = yaw; this.pitch = pitch; @@ -151,12 +195,13 @@ namespace MinecraftClient.ChatBots private Entity? fishingBobber; private Location LastPos = Location.Zero; private DateTime CaughtTime = DateTime.Now; + private DateTime BobberSpawnTime = DateTime.MinValue; private int fishItemCounter = 15; private Dictionary fishItemCnt = new(); private Entity fishItem = new(-1, EntityType.Item, Location.Zero); private int counter = 0; - private readonly object stateLock = new(); + private readonly Lock stateLock = new(); private FishingState state = FishingState.WaitJoinGame; private int curLocationIdx = 0, moveDir = 1; @@ -444,6 +489,7 @@ namespace MinecraftClient.ChatBots fishingBobber = entity; LastPos = entity.Location; isFishing = true; + BobberSpawnTime = DateTime.Now; castTimeout = 24; counter = 0; @@ -454,7 +500,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityDespawn(Entity entity) { - if (entity != null && fishingBobber != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID) + if (entity is not null && fishingBobber is not null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID) { if (Config.Log_Fish_Bobber) LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location)); @@ -479,8 +525,8 @@ namespace MinecraftClient.ChatBots public override void OnEntityMove(Entity entity) { - if (isFishing && entity != null && fishingBobber!.ID == entity.ID && - (state == FishingState.WaitingFishToBite || state == FishingState.WaitingFishingBobber)) + if (isFishing && entity is not null && fishingBobber!.ID == entity.ID && + state == FishingState.WaitingFishToBite) { Location Pos = entity.Location; double Dx = LastPos.X - Pos.X; @@ -495,13 +541,7 @@ namespace MinecraftClient.ChatBots Math.Abs(Dz) < Math.Abs(Config.Stationary_Threshold) && Math.Abs(Dy) > Math.Abs(Config.Hook_Threshold)) { - // prevent triggering multiple time - if ((DateTime.Now - CaughtTime).TotalSeconds > 1) - { - isFishing = false; - CaughtTime = DateTime.Now; - OnCaughtFish(); - } + TryCatchFish(); } } } @@ -520,6 +560,38 @@ namespace MinecraftClient.ChatBots } } + public override void OnEntityVelocity(Entity entity, double velocityX, double velocityY, double velocityZ) + { + if (!Config.Enable_Velocity_Detection || !CanUseAdvancedDetection()) + return; + + if (fishingBobber is null || entity.ID != fishingBobber.ID) + return; + + if (velocityY <= Config.Velocity_Hook_Threshold) + TryCatchFish(); + } + + public override void OnSoundEffect(string? soundName, Location? location, int category, float volume, float pitch, + Entity? sourceEntity) + { + if (!Config.Enable_Sound_Detection || !CanUseAdvancedDetection()) + return; + + if (!IsFishingBobberSplashSound(soundName)) + return; + + Location? soundLocation = location; + if (soundLocation is null && sourceEntity is not null) + soundLocation = sourceEntity.Location; + + if (soundLocation is null || fishingBobber is null) + return; + + if (soundLocation.Value.Distance(fishingBobber.Location) <= Config.Sound_Distance) + TryCatchFish(); + } + public override void AfterGameJoined() { StartFishing(); @@ -542,10 +614,42 @@ namespace MinecraftClient.ChatBots fishingBobber = null; LastPos = Location.Zero; CaughtTime = DateTime.Now; + BobberSpawnTime = DateTime.MinValue; return base.OnDisconnect(reason, message); } + private bool CanUseAdvancedDetection() + { + if (!isFishing || fishingBobber is null || state != FishingState.WaitingFishToBite) + return false; + + return (DateTime.Now - BobberSpawnTime).TotalSeconds >= Config.Detection_Warmup; + } + + private void TryCatchFish() + { + if (!CanUseAdvancedDetection()) + return; + + // Prevent repeated catches from multiple packets of the same bite. + if ((DateTime.Now - CaughtTime).TotalSeconds <= 1) + return; + + isFishing = false; + CaughtTime = DateTime.Now; + OnCaughtFish(); + } + + private static bool IsFishingBobberSplashSound(string? soundName) + { + return string.Equals(soundName, "minecraft:entity.fishing_bobber.splash", + StringComparison.OrdinalIgnoreCase) + || string.Equals(soundName, "entity.fishing_bobber.splash", StringComparison.OrdinalIgnoreCase) + || string.Equals(soundName, "minecraft:entity.bobber.splash", StringComparison.OrdinalIgnoreCase) + || string.Equals(soundName, "entity.bobber.splash", StringComparison.OrdinalIgnoreCase); + } + /// /// Called when detected a fish is caught /// @@ -583,12 +687,12 @@ namespace MinecraftClient.ChatBots LocationConfig curConfig = locationList[curLocationIdx]; - if (curConfig.facing != null) + if (curConfig.facing is not null) (nextYaw, nextPitch) = ((float)curConfig.facing.Value.yaw, (float)curConfig.facing.Value.pitch); else (nextYaw, nextPitch) = (GetYaw(), GetPitch()); - if (curConfig.XYZ != null) + if (curConfig.XYZ is not null) { Location current = GetCurrentLocation(); Location goal = new(curConfig.XYZ.Value.x, curConfig.XYZ.Value.y, curConfig.XYZ.Value.z); diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index 24e6570a..1c975b38 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Threading; using MinecraftClient.Scripting; using Tomlet.Attributes; @@ -39,8 +40,9 @@ namespace MinecraftClient.ChatBots Delay.min = Math.Max(0.1, Delay.min); Delay.max = Math.Max(0.1, Delay.max); - Delay.min = Math.Min(int.MaxValue / 10, Delay.min); - Delay.max = Math.Min(int.MaxValue / 10, Delay.max); + double maxDelaySeconds = int.MaxValue / (double)Settings.ClientTicksPerSecond; + Delay.min = Math.Min(maxDelaySeconds, Delay.min); + Delay.max = Math.Min(maxDelaySeconds, Delay.max); if (Delay.min > Delay.max) (Delay.min, Delay.max) = (Delay.max, Delay.min); @@ -57,6 +59,12 @@ namespace MinecraftClient.ChatBots { public double min, max; + public Range() + { + min = 0; + max = 0; + } + public Range(int value) { min = max = value; @@ -70,7 +78,9 @@ namespace MinecraftClient.ChatBots } } - private static readonly Random random = new(); + private static readonly Lock s_reconnectStateLock = new(); + private static readonly TimeSpan s_stableJoinBeforeRetryReset = TimeSpan.FromSeconds(60); + private static DateTime? s_lastJoinUtc; /// /// This bot automatically re-join the server if kick message contains predefined string @@ -88,6 +98,17 @@ namespace MinecraftClient.ChatBots _Initialize(); } + public override void AfterGameJoined() + { + lock (s_reconnectStateLock) + s_lastJoinUtc = DateTime.UtcNow; + } + + public override void Update() + { + ResetRetriesAfterStableJoin(); + } + private void _Initialize() { McClient.ReconnectionAttemptsLeft = Config.Retries; @@ -103,7 +124,11 @@ namespace MinecraftClient.ChatBots { LogDebugToConsole(Translations.bot_autoRelog_ignore_user_logout); } - else if (Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries) + else if (Program.HasRestartPendingForAnotherThread) + { + return true; + } + else if (CanReconnect()) { message = GetVerbatim(message); string comp = message.ToLower(); @@ -112,18 +137,14 @@ namespace MinecraftClient.ChatBots if (Config.Ignore_Kick_Message) { - Configs._BotRecoAttempts++; - LaunchDelayedReconnection(null); - return true; + return LaunchDelayedReconnection(null); } foreach (string msg in Config.Kick_Messages) { if (comp.Contains(msg)) { - Configs._BotRecoAttempts++; - LaunchDelayedReconnection(msg); - return true; + return LaunchDelayedReconnection(msg); } } @@ -133,14 +154,83 @@ namespace MinecraftClient.ChatBots return false; } - private void LaunchDelayedReconnection(string? msg) + private static bool CanReconnect() { - double delay = random.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min; + lock (s_reconnectStateLock) + return Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries; + } + + private static void ResetRetriesAfterStableJoin() + { + lock (s_reconnectStateLock) + { + if (Configs._BotRecoAttempts <= 0 || s_lastJoinUtc is not DateTime lastJoinUtc) + return; + + if (DateTime.UtcNow - lastJoinUtc < s_stableJoinBeforeRetryReset) + return; + + Configs._BotRecoAttempts = 0; + s_lastJoinUtc = null; + McClient.ReconnectionAttemptsLeft = Config.Retries; + } + } + + private static bool TryConsumeReconnectAttempt(out int retriesLeft) + { + lock (s_reconnectStateLock) + { + bool unlimitedRetries = HasUnlimitedRetries(); + if (!unlimitedRetries && Configs._BotRecoAttempts >= Config.Retries) + { + retriesLeft = 0; + return false; + } + + Configs._BotRecoAttempts++; + s_lastJoinUtc = null; + retriesLeft = unlimitedRetries ? int.MaxValue : Config.Retries - Configs._BotRecoAttempts; + if (retriesLeft < 0) + retriesLeft = 0; + return true; + } + } + + private static bool HasUnlimitedRetries() + { + return Config.Retries < 0 || Config.Retries == int.MaxValue; + } + + private static void RollBackReconnectAttempt() + { + lock (s_reconnectStateLock) + { + if (Configs._BotRecoAttempts > 0) + Configs._BotRecoAttempts--; + } + } + + private bool LaunchDelayedReconnection(string? msg) + { + if (!TryConsumeReconnectAttempt(out int retriesLeft)) + return false; + + double delay = Random.Shared.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min; LogDebugToConsole(string.Format(string.IsNullOrEmpty(msg) ? Translations.bot_autoRelog_reconnect_always : Translations.bot_autoRelog_reconnect, msg)); - - // TODO: Change this translation string to add the retries left text - LogToConsole(string.Format(Translations.bot_autoRelog_wait, delay) + $" ({Config.Retries - Configs._BotRecoAttempts} retries left)"); - ReconnectToTheServer(Config.Retries - Configs._BotRecoAttempts, (int)Math.Floor(delay), true); + + string retriesDisplay = HasUnlimitedRetries() + ? Translations.bot_autoRelog_retries_unlimited + : retriesLeft.ToString(); + + McClient.ReconnectionAttemptsLeft = retriesLeft; + if (Program.TryRestart((int)Math.Floor(delay), true)) + { + LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay)); + return true; + } + + RollBackReconnectAttempt(); + return true; } public static bool OnDisconnectStatic(DisconnectReason reason, string message) diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs index 3b5a2991..a9bb43ea 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -132,7 +132,7 @@ namespace MinecraftClient.ChatBots if (String.IsNullOrEmpty(toSend)) return null; - if (regex != null) + if (regex is not null) { if (regex.IsMatch(message)) { @@ -261,15 +261,15 @@ namespace MinecraftClient.ChatBots /// Minimal cooldown between two matches private void CheckAddMatch(Regex? matchRegex, string? matchString, string? matchAction, string? matchActionPrivate, string? matchActionOther, bool ownersOnly, TimeSpan cooldown) { - if (matchRegex != null || matchString != null || matchAction != null || matchActionPrivate != null || matchActionOther != null || ownersOnly || cooldown != TimeSpan.Zero) + if (matchRegex is not null || matchString is not null || matchAction is not null || matchActionPrivate is not null || matchActionOther is not null || ownersOnly || cooldown != TimeSpan.Zero) { - RespondRule rule = matchRegex != null + RespondRule rule = matchRegex is not null ? new RespondRule(matchRegex, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown) : new RespondRule(matchString, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown); - if (matchAction != null || matchActionPrivate != null || matchActionOther != null) + if (matchAction is not null || matchActionPrivate is not null || matchActionOther is not null) { - if (matchRegex != null || matchString != null) + if (matchRegex is not null || matchString is not null) { respondRules!.Add(rule); LogDebugToConsole(string.Format(Translations.bot_autoRespond_loaded_match, rule)); diff --git a/MinecraftClient/ChatBots/ChatLog.cs b/MinecraftClient/ChatBots/ChatLog.cs index 37aecd36..d5121897 100644 --- a/MinecraftClient/ChatBots/ChatLog.cs +++ b/MinecraftClient/ChatBots/ChatLog.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; using Tomlet.Attributes; @@ -50,7 +51,7 @@ namespace MinecraftClient.ChatBots private bool saveChat = true; private bool savePrivate = true; private bool saveInternal = true; - private readonly object logfileLock = new(); + private readonly Lock logfileLock = new(); /// /// This bot saves the messages received in the specified file, with some filters and date/time tagging. diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index b46ad770..3938ab6a 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -1,7 +1,11 @@ -using System; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using Brigadier.NET.Builder; using DSharpPlus; @@ -33,6 +37,9 @@ namespace MinecraftClient.ChatBots private DiscordChannel? discordChannel; private BridgeDirection bridgeDirection = BridgeDirection.Both; + private readonly ConcurrentQueue aggregationBuffer = new(); + private Timer? aggregationTimer; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -58,6 +65,15 @@ namespace MinecraftClient.ChatBots [TomlInlineComment("$ChatBot.DiscordBridge.MessageSendTimeout$")] public int Message_Send_Timeout = 3; + [TomlInlineComment("$ChatBot.DiscordBridge.AllowOtherBotMessages$")] + public bool Allow_Other_Bot_Messages = false; + + [TomlInlineComment("$ChatBot.DiscordBridge.RelayAllMessages$")] + public bool Relay_All_Messages = false; + + [TomlInlineComment("$ChatBot.DiscordBridge.MessageAggregationInterval$")] + public double Message_Aggregation_Interval = 3.0; + [TomlPrecedingComment("$ChatBot.DiscordBridge.Formats$")] public string PrivateMessageFormat = "**[Private Message]** {username}: {message}"; public string PublicMessageFormat = "{username}: {message}"; @@ -66,6 +82,8 @@ namespace MinecraftClient.ChatBots public void OnSettingUpdate() { Message_Send_Timeout = Message_Send_Timeout <= 0 ? 3 : Message_Send_Timeout; + if (Message_Aggregation_Interval < 0) + Message_Aggregation_Interval = 0; } } @@ -96,6 +114,12 @@ namespace MinecraftClient.ChatBots .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); + if (Config.Message_Aggregation_Interval > 0) + { + var intervalMs = (int)(Config.Message_Aggregation_Interval * 1000); + aggregationTimer = new Timer(_ => FlushAggregationBuffer(), null, intervalMs, intervalMs); + } + Task.Run(async () => await MainAsync()); } @@ -103,6 +127,7 @@ namespace MinecraftClient.ChatBots { McClient.dispatcher.Unregister(CommandName); McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + StopAggregation(); Disconnect(); } @@ -143,6 +168,40 @@ namespace MinecraftClient.ChatBots return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_DiscordBridge_direction, bridgeName)); } + private void FlushAggregationBuffer() + { + if (aggregationBuffer.IsEmpty || !CanSendMessages()) + return; + + var sb = new StringBuilder(); + while (aggregationBuffer.TryDequeue(out var line)) + { + if (sb.Length + line.Length + 1 > 1900) + { + SendMessage(sb.ToString()); + sb.Clear(); + } + + if (sb.Length > 0) + sb.AppendLine(); + sb.Append(line); + } + + if (sb.Length > 0) + SendMessage(sb.ToString()); + } + + private void StopAggregation() + { + if (aggregationTimer is not null) + { + aggregationTimer.Dispose(); + aggregationTimer = null; + } + + FlushAggregationBuffer(); + } + ~DiscordBridge() { Disconnect(); @@ -150,11 +209,11 @@ namespace MinecraftClient.ChatBots private void Disconnect() { - if (discordBotClient != null) + if (discordBotClient is not null) { try { - if (discordChannel != null) + if (discordChannel is not null) discordBotClient.SendMessageAsync(discordChannel, new DiscordEmbedBuilder { Description = Translations.bot_DiscordBridge_disconnected, @@ -184,7 +243,6 @@ namespace MinecraftClient.ChatBots text = GetVerbatim(text).Trim(); - // Stop the crash when an empty text is recived somehow if (string.IsNullOrEmpty(text)) return; @@ -201,7 +259,10 @@ namespace MinecraftClient.ChatBots message = Config.TeleportRequestMessageFormat.Replace("{username}", username).Replace("{timestamp}", GetTimestamp()).Trim(); teleportRequest = true; } - else message = text; + else if (Config.Relay_All_Messages) + message = text; + else + return; if (teleportRequest) { @@ -219,7 +280,28 @@ namespace MinecraftClient.ChatBots SendMessage(messageBuilder); return; } - else SendMessage(message); + + string discordText = GetDiscordText(message); + + if (Config.Message_Aggregation_Interval > 0) + aggregationBuffer.Enqueue(discordText); + else + SendMessage(discordText); + } + + /// + /// Converts Minecraft § formatting codes to Discord Markdown equivalents + /// and strips remaining § codes. + /// Handles both properly closed formatting (§l...§r) and unclosed formatting (§l... end). + /// + private static string GetDiscordText(string text) + { + text = Regex.Replace(text, @"§l(.*?)(?:§r|$)", "**$1**"); + text = Regex.Replace(text, @"§m(.*?)(?:§r|$)", "~~$1~~"); + text = Regex.Replace(text, @"§n(.*?)(?:§r|$)", "__$1__"); + text = Regex.Replace(text, @"§o(.*?)(?:§r|$)", "*$1*"); + text = Regex.Replace(text, @"§.", ""); + return text; } public void SendMessage(string message) @@ -281,10 +363,10 @@ namespace MinecraftClient.ChatBots filePath = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..]; var messageBuilder = new DiscordMessageBuilder(); - if (text != null) + if (text is not null) messageBuilder.WithContent(text); - messageBuilder.WithFiles(new Dictionary() { { $"attachment://{filePath}", fs } }); + messageBuilder.AddFiles(new Dictionary() { { filePath, fs } }); discordBotClient!.SendMessageAsync(discordChannel, messageBuilder).Wait(Config.Message_Send_Timeout * 1000); } @@ -301,12 +383,12 @@ namespace MinecraftClient.ChatBots if (!CanSendMessages()) return; - SendMessage(new DiscordMessageBuilder().WithFile(fileStream)); + SendMessage(new DiscordMessageBuilder().AddFile(fileStream)); } private bool CanSendMessages() { - return discordBotClient != null && discordChannel != null && bridgeDirection != BridgeDirection.Minecraft; + return discordBotClient is not null && discordChannel is not null && bridgeDirection != BridgeDirection.Minecraft; } async Task MainAsync() @@ -372,12 +454,25 @@ namespace MinecraftClient.ChatBots if (e.Channel.Id != Config.ChannelId) return; - if (!Config.OwnersIds.Contains(e.Author.Id)) + // Always ignore own messages to prevent loops + if (e.Author.Id == discordBotClient.CurrentUser.Id) return; string message = e.Message.Content.Trim(); - if (string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message)) + if (string.IsNullOrWhiteSpace(message)) + return; + + // Relay messages from other bots when configured, but never process commands from them. + // Skip relay when direction is Discord-only (Discord -> MC disabled). + if (e.Author.IsBot) + { + if (Config.Allow_Other_Bot_Messages && bridgeDirection != BridgeDirection.Discord) + SendText(message); + return; + } + + if (!Config.OwnersIds.Contains(e.Author.Id)) return; if (bridgeDirection == BridgeDirection.Discord) diff --git a/MinecraftClient/ChatBots/DiscordRpc.cs b/MinecraftClient/ChatBots/DiscordRpc.cs new file mode 100644 index 00000000..e3ab09b3 --- /dev/null +++ b/MinecraftClient/ChatBots/DiscordRpc.cs @@ -0,0 +1,727 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Pipes; +using System.Threading; +using DiscordRPC; +using DiscordRPC.IO; +using DiscordRPC.Logging; +using MinecraftClient.Mapping; +using MinecraftClient.Scripting; +using Tomlet.Attributes; + +namespace MinecraftClient.ChatBots +{ + /// + /// Displays a Discord Rich Presence status showing the player's + /// current Minecraft session information (server, health, dimension, etc.). + /// Requires a Discord Application ID from https://discord.com/developers/applications + /// + public class DiscordRpc : ChatBot + { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "DiscordRpc"; + + public bool Enabled = false; + + [TomlInlineComment("$ChatBot.DiscordRpc.ApplicationId$")] + public string ApplicationId = string.Empty; + + [TomlInlineComment("$ChatBot.DiscordRpc.PresenceDetails$")] + public string PresenceDetails = "Playing on {server_host}:{server_port}"; + + [TomlInlineComment("$ChatBot.DiscordRpc.PresenceState$")] + public string PresenceState = "{dimension} - HP: {health}/{max_health}"; + + [TomlInlineComment("$ChatBot.DiscordRpc.LargeImageKey$")] + public string LargeImageKey = "mcc_icon"; + + [TomlInlineComment("$ChatBot.DiscordRpc.LargeImageText$")] + public string LargeImageText = "Minecraft Console Client"; + + [TomlInlineComment("$ChatBot.DiscordRpc.SmallImageKey$")] + public string SmallImageKey = string.Empty; + + [TomlInlineComment("$ChatBot.DiscordRpc.SmallImageText$")] + public string SmallImageText = string.Empty; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowServerAddress$")] + public bool ShowServerAddress = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowCoordinates$")] + public bool ShowCoordinates = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowHealth$")] + public bool ShowHealth = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowDimension$")] + public bool ShowDimension = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowGamemode$")] + public bool ShowGamemode = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowElapsedTime$")] + public bool ShowElapsedTime = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.ShowPlayerCount$")] + public bool ShowPlayerCount = true; + + [TomlInlineComment("$ChatBot.DiscordRpc.UpdateIntervalSeconds$")] + public int UpdateIntervalSeconds = 10; + + public void OnSettingUpdate() + { + ApplicationId ??= string.Empty; + PresenceDetails ??= string.Empty; + PresenceState ??= string.Empty; + LargeImageKey ??= string.Empty; + LargeImageText ??= string.Empty; + SmallImageKey ??= string.Empty; + SmallImageText ??= string.Empty; + + if (UpdateIntervalSeconds < 1) + { + UpdateIntervalSeconds = 10; + LogToConsole(BotName, Translations.bot_DiscordRpc_invalid_interval); + } + } + } + + private DiscordRpcClient? _rpcClient; + private int _tickCounter; + private int _updateIntervalTicks; + private Timestamps? _sessionTimestamps; + private float _lastHealth; + + public override void Initialize() + { + if (string.IsNullOrWhiteSpace(Config.ApplicationId)) + { + LogToConsole(Translations.bot_DiscordRpc_missing_app_id); + UnloadBot(); + return; + } + + try + { + _rpcClient = OperatingSystem.IsLinux() + ? new DiscordRpcClient(Config.ApplicationId.Trim(), client: new DiscordRpcPipeClient()) + : new DiscordRpcClient(Config.ApplicationId.Trim()); + + _rpcClient.Logger = Settings.Config.Logging.DebugMessages + ? new ConsoleLogger(LogLevel.Trace) + : new ConsoleLogger(LogLevel.None); + + _rpcClient.OnReady += (_, e) => + { + LogToConsole(string.Format(Translations.bot_DiscordRpc_connected, e.User.Username)); + }; + + _rpcClient.OnConnectionFailed += (_, e) => + { + LogToConsole(string.Format(Translations.bot_DiscordRpc_connection_failed, e.FailedPipe)); + }; + + _rpcClient.Initialize(); + _updateIntervalTicks = Settings.DoubleToTick(Config.UpdateIntervalSeconds); + + LogToConsole(Translations.bot_DiscordRpc_initialized); + } + catch (Exception e) + { + LogToConsole(string.Format(Translations.bot_DiscordRpc_init_error, e.Message)); + LogDebugToConsole(e.StackTrace ?? string.Empty); + UnloadBot(); + } + } + + public override void OnUnload() + { + if (_rpcClient is { IsDisposed: false }) + { + _rpcClient.ClearPresence(); + _rpcClient.Dispose(); + } + + _rpcClient = null; + } + + public override void AfterGameJoined() + { + if (Config.ShowElapsedTime) + _sessionTimestamps = Timestamps.Now; + + _lastHealth = Handler.GetHealth(); + _tickCounter = 0; + SetPresence(); + } + + public override void Update() + { + _tickCounter++; + if (_tickCounter < _updateIntervalTicks) + return; + + _tickCounter = 0; + SetPresence(); + } + + public override void OnHealthUpdate(float health, int food) + { + _lastHealth = health; + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + if (_rpcClient is { IsDisposed: false }) + _rpcClient.ClearPresence(); + + return false; + } + + private void SetPresence() + { + if (_rpcClient is null or { IsDisposed: true }) + return; + + try + { + string details = ReplacePlaceholders(Config.PresenceDetails); + string state = ReplacePlaceholders(Config.PresenceState); + + var presence = new RichPresence + { + Details = TruncateForDiscord(details, 128), + State = TruncateForDiscord(state, 128) + }; + + // Assets (images) + var assets = new Assets(); + bool hasAssets = false; + + if (!string.IsNullOrWhiteSpace(Config.LargeImageKey)) + { + assets.LargeImageKey = Config.LargeImageKey.Trim(); + assets.LargeImageText = TruncateForDiscord( + ReplacePlaceholders(Config.LargeImageText), 128); + hasAssets = true; + } + + if (!string.IsNullOrWhiteSpace(Config.SmallImageKey)) + { + assets.SmallImageKey = Config.SmallImageKey.Trim(); + assets.SmallImageText = TruncateForDiscord( + ReplacePlaceholders(Config.SmallImageText), 128); + hasAssets = true; + } + + if (hasAssets) + presence.Assets = assets; + + // Timestamps + if (Config.ShowElapsedTime && _sessionTimestamps is not null) + presence.Timestamps = _sessionTimestamps; + + // Player count as party + if (Config.ShowPlayerCount) + { + string[] onlinePlayers = GetOnlinePlayers(); + int playerCount = onlinePlayers.Length; + if (playerCount > 0) + { + presence.Party = new Party + { + ID = $"mcc_{GetServerHost()}_{GetServerPort()}", + Size = playerCount, + Max = playerCount + }; + } + } + + _rpcClient.SetPresence(presence); + } + catch (Exception e) + { + LogDebugToConsole(string.Format(Translations.bot_DiscordRpc_update_error, e.Message)); + } + } + + private string ReplacePlaceholders(string template) + { + if (string.IsNullOrEmpty(template)) + return string.Empty; + + string serverHost = Config.ShowServerAddress ? GetServerHost() : "Hidden"; + int serverPort = GetServerPort(); + string serverPortStr = Config.ShowServerAddress ? serverPort.ToString() : "****"; + string username = GetUsername(); + float health = Handler.GetHealth(); + int foodLevel = Handler.GetSaturation(); + Location location = GetCurrentLocation(); + string[] onlinePlayers = GetOnlinePlayers(); + int gamemode = GetGamemode(); + int protocolVersion = GetProtocolVersion(); + + string healthStr = Config.ShowHealth ? ((int)Math.Ceiling(health)).ToString() : "?"; + string maxHealthStr = Config.ShowHealth ? "20" : "?"; + string foodStr = Config.ShowHealth ? foodLevel.ToString() : "?"; + string xStr = Config.ShowCoordinates ? ((int)location.X).ToString() : "?"; + string yStr = Config.ShowCoordinates ? ((int)location.Y).ToString() : "?"; + string zStr = Config.ShowCoordinates ? ((int)location.Z).ToString() : "?"; + + string dimensionName = Config.ShowDimension ? "Unknown" : "Hidden"; + if (Config.ShowDimension) + { + try + { + var dim = World.GetDimension(); + dimensionName = dim.Name ?? "Unknown"; + + // Clean up the dimension name for display + if (dimensionName.StartsWith("minecraft:", StringComparison.Ordinal)) + dimensionName = dimensionName["minecraft:".Length..]; + + dimensionName = dimensionName switch + { + "overworld" => "Overworld", + "the_nether" => "The Nether", + "the_end" => "The End", + _ => dimensionName + }; + } + catch + { + // World may not be available + } + } + + string gamemodeStr = Config.ShowGamemode + ? gamemode switch + { + 0 => "Survival", + 1 => "Creative", + 2 => "Adventure", + 3 => "Spectator", + _ => "Unknown" + } + : "Hidden"; + + return template + .Replace("{server_host}", serverHost) + .Replace("{server_port}", serverPortStr) + .Replace("{username}", username) + .Replace("{health}", healthStr) + .Replace("{max_health}", maxHealthStr) + .Replace("{food}", foodStr) + .Replace("{dimension}", dimensionName) + .Replace("{gamemode}", gamemodeStr) + .Replace("{x}", xStr) + .Replace("{y}", yStr) + .Replace("{z}", zStr) + .Replace("{player_count}", onlinePlayers.Length.ToString()) + .Replace("{protocol}", protocolVersion.ToString()); + } + + private static string TruncateForDiscord(string value, int maxLength) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + return value.Length <= maxLength ? value : value[..(maxLength - 3)] + "..."; + } + + /// + /// Flatpak Discord exposes the RPC socket under app/com.discordapp.Discord, + /// but the currently published DiscordRichPresence package does not probe that path. + /// + private sealed class DiscordRpcPipeClient : INamedPipeClient + { + private const string DiscordPipePrefix = "discord-ipc-"; + private const int MaximumPipeVariations = 10; + + private static readonly string[] s_unixPackageDirectories = + [ + // Official Discord clients + "app/com.discordapp.Discord", + "snap.discord", + + // Community desktop clients / wrappers + "app/dev.vencord.Vesktop", + ".flatpak/dev.vencord.Vesktop/xdg-run", + "app/org.equicord.equibop", + "app/io.github.equicord.equibop", + "app/xyz.armcord.ArmCord", + "app/io.github.spacingbat3.webcord" + ]; + + private readonly byte[] _buffer = new byte[PipeFrame.MAX_SIZE]; + private readonly Queue _frameQueue = new(); + private readonly Lock _frameQueueLock = new(); + private readonly Lock _streamLock = new(); + + private int _connectedPipe; + private NamedPipeClientStream? _stream; + private volatile bool _isClosed = true; + private volatile bool _isDisposed; + + public ILogger Logger { get; set; } = new NullLogger(); + + public bool IsConnected + { + get + { + if (_isClosed) + return false; + + lock (_streamLock) + return _stream is { IsConnected: true }; + } + } + + [Obsolete("The connected pipe is not neccessary information.")] + public int ConnectedPipe => _connectedPipe; + + public bool Connect(int pipe) + { + Logger.Trace("DiscordRpcPipeClient.Connect({0})", pipe); + + if (_isDisposed) + throw new ObjectDisposedException(nameof(DiscordRpcPipeClient)); + + if (pipe > 9) + throw new ArgumentOutOfRangeException(nameof(pipe), "Argument cannot be greater than 9"); + + int startPipe = pipe >= 0 ? pipe : 0; + + foreach (string pipeName in GetPipeCandidates(startPipe)) + { + if (AttemptConnection(pipeName)) + { + BeginReadStream(); + return true; + } + } + + return false; + } + + public bool ReadFrame(out PipeFrame frame) + { + if (_isDisposed) + throw new ObjectDisposedException(nameof(DiscordRpcPipeClient)); + + lock (_frameQueueLock) + { + if (_frameQueue.Count == 0) + { + frame = default; + return false; + } + + frame = _frameQueue.Dequeue(); + return true; + } + } + + public bool WriteFrame(PipeFrame frame) + { + if (_isDisposed) + throw new ObjectDisposedException(nameof(DiscordRpcPipeClient)); + + if (_isClosed || !IsConnected) + { + Logger.Error("Failed to write frame because the stream is closed"); + return false; + } + + try + { + frame.WriteStream(_stream); + return true; + } + catch (IOException io) + { + Logger.Error("Failed to write frame because of a IO Exception: {0}", io.Message); + } + catch (ObjectDisposedException) + { + Logger.Warning("Failed to write frame as the stream was already disposed"); + } + catch (InvalidOperationException) + { + Logger.Warning("Failed to write frame because of a invalid operation"); + } + + return false; + } + + public void Close() + { + if (_isClosed) + { + Logger.Warning("Tried to close a already closed pipe."); + return; + } + + try + { + lock (_streamLock) + { + if (_stream is not null) + { + try + { + _stream.Flush(); + _stream.Dispose(); + } + catch + { + } + + _stream = null; + _isClosed = true; + } + else + { + Logger.Warning("Stream was closed, but no stream was available to begin with!"); + } + } + } + catch (ObjectDisposedException) + { + Logger.Warning("Tried to dispose already disposed stream"); + } + finally + { + _isClosed = true; + } + } + + public void Dispose() + { + if (_isDisposed) + return; + + if (!_isClosed) + Close(); + + lock (_streamLock) + { + _stream?.Dispose(); + _stream = null; + } + + _isDisposed = true; + } + + private bool AttemptConnection(string pipeName) + { + if (_isDisposed) + throw new ObjectDisposedException(nameof(DiscordRpcPipeClient)); + + try + { + lock (_streamLock) + { + Logger.Info("Attempting to connect to {0}", pipeName); + _stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); + _stream.Connect(0); + + Logger.Trace("Waiting for connection..."); + while (!_stream.IsConnected) + Thread.Sleep(10); + } + + Logger.Info("Connected to {0}", pipeName); + _connectedPipe = int.Parse(pipeName[(pipeName.LastIndexOf('-') + 1)..], System.Globalization.CultureInfo.InvariantCulture); + _isClosed = false; + } + catch (Exception e) + { + Logger.Error("Failed connection to {0}. {1}", pipeName, e.Message); + Close(); + } + + Logger.Trace("Done. Result: {0}", _isClosed); + return !_isClosed; + } + + private void BeginReadStream() + { + if (_isClosed) + return; + + try + { + lock (_streamLock) + { + if (_stream is not { IsConnected: true }) + return; + + Logger.Trace("Beginning Read of {0} bytes", _buffer.Length); + _stream.BeginRead(_buffer, 0, _buffer.Length, EndReadStream, _stream.IsConnected); + } + } + catch (ObjectDisposedException) + { + Logger.Warning("Attempted to start reading from a disposed pipe"); + } + catch (InvalidOperationException) + { + Logger.Warning("Attempted to start reading from a closed pipe"); + } + catch (Exception e) + { + Logger.Error("An exception occurred while starting to read a stream: {0}", e.Message); + Logger.Error(e.StackTrace); + } + } + + private void EndReadStream(IAsyncResult callback) + { + Logger.Trace("Ending Read"); + int bytes; + + try + { + lock (_streamLock) + { + if (_stream is not { IsConnected: true }) + return; + + bytes = _stream.EndRead(callback); + } + } + catch (IOException) + { + Logger.Warning("Attempted to end reading from a closed pipe"); + return; + } + catch (NullReferenceException) + { + Logger.Warning("Attempted to read from a null pipe"); + return; + } + catch (ObjectDisposedException) + { + Logger.Warning("Attempted to end reading from a disposed pipe"); + return; + } + catch (Exception e) + { + Logger.Error("An exception occurred while ending a read of a stream: {0}", e.Message); + Logger.Error(e.StackTrace); + return; + } + + Logger.Trace("Read {0} bytes", bytes); + + if (bytes > 0) + { + using MemoryStream memory = new(_buffer, 0, bytes); + try + { + PipeFrame frame = new(); + if (frame.ReadStream(memory)) + { + Logger.Trace("Read a frame: {0}", frame.Opcode); + lock (_frameQueueLock) + _frameQueue.Enqueue(frame); + } + else + { + Logger.Error("Pipe failed to read from the data received by the stream."); + Close(); + } + } + catch (Exception e) + { + Logger.Error("An exception has occurred while trying to parse the pipe data: {0}", e.Message); + Close(); + } + } + else + { + Logger.Error("Empty frame was read on {0}, aborting.", Environment.OSVersion); + Close(); + } + + if (!_isClosed && IsConnected) + { + Logger.Trace("Starting another read"); + BeginReadStream(); + } + } + + private static IEnumerable GetPipeCandidates(int startPipe) + { + if (OperatingSystem.IsWindows()) + { + for (int i = startPipe; i < MaximumPipeVariations; i++) + yield return $"{DiscordPipePrefix}{i}"; + + yield break; + } + + foreach (string runtimeDir in GetUnixRuntimeDirectories()) + { + for (int index = startPipe; index < MaximumPipeVariations; index++) + { + string pipeFileName = $"{DiscordPipePrefix}{index}"; + + foreach (string packageDirectory in s_unixPackageDirectories) + { + string packagePipe = Path.Combine(runtimeDir, packageDirectory, pipeFileName); + if (File.Exists(packagePipe)) + yield return packagePipe; + } + + string defaultPipe = Path.Combine(runtimeDir, pipeFileName); + if (File.Exists(defaultPipe)) + yield return defaultPipe; + + foreach (string packageDirectory in s_unixPackageDirectories) + { + string packagePipe = Path.Combine(runtimeDir, packageDirectory, pipeFileName); + if (!File.Exists(packagePipe)) + yield return packagePipe; + } + + if (!File.Exists(defaultPipe)) + yield return defaultPipe; + } + } + } + + private static IEnumerable GetUnixRuntimeDirectories() + { + HashSet yielded = new(StringComparer.Ordinal); + + string[] candidates = + [ + Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR") ?? string.Empty, + Environment.GetEnvironmentVariable("TMPDIR") ?? string.Empty, + Environment.GetEnvironmentVariable("TMP") ?? string.Empty, + Environment.GetEnvironmentVariable("TEMP") ?? string.Empty, + Path.GetTempPath(), + "/tmp" + ]; + + foreach (string candidate in candidates) + { + if (string.IsNullOrWhiteSpace(candidate)) + continue; + + string normalized = candidate.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + if (yielded.Add(normalized)) + yield return normalized; + } + } + } + } +} diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index f072ba2e..fe33176a 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -73,7 +73,7 @@ namespace MinecraftClient.ChatBots public override void Initialize() { - if (GetProtocolVersion() < Protocol18Handler.MC_1_13_Version) + if (GetProtocolVersion() < Protocol18Handler.MC_1_8_Version) { LogToConsole(Translations.bot_farmer_not_implemented); return; @@ -149,6 +149,10 @@ namespace MinecraftClient.ChatBots if (running) return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_running); + if (!IsCropAvailableForProtocol(whatToFarm, GetProtocolVersion())) + return r.SetAndReturn(CmdResult.Status.Fail, + string.Format(Translations.bot_farmer_crop_unavailable, whatToFarm, "1.9")); + var movementLock = BotMovementLock.Instance; if (movementLock is { IsLocked: true }) return r.SetAndReturn(CmdResult.Status.Fail, @@ -369,11 +373,11 @@ namespace MinecraftClient.ChatBots break; } - var loc = new Location(Math.Floor(location.X), Math.Floor(location2.Y), + var loc = new Location(Math.Floor(location.X), Math.Floor(location.Y), Math.Floor(location.Z)); LogDebug("Sending placeblock to: " + loc); - SendPlaceBlock(loc, Direction.Up); + SendPlaceBlock(loc, Direction.Up, lookAtBlock: true); Thread.Sleep(300); } else LogDebug("Can't move to: " + location2); @@ -496,7 +500,7 @@ namespace MinecraftClient.ChatBots { // TODO: Do a check if the carrot/potato is on the first growth stage // if so, use: new Location(location.X, (double)(location.Y - 1) + (double)0.93750, location.Z) - SendPlaceBlock(location2, Direction.Down); + SendPlaceBlock(location2, Direction.Down, lookAtBlock: true); } Thread.Sleep(100); @@ -591,6 +595,15 @@ namespace MinecraftClient.ChatBots }; } + private static bool IsCropAvailableForProtocol(CropType type, int protocolVersion) + { + return type switch + { + CropType.Beetroot => protocolVersion >= Protocol18Handler.MC_1_9_Version, + _ => true + }; + } + private List FindEmptyFarmland(int radius) { return GetWorld() @@ -616,16 +629,19 @@ namespace MinecraftClient.ChatBots if (fullyGrown && material is Material.Melon or Material.Pumpkin) return true; - var isFullyGrown = IsCropFullyGrown(GetWorld().GetBlock(location), cropType); + var isFullyGrown = IsCropFullyGrown(GetWorld().GetBlock(location), cropType, location); return fullyGrown ? isFullyGrown : !isFullyGrown; }) .ToList(); } - private bool IsCropFullyGrown(Block block, CropType cropType) + private bool IsCropFullyGrown(Block block, CropType cropType, Location? location = null) { var protocolVersion = GetProtocolVersion(); + if (protocolVersion < Protocol18Handler.MC_1_13_Version) + return IsLegacyCropFullyGrown(block, cropType, location); + switch (cropType) { case CropType.Beetroot: @@ -781,6 +797,44 @@ namespace MinecraftClient.ChatBots return false; } + private bool IsLegacyCropFullyGrown(Block block, CropType cropType, Location? location) + { + return cropType switch + { + CropType.Beetroot => block.BlockId == 207 && block.BlockMeta >= 3, + CropType.Carrot => block.BlockId == 141 && block.BlockMeta >= 7, + CropType.Melon => block.BlockId == 105 + && (block.BlockMeta >= 7 || HasAdjacentBlock(location, Material.Melon)), + CropType.NetherWart => block.BlockId == 115 && block.BlockMeta >= 3, + CropType.Pumpkin => block.BlockId == 104 + && (block.BlockMeta >= 7 || HasAdjacentBlock(location, Material.Pumpkin)), + CropType.Potato => block.BlockId == 142 && block.BlockMeta >= 7, + CropType.Wheat => block.BlockId == 59 && block.BlockMeta >= 7, + _ => false + }; + } + + private bool HasAdjacentBlock(Location? location, Material material) + { + if (location is not Location stemLocation) + return false; + + var world = GetWorld(); + int x = (int)Math.Floor(stemLocation.X); + int y = (int)Math.Floor(stemLocation.Y); + int z = (int)Math.Floor(stemLocation.Z); + + Location[] adjacentLocations = + [ + new(x + 1, y, z), + new(x - 1, y, z), + new(x, y, z + 1), + new(x, y, z - 1) + ]; + + return adjacentLocations.Any(adjacentLocation => world.GetBlock(adjacentLocation).Type == material); + } + // Yoinked from ReinforceZwei's AutoTree and adapted to search the whole of inventory in additon to the hotbar private bool SwitchToItem(ItemType itemType) { @@ -831,7 +885,7 @@ namespace MinecraftClient.ChatBots // Yoinked from Daenges's Sugarcane Farmer private bool WaitForDigBlock(Location block, int digTimeout = 1000) { - if (!DigBlock(block.ToFloor())) return false; + if (!DigBlock(block.ToFloor(), Direction.Down)) return false; short i = 0; // Maximum wait time of 10 sec. while (GetWorld().GetBlock(block).Type != Material.Air && i <= digTimeout) { @@ -854,4 +908,4 @@ namespace MinecraftClient.ChatBots else LogDebugToConsole(text); } } -} \ No newline at end of file +} diff --git a/MinecraftClient/ChatBots/FileInputBot.cs b/MinecraftClient/ChatBots/FileInputBot.cs new file mode 100644 index 00000000..7d0069e4 --- /dev/null +++ b/MinecraftClient/ChatBots/FileInputBot.cs @@ -0,0 +1,105 @@ +using System; +using System.IO; +using System.Threading; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; + +namespace MinecraftClient.ChatBots +{ + /// + /// Debug-only ChatBot that monitors a text file for commands. + /// Write lines to the file from any external tool (e.g. Cursor Shell) + /// and this bot will execute them as MCC internal commands. + /// + /// Usage from Cursor Shell: + /// Add-Content mcc_input.txt "inventory" + /// Add-Content mcc_input.txt "send /give @s diamond_sword 1" + /// + /// Lines starting with "/" are sent as server chat; others are treated + /// as MCC internal commands (same as typing in the MCC console). + /// + public class FileInputBot : ChatBot + { + private const string BotName = "FileInput"; + private string _filePath = string.Empty; + private long _lastPosition; + private int _tickCounter; + + public override void Initialize() + { + _filePath = Path.GetFullPath( + Environment.GetEnvironmentVariable("MCC_INPUT_FILE") ?? "mcc_input.txt"); + + if (File.Exists(_filePath)) + _lastPosition = new FileInfo(_filePath).Length; + else + File.WriteAllText(_filePath, ""); + + LogToConsole(BotName, $"Watching: {_filePath}"); + LogToConsole(BotName, "Write commands to this file to execute them."); + } + + public override void Update() + { + // Poll every ~500ms while the MCC main loop runs at 20 TPS. + if (++_tickCounter < Settings.DoubleToTick(0.5)) + return; + _tickCounter = 0; + + try + { + if (!File.Exists(_filePath)) + return; + + var info = new FileInfo(_filePath); + if (info.Length <= _lastPosition) + return; + + string newContent; + using (var fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + fs.Seek(_lastPosition, SeekOrigin.Begin); + using var reader = new StreamReader(fs); + newContent = reader.ReadToEnd(); + } + _lastPosition = info.Length; + + foreach (var rawLine in newContent.Split('\n')) + { + var line = rawLine.Trim(); + if (string.IsNullOrEmpty(line)) + continue; + + LogToConsole(BotName, $"> {line}"); + + if (line.StartsWith("/")) + { + SendText(line); + } + else + { + CmdResult result = new(); + if (PerformInternalCommand(line, ref result)) + { + if (!string.IsNullOrEmpty(result.ToString())) + LogToConsole(BotName, result.ToString()); + } + else + { + // Not an internal command — send as chat + SendText(line); + } + } + } + } + catch (IOException) + { + // File may be temporarily locked by the writer + } + catch (Exception ex) + { + LogToConsole(BotName, $"Error: {ex.Message}"); + } + } + } +} diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index 60a40029..09df5f0a 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -110,13 +110,13 @@ namespace MinecraftClient.ChatBots && !string.IsNullOrEmpty(entity.Name) && entity.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - if (player == null) + if (player is null) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_player); if (!CanMoveThere(player.Location)) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_cant_reach_player); - if (_playerToFollow != null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase)) + if (_playerToFollow is not null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase)) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_follow_already_following, _playerToFollow)); @@ -127,7 +127,7 @@ namespace MinecraftClient.ChatBots var result = string.Format( - _playerToFollow != null ? Translations.cmd_follow_switched : Translations.cmd_follow_started, + _playerToFollow is not null ? Translations.cmd_follow_switched : Translations.cmd_follow_started, player.Name!); _playerToFollow = name.ToLower(); @@ -152,7 +152,7 @@ namespace MinecraftClient.ChatBots private int OnCommandStop(CmdResult r) { - if (_playerToFollow == null) + if (_playerToFollow is null) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_already_stopped); var movementLock = BotMovementLock.Instance; @@ -172,7 +172,7 @@ namespace MinecraftClient.ChatBots if (entity.Type != EntityType.Player) return; - if (_playerToFollow == null || string.IsNullOrEmpty(entity.Name)) + if (_playerToFollow is null || string.IsNullOrEmpty(entity.Name)) return; if (_playerToFollow != entity.Name.ToLower()) @@ -200,7 +200,7 @@ namespace MinecraftClient.ChatBots if (entity.Type != EntityType.Player) return; - if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) && + if (_playerToFollow is not null && !string.IsNullOrEmpty(entity.Name) && _playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase)) { LogToConsole(string.Format(Translations.cmd_follow_player_came_to_the_range, _playerToFollow)); @@ -213,7 +213,7 @@ namespace MinecraftClient.ChatBots if (entity.Type != EntityType.Player) return; - if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) && + if (_playerToFollow is not null && !string.IsNullOrEmpty(entity.Name) && _playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase)) { LogToConsole(string.Format(Translations.cmd_follow_player_left_the_range, _playerToFollow)); @@ -223,7 +223,7 @@ namespace MinecraftClient.ChatBots public override void OnPlayerLeave(Guid uuid, string? name) { - if (_playerToFollow != null && !string.IsNullOrEmpty(name) && + if (_playerToFollow is not null && !string.IsNullOrEmpty(name) && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase)) { LogToConsole(string.Format(Translations.cmd_follow_player_left, _playerToFollow)); @@ -235,7 +235,7 @@ namespace MinecraftClient.ChatBots private bool CanMoveThere(Location location) { var chunkColumn = GetWorld().GetChunkColumn(location); - return chunkColumn != null && chunkColumn.FullyLoaded != false; + return chunkColumn is not null && chunkColumn.FullyLoaded != false; } } } \ No newline at end of file diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index 49367ba0..1d84b74f 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; +using System.Threading; using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; @@ -218,7 +219,7 @@ namespace MinecraftClient.ChatBots private IgnoreList ignoreList = new(); private FileMonitor? mailDbFileMonitor; private FileMonitor? ignoreListFileMonitor; - private readonly object readWriteLock = new(); + private readonly Lock readWriteLock = new(); /// /// Initialization of the Mailer bot @@ -423,7 +424,7 @@ namespace MinecraftClient.ChatBots } /// - /// Called on each MCC tick, around 10 times per second + /// Called on each MCC tick, around 20 times per second /// public override void Update() { diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index 25e75a74..a686c39a 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Threading.Tasks; +using Avalonia.Threading; using Brigadier.NET; using Brigadier.NET.Builder; using ImageMagick; @@ -11,6 +12,7 @@ using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; using MinecraftClient.Scripting; +using MinecraftClient.Tui; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -142,7 +144,12 @@ namespace MinecraftClient.ChatBots SaveToFile(map); if (Config.Render_In_Console) - RenderInConsole(map); + { + if (ConsoleIO.Backend is TuiConsoleBackend) + RenderInTui(map); + else + RenderInConsole(map); + } return r.SetAndReturn(CmdResult.Status.Done); } @@ -213,7 +220,12 @@ namespace MinecraftClient.ChatBots SaveToFile(map); if (Config.Render_In_Console) - RenderInConsole(map); + { + if (ConsoleIO.Backend is TuiConsoleBackend) + RenderInTui(map); + else + RenderInConsole(map); + } } } @@ -259,7 +271,8 @@ namespace MinecraftClient.ChatBots { using (var image = new MagickImage(fileName)) { - var size = new MagickGeometry(Config.Resize_To, Config.Resize_To); + uint resizeTo = (uint)Math.Max(Config.Resize_To, 1); + var size = new MagickGeometry(resizeTo, resizeTo); size.IgnoreAspectRatio = true; image.Resize(size); @@ -283,13 +296,13 @@ namespace MinecraftClient.ChatBots if (Config.Send_Rendered_To_Discord) { - if (discordBridge == null || (discordBridge != null && !discordBridge.IsConnected)) + if (discordBridge is null || (discordBridge is not null && !discordBridge.IsConnected)) return; } if (Config.Send_Rendered_To_Telegram) { - if (telegramBridge == null || (telegramBridge != null && !telegramBridge.IsConnected)) + if (telegramBridge is null || (telegramBridge is not null && !telegramBridge.IsConnected)) return; } @@ -341,11 +354,32 @@ namespace MinecraftClient.ChatBots } } + private static void RenderInTui(McMap map) + { + var view = TuiConsoleBackend.Instance?.GetView(); + if (view is null) + return; + + Dispatcher.UIThread.Post(() => + { + if (view.HasOverlay && view.OverlayContent is MapOverlay existing) + { + existing.UpdateMap(map); + return; + } + + view.ShowOverlay(new MapOverlay(map)); + }); + } + private static void RenderInConsole(McMap map) { StringBuilder sb = new(); - int consoleWidth = Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2; - int consoleHeight = Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 1; + int safeBufWidth, safeBufHeight; + try { safeBufWidth = Console.BufferWidth; } catch { safeBufWidth = 120; } + try { safeBufHeight = Console.BufferHeight; } catch { safeBufHeight = 50; } + int consoleWidth = Math.Max(safeBufWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2; + int consoleHeight = Math.Max(safeBufHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 1; int scaleX = (map.Width + consoleWidth - 1) / consoleWidth; int scaleY = (map.Height + consoleHeight - 1) / consoleHeight; int scale = Math.Max(scaleX, scaleY); @@ -443,109 +477,62 @@ namespace MinecraftClient.ChatBots public DateTime LastUpdated { get; set; } } - internal class MapColors + /// + /// Map packet base color palette. Colors are loaded from the embedded + /// MinimapBlockColors.json resource (map_palette section) generated by + /// tools/gen_block_color_map.py, which parses MapColor.java. + /// + internal static class MapColors { - // When colors are updated in a new update, you can get them using the game code: net\minecraft\world\level\material\MaterialColor.java - public static Dictionary Colors = new() + private static readonly Dictionary Colors; + + private static readonly byte[] ShadeMultipliers = [180, 220, 255, 135]; + + static MapColors() { - //Color ID R G B - {0, new byte[]{0, 0, 0}}, - {1, new byte[]{127, 178, 56}}, - {2, new byte[]{247, 233, 163}}, - {3, new byte[]{199, 199, 199}}, - {4, new byte[]{255, 0, 0}}, - {5, new byte[]{160, 160, 255}}, - {6, new byte[]{167, 167, 167}}, - {7, new byte[]{0, 124, 0}}, - {8, new byte[]{255, 255, 255}}, - {9, new byte[]{164, 168, 184}}, - {10, new byte[]{151, 109, 77}}, - {11, new byte[]{112, 112, 112}}, - {12, new byte[]{64, 64, 255}}, - {13, new byte[]{143, 119, 72}}, - {14, new byte[]{255, 252, 245}}, - {15, new byte[]{216, 127, 51}}, - {16, new byte[]{178, 76, 216}}, - {17, new byte[]{102, 153, 216}}, - {18, new byte[]{229, 229, 51}}, - {19, new byte[]{127, 204, 25}}, - {20, new byte[]{242, 127, 165}}, - {21, new byte[]{76, 76, 76}}, - {22, new byte[]{153, 153, 153}}, - {23, new byte[]{76, 127, 153}}, - {24, new byte[]{127, 63, 178}}, - {25, new byte[]{51, 76, 178}}, - {26, new byte[]{102, 76, 51}}, - {27, new byte[]{102, 127, 51}}, - {28, new byte[]{153, 51, 51}}, - {29, new byte[]{25, 25, 25}}, - {30, new byte[]{250, 238, 77}}, - {31, new byte[]{92, 219, 213}}, - {32, new byte[]{74, 128, 255}}, - {33, new byte[]{0, 217, 58}}, - {34, new byte[]{129, 86, 49}}, - {35, new byte[]{112, 2, 0}}, - {36, new byte[]{209, 177, 161}}, - {37, new byte[]{159, 82, 36}}, - {38, new byte[]{149, 87, 108}}, - {39, new byte[]{112, 108, 138}}, - {40, new byte[]{186, 133, 36}}, - {41, new byte[]{103, 117, 53}}, - {42, new byte[]{160, 77, 78}}, - {43, new byte[]{57, 41, 35}}, - {44, new byte[]{135, 107, 98}}, - {45, new byte[]{87, 92, 92}}, - {46, new byte[]{122, 73, 88}}, - {47, new byte[]{76, 62, 92}}, - {48, new byte[]{76, 50, 35}}, - {49, new byte[]{76, 82, 42}}, - {50, new byte[]{142, 60, 46}}, - {51, new byte[]{37, 22, 16}}, - {52, new byte[]{189, 48, 49}}, - {53, new byte[]{148, 63, 97}}, - {54, new byte[]{92, 25, 29}}, - {55, new byte[]{22, 126, 134}}, - {56, new byte[]{58, 142, 140}}, - {57, new byte[]{86, 44, 62}}, - {58, new byte[]{20, 180, 133}}, - {59, new byte[]{100, 100, 100}}, - {60, new byte[]{216, 175, 147}}, - {61, new byte[]{127, 167, 150}} - }; + Colors = new Dictionary(); + try + { + using var stream = System.Reflection.Assembly.GetExecutingAssembly() + .GetManifestResourceStream("MinimapBlockColors.json"); + if (stream is not null) + { + using var doc = System.Text.Json.JsonDocument.Parse(stream); + if (doc.RootElement.TryGetProperty("map_palette", out var palette)) + { + foreach (var prop in palette.EnumerateObject()) + { + if (!byte.TryParse(prop.Name, out byte id)) + continue; + var arr = prop.Value; + Colors[id] = [ + arr[0].GetByte(), + arr[1].GetByte(), + arr[2].GetByte() + ]; + } + } + } + } + catch (Exception ex) + { + ConsoleIO.WriteLogLine($"[Map] Failed to load map palette: {ex.Message}"); + } + } public static ColorRGBA ColorByteToRGBA(byte receivedColorId) { - // Divide received color id by 4 to get the base color id - // Much thanks to DevBobcorn byte baseColorId = (byte)(receivedColorId >> 2); - // Any new colors that we haven't added will be purple like in the missing CS: Source Texture - if (!Colors.ContainsKey(baseColorId)) + if (!Colors.TryGetValue(baseColorId, out byte[]? rgb)) return new(248, 0, 248, 255, true); - byte shadeId = (byte)(receivedColorId % 4); - byte shadeMultiplier = 255; - - switch (shadeId) - { - case 0: - shadeMultiplier = 180; - break; - - case 1: - shadeMultiplier = 220; - break; - - case 3: - // NOTE: If we ever add map support below 1.8, this needs to be 220 before 1.8 - shadeMultiplier = 135; - break; - } + byte multiplier = ShadeMultipliers[receivedColorId & 3]; return new( - r: (byte)((Colors[baseColorId][0] * shadeMultiplier) / 255), - g: (byte)((Colors[baseColorId][1] * shadeMultiplier) / 255), - b: (byte)((Colors[baseColorId][2] * shadeMultiplier) / 255), + r: (byte)(rgb[0] * multiplier / 255), + g: (byte)(rgb[1] * multiplier / 255), + b: (byte)(rgb[2] * multiplier / 255), a: 255 ); } diff --git a/MinecraftClient/ChatBots/McpServer.cs b/MinecraftClient/ChatBots/McpServer.cs new file mode 100644 index 00000000..f9f7394d --- /dev/null +++ b/MinecraftClient/ChatBots/McpServer.cs @@ -0,0 +1,272 @@ +using System; +using MinecraftClient.Mapping; +using MinecraftClient.Mcp; +using MinecraftClient.Scripting; +using Tomlet.Attributes; + +namespace MinecraftClient.ChatBots +{ + public class McpServer : ChatBot + { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "McpServer"; + + [TomlInlineComment("$ChatBot.McpServer.Enabled$")] + public bool Enabled = false; + + [TomlPrecedingComment("$ChatBot.McpServer.Transport$")] + public MccMcpTransportConfig Transport = new(); + + [TomlPrecedingComment("$ChatBot.McpServer.Capabilities$")] + public MccMcpCapabilityToggles Capabilities = new(); + + public void OnSettingUpdate() + { + Transport ??= new MccMcpTransportConfig(); + Capabilities ??= new MccMcpCapabilityToggles(); + + if (Transport.Port is < 1 or > 65535) + Transport.Port = 33333; + + if (string.IsNullOrWhiteSpace(Transport.BindHost)) + Transport.BindHost = "127.0.0.1"; + + if (string.IsNullOrWhiteSpace(Transport.Route)) + Transport.Route = "/mcp"; + + if (!Transport.Route.StartsWith('/')) + Transport.Route = "/" + Transport.Route; + + if (string.IsNullOrWhiteSpace(Transport.AuthTokenEnvVar)) + Transport.AuthTokenEnvVar = "MCC_MCP_AUTH_TOKEN"; + } + } + + private MccEmbeddedMcpHost? host; + + public override void Initialize() + { + Config.OnSettingUpdate(); + } + + public override void AfterGameJoined() + { + if (!Config.Enabled) + return; + + ClearStores(); + + MccMcpConfig mcpConfig = new() + { + Enabled = Config.Enabled, + Transport = Config.Transport, + Capabilities = Config.Capabilities + }; + + host ??= new MccEmbeddedMcpHost(mcpConfig, new MccMcpCapabilities(() => Config.Capabilities)); + + if (host.IsRunning) + return; + + LogToConsole(Translations.bot_mcpserver_starting); + if (!host.Start(out string? error)) + { + if (error == "missing_auth_token") + LogToConsole(string.Format(Translations.bot_mcpserver_missing_auth_token, Config.Transport.AuthTokenEnvVar)); + LogToConsole(string.Format(Translations.bot_mcpserver_start_failed, error ?? "unknown")); + return; + } + + LogToConsole(string.Format(Translations.bot_mcpserver_started, host.Endpoint)); + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + MccObservedStateStore.AddRecentEvent("disconnect", new + { + reason = reason.ToString(), + message + }); + StopHost(); + ClearStores(); + return false; + } + + public override void OnUnload() + { + StopHost(); + ClearStores(); + } + + public override void GetText(string text, string? json) + { + string clean = GetVerbatim(text); + if (string.IsNullOrWhiteSpace(clean)) + return; + + string kind = "system"; + string? sender = null; + string? message = null; + + string parsedMessage = string.Empty; + string parsedSender = string.Empty; + if (IsPrivateMessage(clean, ref parsedMessage, ref parsedSender)) + { + kind = "private"; + sender = parsedSender; + message = parsedMessage; + } + else if (IsChatMessage(clean, ref parsedMessage, ref parsedSender)) + { + kind = "chat"; + sender = parsedSender; + message = parsedMessage; + } + + MccObservedStateStore.AddChatHistoryEntry(new MccChatHistoryEntry + { + TimestampUtc = DateTimeOffset.UtcNow, + Kind = kind, + Text = clean, + Sender = sender, + Message = message, + Json = json + }); + } + + public override void OnTimeUpdate(long WorldAge, long TimeOfDay) + { + MccObservedStateStore.SetTime(WorldAge, TimeOfDay); + } + + public override void OnRainLevelChange(float level) + { + MccObservedStateStore.SetRainLevel(level); + MccObservedStateStore.AddRecentEvent("weather_rain", new { level }); + } + + public override void OnThunderLevelChange(float level) + { + MccObservedStateStore.SetThunderLevel(level); + MccObservedStateStore.AddRecentEvent("weather_thunder", new { level }); + } + + public override void OnDeath() + { + MccObservedStateStore.AddRecentEvent("death"); + } + + public override void OnRespawn() + { + MccObservedStateStore.AddRecentEvent("respawn"); + } + + public override void OnPlayerJoin(Guid uuid, string name) + { + MccObservedStateStore.AddRecentEvent("player_join", new + { + uuid, + name + }); + } + + public override void OnPlayerLeave(Guid uuid, string? name) + { + MccObservedStateStore.AddRecentEvent("player_leave", new + { + uuid, + name + }); + } + + public override void OnInventoryOpen(int inventoryId) + { + MccObservedStateStore.AddRecentEvent("inventory_open", new { inventoryId }); + } + + public override void OnInventoryClose(int inventoryId) + { + MccObservedStateStore.AddRecentEvent("inventory_close", new { inventoryId }); + } + + public override void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json) + { + if (action == 2) + { + MccObservedStateStore.AddRecentEvent("actionbar", new + { + action, + text = actionbartext, + fadein, + stay, + fadeout, + json + }); + return; + } + + if (action is 0 or 1) + { + MccObservedStateStore.AddRecentEvent("title", new + { + action, + titleText = titletext, + subtitleText = subtitletext, + fadein, + stay, + fadeout, + json + }); + } + } + + public override void OnBlockBreakAnimation(Entity entity, Location location, byte stage) + { + MccObservedStateStore.AddRecentEvent("block_break_animation", new + { + entityId = entity.ID, + entityType = entity.Type.ToString(), + stage, + location = new + { + x = location.X, + y = location.Y, + z = location.Z + } + }); + } + + public override void OnEntityAnimation(Entity entity, byte animation) + { + MccObservedStateStore.AddRecentEvent("entity_animation", new + { + entityId = entity.ID, + entityType = entity.Type.ToString(), + animation, + name = entity.Name, + customName = entity.CustomName + }); + } + + private void StopHost() + { + if (host is null || !host.IsRunning) + return; + + if (host.Stop(out string? error)) + LogToConsole(Translations.bot_mcpserver_stopped); + else + LogToConsole(string.Format(Translations.bot_mcpserver_stop_failed, error ?? "unknown")); + } + + private static void ClearStores() + { + MccObservedStateStore.ClearAll(); + } + } +} diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index eea74911..1c9b50cf 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; @@ -42,8 +43,7 @@ namespace MinecraftClient.ChatBots public override void Initialize() { SetNetworkPacketEventEnabled(true); - replay = new ReplayHandler(GetProtocolVersion()); - replay.MetaData.serverName = GetServerHost() + GetServerPort(); + replay = new ReplayHandler(GetProtocolVersion(), $"{GetServerHost()}:{GetServerPort()}"); backupCounter = Settings.DoubleToTick(Config.Backup_Interval); McClient.dispatcher.Register(l => l.Literal("help") @@ -67,6 +67,8 @@ namespace MinecraftClient.ChatBots { McClient.dispatcher.Unregister(CommandName); McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + replay?.Dispose(); + replay = null; } private int OnCommandHelp(CmdResult r, string? cmd) @@ -84,9 +86,9 @@ namespace MinecraftClient.ChatBots { try { - if (replay!.RecordRunning) + if (replay is { RecordRunning: true }) { - replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); + replay.CreateBackupReplay(Path.Combine(replay.ReplayFileDirectory, replay.GetReplayDefaultName())); return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_created); } else @@ -102,7 +104,7 @@ namespace MinecraftClient.ChatBots { try { - if (replay!.RecordRunning) + if (replay is { RecordRunning: true }) { replay.OnShutDown(); return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_stopped); @@ -118,16 +120,16 @@ namespace MinecraftClient.ChatBots public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) { - replay!.AddPacket(packetID, packetData, isLogin, isInbound); + replay?.AddPacket(packetID, packetData, isLogin, isInbound); } public override void Update() { - if (Config.Backup_Interval > 0 && replay!.RecordRunning) + if (Config.Backup_Interval > 0 && replay is { RecordRunning: true }) { if (backupCounter <= 0) { - replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr"); + replay.CreateBackupReplay(replay.GetBackupReplayPath()); backupCounter = Settings.DoubleToTick(Config.Backup_Interval); } else backupCounter--; @@ -136,7 +138,7 @@ namespace MinecraftClient.ChatBots public override bool OnDisconnect(DisconnectReason reason, string message) { - replay!.OnShutDown(); + replay?.OnShutDown(); return base.OnDisconnect(reason, message); } } diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 4823f9de..4f764aa4 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -19,12 +19,13 @@ namespace MinecraftClient.ChatBots private string? file; private string[] lines = Array.Empty(); private string[] args = Array.Empty(); - private int sleepticks = 10; + private int sleepticks = Settings.ClientTicksPerSecond; private int nextline = 0; private readonly string? owner; private bool csharp; private Thread? thread; private readonly Dictionary? localVars; + private readonly string? scriptOwnerKey; public Script(string filename) { @@ -38,6 +39,13 @@ namespace MinecraftClient.ChatBots this.localVars = localVars; } + internal Script(string filename, string? ownername, Dictionary? localVars, string? scriptOwnerKey) + : this(filename, ownername, localVars) + { + this.scriptOwnerKey = scriptOwnerKey; + SetScriptOwnerKey(scriptOwnerKey); + } + private void ParseArguments(string argstr) { List args = new(); @@ -86,7 +94,7 @@ namespace MinecraftClient.ChatBots public static bool LookForScript(ref string filename) { //Automatically look in subfolders and try to add ".txt" file extension - char dir_slash = Path.DirectorySeparatorChar; + char dir_slash = Path.DirectorySeparatorChar; string[] files = new string[] { filename, @@ -149,24 +157,30 @@ namespace MinecraftClient.ChatBots } } + public override bool OnDisconnect(DisconnectReason reason, string message) + { + UnloadBot(); + return false; + } + public override void Update() { if (csharp) //C# compiled script { //Initialize thread on first update - if (thread == null) + if (thread is null) { thread = new Thread(() => { try { - CSharpRunner.Run(this, lines, args, localVars, scriptName: file!); + CSharpRunner.Run(this, lines, args, localVars, scriptName: file!, scriptOwnerKey: scriptOwnerKey); } catch (CSharpException e) { string errorMessage = string.Format(Translations.bot_script_fail, file, e.ExceptionType); LogToConsole(errorMessage); - if (owner != null) + if (owner is not null) SendPrivateMessage(owner, errorMessage); LogToConsole(e.InnerException); } @@ -178,7 +192,7 @@ namespace MinecraftClient.ChatBots } //Unload bot once the thread has finished running - if (thread != null && !thread.IsAlive) + if (thread is not null && !thread.IsAlive) { UnloadBot(); } @@ -202,7 +216,7 @@ namespace MinecraftClient.ChatBots switch (instruction_name.ToLower()) { case "wait": - int ticks = 10; + int ticks = Settings.ClientTicksPerSecond; try { if (instruction_line[5..].Contains("to", StringComparison.OrdinalIgnoreCase) || @@ -213,7 +227,7 @@ namespace MinecraftClient.ChatBots .ToLower(); processedLine = string.Join("", processedLine.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries)); var parts = processedLine.Contains("to") ? processedLine.Split("to") : processedLine.Split("-"); - + if (parts.Length == 2) { var min = Convert.ToInt32(parts[0]); @@ -224,10 +238,12 @@ namespace MinecraftClient.ChatBots (min, max) = (max, min); LogToConsole(Translations.cmd_wait_random_min_bigger); } - + ticks = new Random().Next(min, max); - } else ticks = Convert.ToInt32(instruction_line[5..]); - } else ticks = Convert.ToInt32(instruction_line[5..]); + } + else ticks = Convert.ToInt32(instruction_line[5..]); + } + else ticks = Convert.ToInt32(instruction_line[5..]); } catch { } sleepticks = ticks; diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index 0e15949d..f5af44ec 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -116,6 +116,12 @@ namespace MinecraftClient.ChatBots public bool Enable = false; public TimeSpan[] Times; + public TriggerOnTimeConfig() + { + Enable = false; + Times = Array.Empty(); + } + public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time) { this.Enable = Enable; @@ -134,6 +140,13 @@ namespace MinecraftClient.ChatBots public bool Enable = false; public double MinTime, MaxTime; + public TriggerOnIntervalConfig() + { + Enable = false; + MinTime = 0; + MaxTime = 0; + } + public TriggerOnIntervalConfig(double value) { this.Enable = true; @@ -167,67 +180,57 @@ namespace MinecraftClient.ChatBots private static bool firstlogin_done = false; private bool serverlogin_done = false; - private int verifytasks_timeleft = 10; - private readonly int verifytasks_delay = 10; + private int verifytasks_timeleft = Settings.ClientTicksPerSecond; + private readonly int verifytasks_delay = Settings.ClientTicksPerSecond; + + public override void AfterGameJoined() + { + if (serverlogin_done) + return; + + serverlogin_done = true; + verifytasks_timeleft = verifytasks_delay; + RunLoginTasks(); + } public override void Update() { + if (!serverlogin_done) + return; + if (verifytasks_timeleft <= 0) { verifytasks_timeleft = verifytasks_delay; - if (serverlogin_done) + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) { - foreach (TaskConfig task in Config.TaskList) + TaskConfig task = Config.TaskList[taskIndex]; + if (task.Trigger_On_Times.Enable) { - if (task.Trigger_On_Times.Enable) - { - bool matching_time_found = false; + bool matching_time_found = false; - foreach (TimeSpan time in task.Trigger_On_Times.Times) + foreach (TimeSpan time in task.Trigger_On_Times.Times) + { + if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute) { - if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute) + matching_time_found = true; + if (!task.Trigger_On_Time_Already_Triggered) { - matching_time_found = true; - if (!task.Trigger_On_Time_Already_Triggered) - { - task.Trigger_On_Time_Already_Triggered = true; - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_time, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); - } + task.Trigger_On_Time_Already_Triggered = true; + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_time, task.Action)); } } - - if (!matching_time_found) - task.Trigger_On_Time_Already_Triggered = false; } + if (!matching_time_found) + task.Trigger_On_Time_Already_Triggered = false; } } - else - { - foreach (TaskConfig task in Config.TaskList) - { - if (task.Trigger_On_Login || (firstlogin_done == false && task.Trigger_On_First_Login)) - { - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_login, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); - } - } - - firstlogin_done = true; - serverlogin_done = true; - } } else verifytasks_timeleft--; - foreach (TaskConfig task in Config.TaskList) + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) { + TaskConfig task = Config.TaskList[taskIndex]; if (task.Trigger_On_Interval.Enable) { if (task.Trigger_On_Interval_Countdown == 0) @@ -235,11 +238,7 @@ namespace MinecraftClient.ChatBots task.Trigger_On_Interval_Countdown = random.Next( Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime) ); - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action)); } else task.Trigger_On_Interval_Countdown--; } @@ -252,6 +251,58 @@ namespace MinecraftClient.ChatBots return false; } + private void RunLoginTasks() + { + bool isFirstLogin = !firstlogin_done; + + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) + { + TaskConfig task = Config.TaskList[taskIndex]; + if (task.Trigger_On_Login || (isFirstLogin && task.Trigger_On_First_Login)) + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_login, task.Action)); + } + + firstlogin_done = true; + } + + private void RunTaskAction(TaskConfig task, int taskIndex, string debugMessage) + { + LogDebugToConsole(debugMessage); + + if (TryRunOwnedScript(task, taskIndex)) + return; + + CmdResult response = new(); + PerformInternalCommand(task.Action, ref response); + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + LogToConsole(response); + } + + private bool TryRunOwnedScript(TaskConfig task, int taskIndex) + { + string action = task.Action.Trim(); + const string scriptCommand = "script"; + if (!action.StartsWith(scriptCommand, StringComparison.OrdinalIgnoreCase)) + return false; + + if (action.Length == scriptCommand.Length || !char.IsWhiteSpace(action[scriptCommand.Length])) + return false; + + string scriptArgs = action[scriptCommand.Length..].Trim(); + if (string.IsNullOrWhiteSpace(scriptArgs)) + return false; + + string scriptOwnerKey = BuildScriptOwnerKey(task, taskIndex); + Handler.UnloadBotsByScriptOwnerKey(scriptOwnerKey); + Handler.BotLoad(new Script(scriptArgs, null, null, scriptOwnerKey)); + return true; + } + + private static string BuildScriptOwnerKey(TaskConfig task, int taskIndex) + { + return $"{nameof(ScriptScheduler)}:{taskIndex}:{task.Task_Name}:{task.Action.Trim()}"; + } + private static string Task2String(TaskConfig task) { return string.Format( diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index f30b7107..f53ea28c 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -12,7 +12,6 @@ using Telegram.Bot.Exceptions; using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; -using Telegram.Bot.Types.InputFiles; using Tomlet.Attributes; using File = System.IO.File; @@ -149,7 +148,7 @@ namespace MinecraftClient.ChatBots private void Disconnect() { - if (botClient != null) + if (botClient is not null) { try { @@ -195,7 +194,7 @@ namespace MinecraftClient.ChatBots else message = text; - SendMessage(message); + SendRawMessage(message); } public void SendMessage(string message) @@ -205,7 +204,23 @@ namespace MinecraftClient.ChatBots try { - botClient!.SendTextMessageAsync(Config.ChannelId.Trim(), message, ParseMode.Markdown).Wait(Config.Message_Send_Timeout); + botClient!.SendMessage(Config.ChannelId.Trim(), message, parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout); + } + catch (Exception e) + { + LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_canceled_sending); + LogDebugToConsole(e); + } + } + + public void SendRawMessage(string message) + { + if (!CanSendMessages() || string.IsNullOrEmpty(message)) + return; + + try + { + botClient!.SendMessage(Config.ChannelId.Trim(), message).Wait(Config.Message_Send_Timeout); } catch (Exception e) { @@ -224,9 +239,9 @@ namespace MinecraftClient.ChatBots string fileName = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..]; Stream stream = File.OpenRead(filePath); - botClient!.SendDocumentAsync( + botClient!.SendDocument( Config.ChannelId.Trim(), - document: new InputOnlineFile(content: stream, fileName), + document: InputFile.FromStream(stream, fileName), caption: text, parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout * 1000); } @@ -239,7 +254,7 @@ namespace MinecraftClient.ChatBots private bool CanSendMessages() { - return botClient != null && !string.IsNullOrEmpty(Config.ChannelId.Trim()) && bridgeDirection != BridgeDirection.Minecraft; + return botClient is not null && !string.IsNullOrEmpty(Config.ChannelId.Trim()) && bridgeDirection != BridgeDirection.Minecraft; } async Task MainAsync() @@ -260,14 +275,14 @@ namespace MinecraftClient.ChatBots cancellationToken = new CancellationTokenSource(); botClient.StartReceiving( - updateHandler: HandleUpdateAsync, - pollingErrorHandler: HandlePollingErrorAsync, - receiverOptions: new ReceiverOptions + HandleUpdateAsync, + HandlePollingErrorAsync, + new ReceiverOptions { // receive all update types AllowedUpdates = Array.Empty() }, - cancellationToken: cancellationToken.Token + cancellationToken.Token ); IsConnected = true; @@ -313,9 +328,9 @@ namespace MinecraftClient.ChatBots if (text.ToLower().Contains(".chatid")) { - await botClient.SendTextMessageAsync(chatId: chatId, - replyToMessageId: message.MessageId, + await botClient.SendMessage(chatId: chatId, text: $"Chat ID: {chatId}", + replyParameters: message.MessageId, cancellationToken: _cancellationToken, parseMode: ParseMode.Markdown); return; @@ -324,10 +339,10 @@ namespace MinecraftClient.ChatBots if (Config.Authorized_Chat_Ids.Length > 0 && !Config.Authorized_Chat_Ids.Contains(chatId)) { LogDebugToConsole($"Unauthorized message '{messageText}' received in a chat with with an ID: {chatId} !"); - await botClient.SendTextMessageAsync( + await botClient.SendMessage( chatId: chatId, - replyToMessageId: message.MessageId, text: Translations.bot_TelegramBridge_unauthorized, + replyParameters: message.MessageId, cancellationToken: _cancellationToken, parseMode: ParseMode.Markdown); return; @@ -347,23 +362,22 @@ namespace MinecraftClient.ChatBots if (command.ToLower().Contains("quit") || command.ToLower().Contains("exit")) { - await botClient.SendTextMessageAsync( + await botClient.SendMessage( chatId: chatId, - replyToMessageId: message.MessageId, text: $"{Translations.bot_TelegramBridge_quit_disabled}", + replyParameters: message.MessageId, cancellationToken: _cancellationToken, parseMode: ParseMode.Markdown); - return;; + return; ; } CmdResult result = new(); PerformInternalCommand(command, ref result); - await botClient.SendTextMessageAsync( + await botClient.SendMessage( chatId: chatId, - replyToMessageId: - message.MessageId, text: $"{Translations.bot_TelegramBridge_command_executed}:\n\n{result}", + replyParameters: message.MessageId, cancellationToken: _cancellationToken, parseMode: ParseMode.Markdown); } diff --git a/MinecraftClient/ChatBots/WebSocketBot.cs b/MinecraftClient/ChatBots/WebSocketBot.cs deleted file mode 100644 index c57f8605..00000000 --- a/MinecraftClient/ChatBots/WebSocketBot.cs +++ /dev/null @@ -1,1349 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Net.WebSockets; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using MinecraftClient.CommandHandler; -using MinecraftClient.Inventory; -using MinecraftClient.Mapping; -using MinecraftClient.Scripting; -using Newtonsoft.Json; -using Tomlet.Attributes; - -namespace MinecraftClient.ChatBots; - -internal class SessionEventArgs : EventArgs -{ - public string SessionId { get; } - - public SessionEventArgs(string sessionId) - { - SessionId = sessionId; - } -} - -internal class MessageReceivedEventArgs : EventArgs -{ - public string SessionId { get; } - public string Message { get; } - - public MessageReceivedEventArgs(string sessionId, string message) - { - SessionId = sessionId; - Message = message; - } -} - -internal class WebSocketSession -{ - public string SessionId { get; set; } - public WebSocket WebSocket { get; set; } - - public WebSocketSession(string sessionId, WebSocket webSocket) - { - SessionId = sessionId; - WebSocket = webSocket; - } -} - -internal class WebSocketServer -{ - public readonly ConcurrentDictionary Sessions; - public event EventHandler? NewSession; - public event EventHandler? SessionDropped; - public event EventHandler? MessageReceived; - - private HttpListener? listener; - - public WebSocketServer() - { - Sessions = new ConcurrentDictionary(); - } - - public async Task Start(string ipAddress, int port) - { - listener = new HttpListener(); - listener.Prefixes.Add($"http://{ipAddress}:{port}/"); - listener.Start(); - - while (listener.IsListening) - { - var context = await listener.GetContextAsync(); - if (context.Request.IsWebSocketRequest) - { - var sessionGuid = Guid.NewGuid().ToString(); - var webSocketContext = await context.AcceptWebSocketAsync(null); - var webSocket = webSocketContext.WebSocket; - var webSocketSession = new WebSocketSession(sessionGuid, webSocket); - - NewSession?.Invoke(this, new SessionEventArgs(sessionGuid)); - Sessions.TryAdd(sessionGuid, webSocketSession); - _ = ProcessWebSocketSession(webSocketSession); - } - else - { - context.Response.StatusCode = 400; - context.Response.Close(); - } - } - } - - public async Task Stop() - { - foreach (var session in Sessions) - { - await session.Value.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server shutting down", - CancellationToken.None); - } - - Sessions.Clear(); - listener?.Stop(); - } - - private async Task ProcessWebSocketSession(WebSocketSession webSocketSession) - { - var buffer = new byte[1024]; - - try - { - while (webSocketSession.WebSocket.State == WebSocketState.Open) - { - var receiveResult = - await webSocketSession.WebSocket.ReceiveAsync(new ArraySegment(buffer), - CancellationToken.None); - - if (receiveResult.MessageType == WebSocketMessageType.Text) - { - var message = Encoding.UTF8.GetString(buffer, 0, receiveResult.Count); - MessageReceived?.Invoke(this, new MessageReceivedEventArgs(webSocketSession.SessionId, message)); - } - else if (receiveResult.MessageType == WebSocketMessageType.Close) - { - await webSocketSession.WebSocket.CloseAsync( - WebSocketCloseStatus.NormalClosure, - "Connection closed by the client", - CancellationToken.None); - break; - } - } - } - finally - { - Sessions.TryRemove(webSocketSession.SessionId, out _); - SessionDropped?.Invoke(this, new SessionEventArgs(webSocketSession.SessionId)); - } - } - - public bool RenameSession(string oldSessionId, string newSessionId) - { - if (!Sessions.ContainsKey(oldSessionId) || Sessions.ContainsKey(newSessionId)) - return false; - - if (!Sessions.TryRemove(oldSessionId, out var webSocketSession)) - return false; - - webSocketSession.SessionId = newSessionId; - - if (Sessions.TryAdd(newSessionId, webSocketSession)) - return true; - - webSocketSession.SessionId = oldSessionId; - - if (!Sessions.TryAdd(oldSessionId, webSocketSession)) - throw new Exception("Failed to add back the old session after failed rename"); - - return false; - } - - public async Task SendToSession(string sessionId, string message) - { - try - { - if (Sessions.TryGetValue(sessionId, out var webSocketSession)) - { - var buffer = Encoding.UTF8.GetBytes(message); - await webSocketSession.WebSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, - true, - CancellationToken.None); - } - } - catch (WebSocketException ex) - { - if (ex.InnerException is SocketException { SocketErrorCode: SocketError.ConnectionReset }) - { - if (Sessions.ContainsKey(sessionId)) - Sessions.Remove(sessionId, out _); - } - } - } -} - -internal class WsChatBotCommand -{ - [JsonProperty("command")] public string Command { get; set; } = ""; - - [JsonProperty("requestId")] public string RequestId { get; set; } = ""; - - [JsonProperty("parameters")] public object[]? Parameters { get; set; } -} - -internal class WsCommandResponder -{ - private WebSocketBot _bot; - private string _sessionId; - private string _command; - private string _requestId; - - public WsCommandResponder(WebSocketBot bot, string sessionId, string command, string requestId) - { - _bot = bot; - _sessionId = sessionId; - _command = command; - _requestId = requestId; - } - - private void SendCommandResponse(bool success, string result, bool overrideAuth = false) - { - _bot.SendCommandResponse(_sessionId, success, _requestId, _command, result, overrideAuth); - } - - public void SendErrorResponse(string error, bool overrideAuth = false) - { - SendCommandResponse(false, error, overrideAuth); - } - - public void SendSuccessResponse(string result, bool overrideAuth = false) - { - SendCommandResponse(true, result, overrideAuth); - } - - public void SendSuccessResponse(bool overrideAuth = false) - { - SendSuccessResponse(JsonConvert.SerializeObject(true), overrideAuth); - } - - public string Quote(string text) - { - return $"\"{text}\""; - } -} - -internal class NbtData -{ - public NBT? nbt { get; set; } -} - -internal class NBT -{ - public Dictionary? nbt { get; set; } -} - -internal class NbtDictionaryConverter : JsonConverter> -{ - public override void WriteJson(JsonWriter writer, Dictionary? value, JsonSerializer serializer) - => throw new NotImplementedException(); - - public override Dictionary? ReadJson(JsonReader reader, Type objectType, - Dictionary? existingValue, bool hasExistingValue, JsonSerializer serializer) - { - var keyValuePairs = serializer.Deserialize>>(reader); - return new(keyValuePairs!); - } -} - -public class WebSocketBot : ChatBot -{ - private string? _ip; - private int _port; - private string? _password; - private WebSocketServer? _server; - private List _authenticatedSessions; - private List<(string, string)> _waitingEvents; - - public static Configs Config = new(); - - [TomlDoNotInlineObject] - public class Configs - { - [NonSerialized] private const string BotName = "Websocket"; - - public bool Enabled = false; - - [TomlInlineComment("$ChatBot.WebSocketBot.Ip$")] - public string? Ip = "127.0.0.1"; - - [TomlInlineComment("$ChatBot.WebSocketBot.Port$")] - public int Port = 8043; - - [TomlInlineComment("$ChatBot.WebSocketBot.Password$")] - public string? Password = Guid.NewGuid().ToString().Replace("-", "").Trim().ToLower(); - - [TomlInlineComment("$ChatBot.WebSocketBot.DebugMode$")] - public bool DebugMode = false; - - [TomlInlineComment("$ChatBot.WebSocketBot.AllowIpAlias$")] - public bool AllowIpAlias = false; - } - - public WebSocketBot() - { - _password = Config.Password; - _authenticatedSessions = new(); - _waitingEvents = new(); - - var match = Regex.Match(Config.Ip!, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); - - // If AllowIpAlias is set to true in the config, then always ignore this check - if (!match.Success & !Config.AllowIpAlias!) - { - LogToConsole(Translations.bot_WebSocketBot_failed_to_start_ip); - return; - } - - if (Config.Port > 65535) - { - LogToConsole(string.Format(Translations.bot_WebSocketBot_failed_to_start_port, _port.ToString())); - return; - } - - _ip = Config.Ip; - _port = Config.Port; - } - - public override void Initialize() - { - Task.Run(() => - { - _authenticatedSessions.Clear(); - - if (_server != null) - { - SendEvent("OnWsRestarting", ""); - _server.Stop(); // If you await, this will freeze the task and the websocket won't work - _server = null; - } - - try - { - LogToConsole(Translations.bot_WebSocketBot_starting); - _server = new(); - _server.Start(_ip!, _port); // If you await, this will freeze the task and the websocket won't work - - LogToConsole(string.Format(Translations.bot_WebSocketBot_started, _ip, _port.ToString())); - - foreach (var (eventName, data) in _waitingEvents) - SendEvent(eventName, data); - } - catch (Exception e) - { - LogToConsole(string.Format(Translations.bot_WebSocketBot_failed_to_start_custom, e)); - return; - } - - _server.NewSession += (_, session) => - LogToConsole(string.Format(Translations.bot_WebSocketBot_new_session, session.SessionId)); - _server.SessionDropped += (_, session) => - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_disconnected, session.SessionId)); - - _server.MessageReceived += (_, messageObject) => - { - if (!ProcessWebsocketCommand(messageObject.SessionId, _password!, messageObject.Message)) - return; - - var command = messageObject.Message; - command = command.StartsWith('/') ? command[1..] : $"send {command}"; - - CmdResult response = new(); - PerformInternalCommand(command, ref response); - SendSessionEvent(messageObject.SessionId, "OnMccCommandResponse", $"{{\"response\": \"{response}\"}}"); - }; - }); - } - - private bool ProcessWebsocketCommand(string sessionId, string password, string message) - { - message = message.Trim(); - - if (string.IsNullOrEmpty(message)) - return false; - - if (message.StartsWith('{')) - { - try - { - if (Config.DebugMode) - LogDebugToConsole($"\n\n\tGot command\n\n\t{message}\n\n"); - - var cmd = JsonConvert.DeserializeObject(message)!; - var responder = new WsCommandResponder(this, sessionId, cmd.Command, cmd.RequestId); - - // Allow session name changing without authenticating for easier identification - if (cmd.Command.Equals("ChangeSessionId", StringComparison.OrdinalIgnoreCase)) - { - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expected 1 (newSessionid)!"), true); - return false; - } - - var newId = (cmd.Parameters[0] as string)!; - - switch (newId.Length) - { - case 0: - responder.SendErrorResponse(responder.Quote("Please provide a valid session ID!"), - true); - return false; - case > 32: - responder.SendErrorResponse( - responder.Quote("The session ID can't be longer than 32 characters!"), true); - return false; - } - - if (!_server!.RenameSession(sessionId, newId)) - { - responder.SendErrorResponse( - responder.Quote("Failed to change the session id to: '" + newId + "'"), - true); - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_id_failed_to_change, sessionId, - newId)); - return false; - } - - // If the session is authenticated, remove the old session id and add the new one - if (_authenticatedSessions.Contains(sessionId)) - { - _authenticatedSessions.Remove(sessionId); - _authenticatedSessions.Add(newId); - } - - // Update the responder to the new session id - responder = new WsCommandResponder(this, newId, cmd.Command, cmd.RequestId); - - responder.SendSuccessResponse( - responder.Quote("The session ID was successfully changed to: '" + newId + "'"), true); - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_id_changed, sessionId, newId)); - return false; - } - - // Authentication and session commands - if (password.Length != 0) - { - if (!_authenticatedSessions.Contains(sessionId)) - { - // Special case for authentication - if (cmd.Command.Equals("Authenticate", StringComparison.OrdinalIgnoreCase)) - { - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expected 1 (password)!"), true); - return false; - } - - var pass = (cmd.Parameters[0] as string)!; - - if (pass.Length == 0) - { - responder.SendErrorResponse( - responder.Quote( - "Please provide a valid password! (Example: 'Authenticate password123')"), - true); - return false; - } - - if (!pass.Equals(password)) - { - responder.SendErrorResponse(responder.Quote("Incorrect password provided!"), true); - return false; - } - - _authenticatedSessions.Add(sessionId); - responder.SendSuccessResponse(responder.Quote("Successfully authenticated!"), true); - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_authenticated, sessionId)); - return false; - } - - responder.SendErrorResponse( - responder.Quote("You must authenticate in order to send and receive data!"), true); - return false; - } - } - else - { - if (!_authenticatedSessions.Contains(sessionId)) - { - responder.SendSuccessResponse(responder.Quote("Successfully authenticated!")); - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_authenticated, sessionId)); - _authenticatedSessions.Add(sessionId); - return false; - } - } - - // Process other commands - switch (cmd.Command) - { - case "LogToConsole": - if (cmd.Parameters == null || cmd.Parameters.Length > 1 || cmd.Parameters.Length < 1) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting a single parameter!")); - return false; - } - - LogToConsole((cmd.Parameters[0] as string)!); - responder.SendSuccessResponse(); - break; - - case "LogDebugToConsole": - if (cmd.Parameters == null || cmd.Parameters.Length > 1 || cmd.Parameters.Length < 1) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting a single parameter!")); - return false; - } - - LogDebugToConsole((cmd.Parameters[0] as string)!); - responder.SendSuccessResponse(); - break; - - case "LogToConsoleTranslated": - if (cmd.Parameters == null || cmd.Parameters.Length > 1 || cmd.Parameters.Length < 1) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting a single parameter!")); - return false; - } - - LogToConsoleTranslated((cmd.Parameters[0] as string)!); - responder.SendSuccessResponse(); - break; - - case "LogDebugToConsoleTranslated": - if (cmd.Parameters!.Length > 1 || cmd.Parameters.Length < 1) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting a single parameter!")); - return false; - } - - LogDebugToConsoleTranslated((cmd.Parameters[0] as string)!); - responder.SendSuccessResponse(); - break; - - case "ReconnectToTheServer": - if (cmd.Parameters is not { Length: 2 }) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 2 parameters (extraAttempts, delaySeconds)!")); - return false; - } - - ReconnectToTheServer(Convert.ToInt32(cmd.Parameters[0]), Convert.ToInt32(cmd.Parameters[1])); - responder.SendSuccessResponse(); - break; - - case "DisconnectAndExit": - responder.SendSuccessResponse(); - DisconnectAndExit(); - break; - - case "SendPrivateMessage": - if (cmd.Parameters is not { Length: 2 }) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 2 parameters (player, message)!")); - return false; - } - - SendPrivateMessage((cmd.Parameters[0] as string)!, (cmd.Parameters[1] as string)!); - responder.SendSuccessResponse(); - break; - - case "RunScript": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (filename)!")); - return false; - } - - RunScript((cmd.Parameters[0] as string)!); - responder.SendSuccessResponse(); - break; - - case "GetTerrainEnabled": - responder.SendSuccessResponse(GetTerrainEnabled().ToString().ToLower()); - break; - - case "SetTerrainEnabled": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (enabled)!")); - return false; - } - - SetTerrainEnabled((bool)cmd.Parameters[0]); - responder.SendSuccessResponse(); - break; - - case "GetEntityHandlingEnabled": - responder.SendSuccessResponse(GetEntityHandlingEnabled().ToString().ToLower()); - break; - - case "Sneak": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (on)!")); - return false; - } - - Sneak((bool)cmd.Parameters[0]); - responder.SendSuccessResponse(); - break; - - case "SendEntityAction": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (actionType)!")); - return false; - } - - SendEntityAction(((Protocol.EntityActionType)(Convert.ToInt32(cmd.Parameters[0])))); - responder.SendSuccessResponse(); - break; - - case "DigBlock": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 3 || - cmd.Parameters.Length > 5) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 1 or 3 parameter(s) (location, swingArms?, lookAtBlock?)!")); - return false; - } - - var location = new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), Convert.ToInt32(cmd.Parameters[2])); - - if (location.DistanceSquared(GetCurrentLocation().EyesLocation()) > 25) - { - responder.SendErrorResponse( - responder.Quote("The block you're trying to dig is too far away!")); - return false; - } - - if (GetWorld().GetBlock(location).Type == Material.Air) - { - responder.SendErrorResponse(responder.Quote("The block you're trying to dig is is air!")); - return false; - } - - var result = cmd.Parameters.Length switch - { - 3 => DigBlock(location), - 4 => DigBlock(location, (bool)cmd.Parameters[3]), - 5 => DigBlock(location, (bool)cmd.Parameters[3], (bool)cmd.Parameters[4]), - _ => false - }; - - responder.SendSuccessResponse(JsonConvert.SerializeObject(result)); - break; - - case "SetSlot": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (slotNumber)!")); - return false; - } - - SetSlot(Convert.ToInt32(cmd.Parameters[0])); - responder.SendSuccessResponse(); - break; - - case "GetWorld": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetWorld())); - break; - - case "GetEntities": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetEntities())); - break; - - case "GetPlayersLatency": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetPlayersLatency())); - break; - - case "GetCurrentLocation": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetCurrentLocation())); - break; - - case "MoveToLocation": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 3 || - cmd.Parameters.Length > 8) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 1 or 7 parameter(s) (x, y, z, allowUnsafe?, allowDirectTeleport?, maxOffset?, minoffset?, timeout?)!")); - return false; - } - - var allowUnsafe = false; - var allowDirectTeleport = false; - var maxOffset = 0; - var minOffset = 0; - TimeSpan? timeout = null; - - if (cmd.Parameters.Length >= 4) - allowUnsafe = (bool)cmd.Parameters[3]; - - if (cmd.Parameters.Length >= 5) - allowDirectTeleport = (bool)cmd.Parameters[4]; - - if (cmd.Parameters.Length >= 6) - maxOffset = Convert.ToInt32(cmd.Parameters[5]); - - if (cmd.Parameters.Length >= 7) - minOffset = Convert.ToInt32(cmd.Parameters[6]); - - if (cmd.Parameters.Length == 8) - timeout = TimeSpan.FromSeconds(Convert.ToInt32(cmd.Parameters[7])); - - var canMove = MoveToLocation( - new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), - Convert.ToInt32(cmd.Parameters[2])), - allowUnsafe, - allowDirectTeleport, - maxOffset, - minOffset, - timeout); - - responder.SendSuccessResponse(JsonConvert.SerializeObject(canMove)); - break; - - case "ClientIsMoving": - responder.SendSuccessResponse(JsonConvert.SerializeObject(ClientIsMoving())); - break; - - case "LookAtLocation": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 3 || - cmd.Parameters.Length > 3) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 3 parameter(s) (x, y, z)!")); - return false; - } - - LookAtLocation(new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), Convert.ToInt32(cmd.Parameters[2]))); - responder.SendSuccessResponse(); - break; - - case "GetTimestamp": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetTimestamp())); - break; - - case "GetServerPort": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetServerPort())); - break; - - case "GetServerHost": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetServerHost())); - break; - - case "GetUsername": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetUsername())); - break; - - case "GetGamemode": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GameModeString(GetGamemode()))); - break; - - case "GetYaw": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetYaw())); - break; - - case "GetPitch": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetPitch())); - break; - - case "GetUserUUID": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetUserUUID())); - break; - - case "GetOnlinePlayers": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetOnlinePlayers())); - break; - - case "GetOnlinePlayersWithUUID": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetOnlinePlayersWithUUID())); - break; - - case "GetServerTPS": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetServerTPS())); - break; - - case "InteractEntity": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 2 || - cmd.Parameters.Length > 3) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting at least 2 and at most 3 parameter(s) (entityId, interactionType, hand?)!")); - return false; - } - - var interactionType = (InteractType)Convert.ToInt32(cmd.Parameters[1]); - var interactionHand = Hand.MainHand; - - if (cmd.Parameters.Length == 3) - interactionHand = (Hand)Convert.ToInt32(cmd.Parameters[2]); - - responder.SendSuccessResponse(JsonConvert.SerializeObject( - InteractEntity(Convert.ToInt32(cmd.Parameters[0]), interactionType, interactionHand))); - break; - - case "CreativeGive": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 3 || - cmd.Parameters.Length > 4) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting at least 3 and at most 4 parameter(s) (slotId, itemType, count, nbt?)!")); - return false; - } - - NBT? nbt = null; - - if (cmd.Parameters.Length == 4) - nbt = JsonConvert.DeserializeObject(cmd.Parameters[3].ToString()!, - new NbtDictionaryConverter())!; - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(CreativeGive( - Convert.ToInt32(cmd.Parameters[0]), - (ItemType)Convert.ToInt32(cmd.Parameters[1]), - Convert.ToInt32(cmd.Parameters[2]), - nbt == null ? new Dictionary() : nbt!.nbt!) - )); - - break; - - case "CreativeDelete": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting at 1 parameter (slotId)!")); - return false; - } - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(CreativeDelete(Convert.ToInt32(cmd.Parameters[0])))); - break; - - case "SendAnimation": - var hand = Hand.MainHand; - - if (cmd.Parameters is { Length: 1 }) - hand = (Hand)Convert.ToInt32(cmd.Parameters[0]); - - responder.SendSuccessResponse(JsonConvert.SerializeObject(SendAnimation(hand))); - break; - - case "SendPlaceBlock": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length < 4 || - cmd.Parameters.Length > 4) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting at least 4 and at most 5 parameters (x, y, z, blockFace, hand?)!")); - return false; - } - - var blockLocation = new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), Convert.ToInt32(cmd.Parameters[2])); - var blockFacingDirection = (Direction)Convert.ToInt32(cmd.Parameters[3]); - var handToUse = Hand.MainHand; - - if (cmd.Parameters.Length == 4) - handToUse = (Hand)Convert.ToInt32(cmd.Parameters[4]); - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(SendPlaceBlock(blockLocation, blockFacingDirection, - handToUse))); - break; - - case "UseItemInHand": - responder.SendSuccessResponse(JsonConvert.SerializeObject(UseItemInHand())); - break; - - case "GetInventoryEnabled": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetInventoryEnabled())); - break; - - case "GetPlayerInventory": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetPlayerInventory())); - break; - - case "GetInventories": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetInventories())); - break; - - case "WindowAction": - if (cmd.Parameters == null || cmd.Parameters.Length == 0 || cmd.Parameters.Length != 3) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 3 parameters (inventoryId, slotId, windowActionType)!")); - return false; - } - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(WindowAction( - Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), - (WindowActionType)Convert.ToInt32(cmd.Parameters[2]) - ))); - break; - - case "ChangeSlot": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (slotId)!")); - return false; - } - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(ChangeSlot((short)Convert.ToInt32(cmd.Parameters[0])))); - break; - - case "GetCurrentSlot": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetCurrentSlot())); - break; - - case "ClearInventories": - responder.SendSuccessResponse(JsonConvert.SerializeObject(ClearInventories())); - break; - - case "UpdateSign": - if (cmd.Parameters is not { Length: 7 }) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 1 parameter (x, y, z, line1, line2, line3, line4)!")); - return false; - } - - var signLocation = new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), Convert.ToInt32(cmd.Parameters[2])); - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(UpdateSign(signLocation, - (string)cmd.Parameters[3], - (string)cmd.Parameters[4], - (string)cmd.Parameters[5], - (string)cmd.Parameters[6] - ))); - break; - - case "SelectTrade": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (selectedSlot)!")); - return false; - } - - responder.SendSuccessResponse( - JsonConvert.SerializeObject(SelectTrade(Convert.ToInt32(cmd.Parameters[0])))); - break; - - case "UpdateCommandBlock": - if (cmd.Parameters is not { Length: 6 }) - { - responder.SendErrorResponse(responder.Quote( - "Invalid number of parameters, expecting 1 parameter (x, y, z, command, commandBlockMode, commandBlockFlags)!")); - return false; - } - - var commandBlockLocation = new Location(Convert.ToInt32(cmd.Parameters[0]), - Convert.ToInt32(cmd.Parameters[1]), Convert.ToInt32(cmd.Parameters[2])); - - responder.SendSuccessResponse( - UpdateCommandBlock(commandBlockLocation, - (string)cmd.Parameters[3], - (CommandBlockMode)Convert.ToInt32(cmd.Parameters[4]), - (CommandBlockFlags)Convert.ToInt32(cmd.Parameters[5]) - ).ToString().ToLower()); - break; - - case "CloseInventory": - if (cmd.Parameters is not { Length: 1 }) - { - responder.SendErrorResponse( - responder.Quote("Invalid number of parameters, expecting 1 parameter (inventoryId)!")); - return false; - } - - responder.SendSuccessResponse(CloseInventory(Convert.ToInt32(cmd.Parameters[0])).ToString() - .ToLower()); - break; - - case "GetMaxChatMessageLength": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetMaxChatMessageLength())); - break; - - case "Respawn": - responder.SendSuccessResponse(JsonConvert.SerializeObject(Respawn())); - break; - - case "GetProtocolVersion": - responder.SendSuccessResponse(JsonConvert.SerializeObject(GetProtocolVersion())); - break; - - default: - responder.SendErrorResponse( - responder.Quote($"Unknown command {cmd.Command} received!")); - break; - } - } - catch (Exception e) - { - LogDebugToConsole(e.Message); - SendSessionEvent(sessionId, "OnWsCommandResponse", - "{\"success\": false, \"message\": \"An error occured, possible reasons: mail-formed json, type conversion, internal error\", \"stackTrace\": \"" + - Json.EscapeString(e.ToString()) + "\"}", true); - return false; - } - - return false; - } - - if (password.Length != 0) - { - if (!_authenticatedSessions.Contains(sessionId)) - { - SendSessionEvent(sessionId, "OnWsCommandResponse", - "{\"error\": true, \"message\": \"You must authenticate in order to send and receive data!\"}", - true); - return false; - } - } - else - { - if (!_authenticatedSessions.Contains(sessionId)) - { - SendSessionEvent(sessionId, "OnWsCommandResponse", - "{\"success\": true, \"message\": \"Successfully authenticated!\"}", true); - LogToConsole(string.Format(Translations.bot_WebSocketBot_session_authenticated, sessionId)); - _authenticatedSessions.Add(sessionId); - } - } - - return true; - } - - public override void OnUnload() - { - if (_server != null) - { - SendEvent("OnWsConnectionClose", ""); - _server.Stop(); - _server = null; - } - - _authenticatedSessions.Clear(); - } - - // ========================================================================================== - // Bot Events - // ========================================================================================== - public override void AfterGameJoined() - { - // Workaround to wait until the WebSocket server has been started - // This would fire before the WS server is started, this causing a null exception. - _waitingEvents.Add(("OnGameJoined", "")); - } - - public override void OnBlockBreakAnimation(Entity entity, Location location, byte stage) - { - SendEvent("OnBlockBreakAnimation", new { entity, location, stage }); - } - - public override void OnEntityAnimation(Entity entity, byte animation) - { - SendEvent("OnEntityAnimation", new { entity, animation }); - } - - public override void GetText(string text) - { - text = GetVerbatim(text).Trim(); - - var message = ""; - var username = ""; - - if (IsPrivateMessage(text, ref message, ref username)) - SendEvent("OnChatPrivate", new { sender = username, message, rawText = text }); - else if (IsChatMessage(text, ref message, ref username)) - SendEvent("OnChatPublic", new { username, message, rawText = text }); - else if (IsTeleportRequest(text, ref username)) - SendEvent("OnTeleportRequest", new { sender = username, rawText = text }); - } - - public override void GetText(string text, string? json) - { - SendEvent("OnChatRaw", new { text, json }); - } - - public override bool OnDisconnect(DisconnectReason reason, string message) - { - var reasonString = reason switch - { - DisconnectReason.ConnectionLost => "Connection Lost", - DisconnectReason.UserLogout => "User Logout", - DisconnectReason.InGameKick => "In-Game Kick", - DisconnectReason.LoginRejected => "Login Rejected", - _ => "Unknown" - }; - - SendEvent("OnDisconnect", new { reason = reasonString, message }); - return false; - } - - public override void OnPlayerProperty(Dictionary prop) - { - SendEvent("OnPlayerProperty", prop); - } - - public override void OnServerTpsUpdate(double tps) - { - SendEvent("OnServerTpsUpdate", new { tps }); - } - - public override void OnTimeUpdate(long worldAge, long timeOfDay) - { - SendEvent("OnTimeUpdate", new { worldAge, timeOfDay }); - } - - public override void OnEntityMove(Entity entity) - { - SendEvent("OnEntityMove", entity); - } - - public override void OnInternalCommand(string commandName, string commandParams, CmdResult result) - { - SendEvent("OnInternalCommand", - new { command = commandName, parameters = commandParams, result = result.ToString().Replace("\"", "'") }); - } - - public override void OnEntitySpawn(Entity entity) - { - SendEvent("OnEntitySpawn", entity); - } - - public override void OnEntityDespawn(Entity entity) - { - SendEvent("OnEntityDespawn", entity); - } - - public override void OnHeldItemChange(byte slot) - { - SendEvent("OnHeldItemChange", new { itemSlot = slot }); - } - - public override void OnHealthUpdate(float health, int food) - { - SendEvent("OnHealthUpdate", new { health, food }); - } - - public override void OnExplosion(Location explode, float strength, int recordCount) - { - SendEvent("OnExplosion", new { location = explode, strength, recordCount }); - } - - public override void OnSetExperience(float experienceBar, int level, int totalExperience) - { - SendEvent("OnSetExperience", - new { experienceBar, level, totalExperience }); - } - - public override void OnGamemodeUpdate(string playerName, Guid uuid, int gameMode) - { - SendEvent("OnGamemodeUpdate", new { playerName, uuid, gameMode = GameModeString(gameMode) }); - } - - public override void OnLatencyUpdate(string playerName, Guid uuid, int latency) - { - SendEvent("OnLatencyUpdate", new { playerName, uuid, latency }); - } - - public override void OnMapData(int mapId, byte scale, bool trackingPosition, bool locked, List icons, - byte columnsUpdated, byte rowsUpdated, byte mapColumnX, byte mapRowZ, byte[]? colors) - { - SendEvent("OnMapData", - new - { - mapId, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapColumnX, mapRowZ, - colors - }); - } - - public override void OnTradeList(int windowId, List trades, VillagerInfo villagerInfo) - { - SendEvent("OnTradeList", new { windowId, trades, villagerInfo }); - } - - public override void OnTitle(int action, string titleText, string subtitleText, string actionBarText, int fadein, - int stay, int fadeout, string json_) - { - SendEvent("OnTitle", - new - { - action, titleText, subtitleText, actionBarText, - fadeIn = fadein, stay, rawJson = json_ - }); - } - - public override void OnEntityEquipment(Entity entity, int slot, Item? item) - { - SendEvent("OnEntityEquipment", new { entity, slot, item }); - } - - public override void OnEntityEffect(Entity entity, Effects effect, int amplifier, int duration, byte flags) - { - SendEvent("OnEntityEffect", new { entity, effect, amplifier, duration, flags }); - } - - public override void OnScoreboardObjective(string objectiveName, byte mode, string objectiveValue, int type, - string json_, int numberFormat) - { - SendEvent("OnScoreboardObjective", - new { objectiveName, mode, objectiveValue, type, rawJson = json_, numberFormat }); - } - - public override void OnUpdateScore(string entityName, int action, string objectiveName, string objectiveDisplayName, int value, int numberFormat) - { - SendEvent("OnUpdateScore", - new { entityName, action, objectiveName, objectiveDisplayName, type = value, numberFormat }); - } - - public override void OnInventoryUpdate(int inventoryId) - { - SendEvent("OnInventoryUpdate", new { inventoryId }); - } - - public override void OnInventoryOpen(int inventoryId) - { - SendEvent("OnInventoryOpen", new { inventoryId }); - } - - public override void OnInventoryClose(int inventoryId) - { - SendEvent("OnInventoryClose", new { inventoryId }); - } - - public override void OnPlayerJoin(Guid uuid, string name) - { - SendEvent("OnPlayerJoin", new { uuid, name }); - } - - public override void OnPlayerLeave(Guid uuid, string? name) - { - SendEvent("OnPlayerLeave", new { uuid, name = name ?? "null" }); - } - - public override void OnDeath() - { - SendEvent("OnDeath", ""); - } - - public override void OnRespawn() - { - SendEvent("OnRespawn", ""); - } - - public override void OnEntityHealth(Entity entity, float health) - { - SendEvent("OnEntityHealth", new { entity, health }); - } - - public override void OnEntityMetadata(Entity entity, Dictionary? metadata) - { - SendEvent("OnEntityMetadata", new { entity, metadata }); - } - - public override void OnPlayerStatus(byte statusId) - { - SendEvent("OnPlayerStatus", new { statusId }); - } - - public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) - { - SendEvent("OnNetworkPacket", new { packetId = packetID, isLogin, isInbound, packetData }); - } - - // ========================================================================================== - // Helper methods - // ========================================================================================== - - private void SendEvent(string type, object data, bool overrideAuth = false) - { - if (_server == null) - return; - - foreach (var (sessionId, _) in _server!.Sessions) - SendSessionEvent(sessionId, type, JsonConvert.SerializeObject(data), overrideAuth); - } - - private void SendEvent(string type, string data, bool overrideAuth = false) - { - if (_server == null) - return; - - foreach (var (sessionId, _) in _server.Sessions) - SendSessionEvent(sessionId, type, data, overrideAuth); - } - - private void SendSessionEvent(string sessionId, string type, string data, bool overrideAuth = false) - { - if (sessionId.Length > 0 && (overrideAuth || _authenticatedSessions.Contains(sessionId))) - { - _server?.SendToSession(sessionId, - $"{{\"event\": \"{type}\", \"data\": {(string.IsNullOrEmpty(data) ? "null" : $"\"{Json.EscapeString(data)}\"")}}}") - .Wait(); - - if (!(type.Contains("Entity") || type.Equals("OnTimeUpdate") || type.Equals("OnServerTpsUpdate")) && - Config.DebugMode) - LogDebugToConsole( - $"\n\n\tSending:\n\n\t{{\"event\": \"{type}\", \"data\": {(string.IsNullOrEmpty(data) - ? "null" - : $"\"{Json.EscapeString(data)}\"")}}}\n\n"); - } - } - - public void SendCommandResponse(string sessionId, bool success, string requestId, string command, - string result, bool overrideAuth = false) - { - SendSessionEvent(sessionId, "OnWsCommandResponse", - $"{{\"success\": {success.ToString().ToLower()}, \"requestId\": \"{requestId}\", \"command\": \"{command}\", \"result\": {(string.IsNullOrEmpty(result) ? "null" : result)}}}", - overrideAuth); - } - - private static string GameModeString(int gameMode) - { - return gameMode switch - { - 0 => "survival", - 1 => "creative", - 2 => "adventure", - 3 => "spectator", - _ => "unknown" - }; - } -} \ No newline at end of file diff --git a/MinecraftClient/ClassicConsoleBackend.cs b/MinecraftClient/ClassicConsoleBackend.cs new file mode 100644 index 00000000..38411413 --- /dev/null +++ b/MinecraftClient/ClassicConsoleBackend.cs @@ -0,0 +1,235 @@ +using System; +using System.Text.RegularExpressions; + +namespace MinecraftClient +{ + /// + /// Console backend wrapping the ConsoleInteractive library (existing behavior). + /// + public partial class ClassicConsoleBackend : IConsoleBackend + { + private static readonly (byte R, byte G, byte B, char Code)[] McStandardColors = + [ + (0, 0, 0, '0'), // black + (0, 0, 170, '1'), // dark_blue + (0, 170, 0, '2'), // dark_green + (0, 170, 170, '3'), // dark_aqua + (170, 0, 0, '4'), // dark_red + (170, 0, 170, '5'), // dark_purple + (255, 170, 0, '6'), // gold + (170, 170, 170, '7'), // gray + (85, 85, 85, '8'), // dark_gray + (85, 85, 255, '9'), // blue + (85, 255, 85, 'a'), // green + (85, 255, 255, 'b'), // aqua + (255, 85, 85, 'c'), // red + (255, 85, 255, 'd'), // light_purple + (255, 255, 85, 'e'), // yellow + (255, 255, 255, 'f'), // white + ]; + + [GeneratedRegex("§#([0-9a-fA-F]{6})")] + private static partial Regex HexColorRegex(); + + private static char NearestMcColor(byte r, byte g, byte b) + { + int bestIdx = 0; + long bestDist = long.MaxValue; + + for (int i = 0; i < McStandardColors.Length; i++) + { + var (sr, sg, sb, _) = McStandardColors[i]; + long dr = r - sr; + long dg = g - sg; + long db = b - sb; + long dist = dr * dr + dg * dg + db * db; + if (dist < bestDist) + { + bestDist = dist; + bestIdx = i; + } + } + + return McStandardColors[bestIdx].Code; + } + + private static string ResolveHexColors(string text) + { + if (string.IsNullOrEmpty(text) || !text.Contains("§#", StringComparison.Ordinal)) + return text; + + return HexColorRegex().Replace(text, match => + { + ReadOnlySpan hex = match.Groups[1].ValueSpan; + byte r = (byte)((HexVal(hex[0]) << 4) | HexVal(hex[1])); + byte g = (byte)((HexVal(hex[2]) << 4) | HexVal(hex[3])); + byte b = (byte)((HexVal(hex[4]) << 4) | HexVal(hex[5])); + return ColorHelper.GetColorEscapeCode(r, g, b, foreground: true); + }); + } + + private static int HexVal(char c) => c switch + { + >= '0' and <= '9' => c - '0', + >= 'a' and <= 'f' => c - 'a' + 10, + >= 'A' and <= 'F' => c - 'A' + 10, + _ => 0 + }; + + public event EventHandler? MessageReceived; + public event EventHandler? OnInputChange; + + public bool DisplayUserInput + { + get => ConsoleInteractive.ConsoleReader.DisplayUesrInput; + set => ConsoleInteractive.ConsoleReader.DisplayUesrInput = value; + } + + public void Init() + { + ConsoleInteractive.ConsoleWriter.Init(); + } + + public void WriteLine(string text) + { + ConsoleInteractive.ConsoleWriter.WriteLine(text); + } + + public void WriteLineFormatted(string text) + { + ConsoleInteractive.ConsoleWriter.WriteLineFormatted(ResolveHexColors(text)); + } + + public void BeginReadThread() + { + ConsoleInteractive.ConsoleReader.MessageReceived += ForwardMessage; + ConsoleInteractive.ConsoleReader.OnInputChange += ForwardInputChange; + ConsoleInteractive.ConsoleReader.BeginReadThread(); + } + + public void StopReadThread() + { + ConsoleInteractive.ConsoleReader.StopReadThread(); + ConsoleInteractive.ConsoleReader.MessageReceived -= ForwardMessage; + ConsoleInteractive.ConsoleReader.OnInputChange -= ForwardInputChange; + } + + public string RequestImmediateInput() + { + return ConsoleInteractive.ConsoleReader.RequestImmediateInput(); + } + + public string? ReadPassword() + { + ConsoleInteractive.ConsoleReader.SetInputVisible(false); + var input = ConsoleInteractive.ConsoleReader.RequestImmediateInput(); + ConsoleInteractive.ConsoleReader.SetInputVisible(true); + return input; + } + + public void ClearInputBuffer() + { + ConsoleInteractive.ConsoleReader.ClearBuffer(); + } + + public void ClearScreen() + { + Console.Clear(); + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + } + + public void SetInputVisible(bool visible) + { + ConsoleInteractive.ConsoleReader.SetInputVisible(visible); + } + + public void SetBackreadBufferLimit(int limit) + { + ConsoleInteractive.ConsoleBuffer.SetBackreadBufferLimit(limit); + } + + public void Shutdown() + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + } + + #region Suggestion forwarding for classic mode + + public void UpdateSuggestions( + ConsoleInteractive.ConsoleSuggestion.Suggestion[] suggestions, + Tuple range) + { + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(suggestions, range); + } + + public void ClearSuggestions() + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + } + + public void SetSuggestionColors( + string textColor, string textBgColor, + string hlTextColor, string hlTextBgColor, + string tooltipColor, string hlTooltipColor, + string arrowColor) + { + ConsoleInteractive.ConsoleSuggestion.SetColors( + textColor, textBgColor, + hlTextColor, hlTextBgColor, + tooltipColor, hlTooltipColor, + arrowColor); + } + + public bool EnableSuggestionColor + { + get => ConsoleInteractive.ConsoleSuggestion.EnableColor; + set => ConsoleInteractive.ConsoleSuggestion.EnableColor = value; + } + + public bool Enable24bitColor + { + get => ConsoleInteractive.ConsoleSuggestion.Enable24bitColor; + set => ConsoleInteractive.ConsoleSuggestion.Enable24bitColor = value; + } + + public bool UseBasicArrow + { + get => ConsoleInteractive.ConsoleSuggestion.UseBasicArrow; + set => ConsoleInteractive.ConsoleSuggestion.UseBasicArrow = value; + } + + public int SetMaxSuggestionLength(int length) + { + return ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionLength(length); + } + + public int SetMaxSuggestionCount(int count) + { + return ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionCount(count); + } + + public bool EnableWriterColor + { + get => ConsoleInteractive.ConsoleWriter.EnableColor; + set => ConsoleInteractive.ConsoleWriter.EnableColor = value; + } + + public bool UseVT100ColorCode + { + get => ConsoleInteractive.ConsoleWriter.UseVT100ColorCode; + set => ConsoleInteractive.ConsoleWriter.UseVT100ColorCode = value; + } + + #endregion + + private void ForwardMessage(object? sender, string e) + { + MessageReceived?.Invoke(sender, e); + } + + private void ForwardInputChange(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer) + { + OnInputChange?.Invoke(sender, new ConsoleInputBuffer(buffer.Text, buffer.CursorPosition)); + } + } +} diff --git a/MinecraftClient/ColorHelper.cs b/MinecraftClient/ColorHelper.cs index fe91a780..4c7880aa 100644 --- a/MinecraftClient/ColorHelper.cs +++ b/MinecraftClient/ColorHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig; namespace MinecraftClient @@ -100,9 +100,9 @@ namespace MinecraftClient } } if (foreground) - return $"§{best_idx:X}"; + return $"§{best_idx:x}"; else - return $"§§{best_idx:X}"; + return $"§§{best_idx:x}"; } case ConsoleColorModeType.vt100_4bit: @@ -163,7 +163,7 @@ namespace MinecraftClient } } - public class ColorRGBA + public record struct ColorRGBA { public byte R { get; set; } public byte G { get; set; } diff --git a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs index 1f49da98..b17bdf1c 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs @@ -17,7 +17,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null) + if (client is not null) { var botList = client.GetLoadedChatBots(); foreach (var bot in botList) diff --git a/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs index ebf05813..a71349b6 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs @@ -18,10 +18,10 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null) + if (client is not null) { Inventory.Container? inventory = client.GetInventory(0); - if (inventory != null) + if (inventory is not null) { for (int i = 1; i <= 9; ++i) { diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs index 15164956..d485f82a 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs @@ -18,7 +18,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null) + if (client is not null) { var invList = client.GetInventories(); foreach (var inv in invList) diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs index 2536aa1e..b9e01942 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs @@ -19,7 +19,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null && context.Nodes.Count >= 2) + if (client is not null && context.Nodes.Count >= 2) { string invName = context.Nodes[1].Range.Get(builder.Input); if (!int.TryParse(invName, out int invId)) @@ -33,11 +33,11 @@ namespace MinecraftClient.CommandHandler.ArgumentType }; Inventory.Container? inventory = client.GetInventory(invId); - if (inventory != null) + if (inventory is not null) { foreach ((int slot, Inventory.Item item) in inventory.Items) { - if (item != null && item.Count > 0) + if (item is not null && item.Count > 0) { string slotStr = slot.ToString(); if (slotStr.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) diff --git a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs index f2ef9b0a..fbd8a4dc 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs @@ -51,7 +51,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType string[] args = builder.Remaining.Split(' ', StringSplitOptions.TrimEntries); if (args.Length == 0 || (args.Length == 1 && string.IsNullOrWhiteSpace(args[0]))) { - if (client != null) + if (client is not null) { Location current = client.GetCurrentLocation(); builder.Suggest(string.Format("{0:0.00}", current.X)); @@ -68,7 +68,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType else if (args.Length == 1 || (args.Length == 2 && string.IsNullOrWhiteSpace(args[1]))) { string add = args.Length == 1 ? " " : string.Empty; - if (client != null) + if (client is not null) { Location current = client.GetCurrentLocation(); builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Y, add)); @@ -83,7 +83,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType else if (args.Length == 2 || (args.Length == 3 && string.IsNullOrWhiteSpace(args[2]))) { string add = args.Length == 2 ? " " : string.Empty; - if (client != null) + if (client is not null) { Location current = client.GetCurrentLocation(); builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Z, add)); diff --git a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs index bd1ffee6..7b8b9b5d 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs @@ -19,10 +19,10 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null) + if (client is not null) { var bot = (Map?)client.GetLoadedChatBots().Find(bot => bot.GetType().Name == "Map"); - if (bot != null) + if (bot is not null) { var mapList = bot.cachedMaps; foreach (var map in mapList) diff --git a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs index b5092251..4a622924 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs @@ -19,7 +19,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { McClient? client = CmdResult.currentHandler; - if (client != null) + if (client is not null) { var entityList = client.GetEntities().Values.ToList(); foreach (var entity in entityList) diff --git a/MinecraftClient/CommandHandler/CmdResult.cs b/MinecraftClient/CommandHandler/CmdResult.cs index 8ecafc8a..9c4cd840 100644 --- a/MinecraftClient/CommandHandler/CmdResult.cs +++ b/MinecraftClient/CommandHandler/CmdResult.cs @@ -87,7 +87,7 @@ namespace MinecraftClient.CommandHandler public override string ToString() { - if (result != null) + if (result is not null) return result; else return status.ToString(); diff --git a/MinecraftClient/CommandHandler/SuggestionTooltip.cs b/MinecraftClient/CommandHandler/SuggestionTooltip.cs index c235f061..330c63ed 100644 --- a/MinecraftClient/CommandHandler/SuggestionTooltip.cs +++ b/MinecraftClient/CommandHandler/SuggestionTooltip.cs @@ -2,13 +2,8 @@ namespace MinecraftClient.CommandHandler { - internal class SuggestionTooltip : IMessage + internal class SuggestionTooltip(string tooltip) : IMessage { - public SuggestionTooltip(string tooltip) - { - String = tooltip; - } - - public string String { get; set; } + public string String { get; set; } = tooltip; } } diff --git a/MinecraftClient/Commands/AchievementCommand.cs b/MinecraftClient/Commands/AchievementCommand.cs new file mode 100644 index 00000000..ee99c4d7 --- /dev/null +++ b/MinecraftClient/Commands/AchievementCommand.cs @@ -0,0 +1,106 @@ +using System.Linq; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + public class AchievementCommand : Command + { + public override string CmdName => "achievement"; + public override string CmdUsage => "achievement "; + public override string CmdDesc => Translations.cmd_achievement_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("locked") + .Executes(r => GetUsage(r.Source, "locked"))) + .Then(l => l.Literal("unlocked") + .Executes(r => GetUsage(r.Source, "unlocked"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ListAchievements(r.Source, null)) + .Then(l => l.Literal("list") + .Executes(r => ListAchievements(r.Source, null))) + .Then(l => l.Literal("locked") + .Executes(r => ListAchievements(r.Source, false))) + .Then(l => l.Literal("unlocked") + .Executes(r => ListAchievements(r.Source, true))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format + "list" => GetCmdDescTranslated(), + "locked" => GetCmdDescTranslated(), + "unlocked" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format + }); + } + + /// null = all, true = unlocked only, false = locked only + private static int ListAchievements(CmdResult r, bool? completed) + { + McClient handler = CmdResult.currentHandler!; + + Achievement[] items = completed switch + { + true => handler.GetUnlockedAchievements(), + false => handler.GetLockedAchievements(), + null => handler.GetAchievements() + }; + + if (items.Length == 0) + { + string msg = completed switch + { + true => Translations.cmd_achievement_none_unlocked, + false => Translations.cmd_achievement_none_locked, + _ => Translations.cmd_achievement_none + }; + return r.SetAndReturn(CmdResult.Status.Done, msg); + } + + string header = completed switch + { + true => Translations.cmd_achievement_header_unlocked, + false => Translations.cmd_achievement_header_locked, + _ => Translations.cmd_achievement_header + }; + + StringBuilder sb = new(); + sb.AppendLine(header); + + foreach (Achievement a in items.OrderBy(static a => a.Id)) + { + string status = a.IsCompleted + ? Translations.cmd_achievement_done + : Translations.cmd_achievement_todo; + + string display = a.Title is not null + ? string.Format(Translations.cmd_achievement_entry_titled, status, a.Title, a.Id, a.Type) + : string.Format(Translations.cmd_achievement_entry, status, a.Id, a.Type); + + sb.AppendLine(display); + } + + handler.Log.Info(sb.ToString().TrimEnd()); + return r.SetAndReturn(CmdResult.Status.Done); + } + } +} diff --git a/MinecraftClient/Commands/Book.cs b/MinecraftClient/Commands/Book.cs new file mode 100644 index 00000000..e035f277 --- /dev/null +++ b/MinecraftClient/Commands/Book.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Inventory; +using MinecraftClient.Tui; + +namespace MinecraftClient.Commands +{ + public class Book : Command + { + private const char PageDelimiter = '\f'; + + public override string CmdName => "book"; + public override string CmdUsage => Translations.cmd_book_usage; + public override string CmdDesc => Translations.cmd_book_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("read").Executes(r => GetUsage(r.Source, "read"))) + .Then(l => l.Literal("write").Executes(r => GetUsage(r.Source, "write"))) + .Then(l => l.Literal("edit").Executes(r => GetUsage(r.Source, "edit"))) + .Then(l => l.Literal("sign").Executes(r => GetUsage(r.Source, "sign"))))); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("read") + .Executes(r => ReadBook(r.Source, null)) + .Then(l => l.Argument("Page", Arguments.Integer(min: 1)) + .Executes(r => ReadBook(r.Source, Arguments.GetInteger(r, "Page"))))) + .Then(l => l.Literal("write") + .Then(l => l.Literal("text") + .Then(l => l.Argument("Text", Arguments.GreedyString()) + .Executes(r => WriteBook(r.Source, Arguments.GetString(r, "Text"))))) + .Then(l => l.Literal("file") + .Then(l => l.Argument("Path", Arguments.GreedyString()) + .Executes(r => WriteBookFromFile(r.Source, Arguments.GetString(r, "Path")))))) + .Then(l => l.Literal("edit") + .Executes(r => OpenEditor(r.Source)) + .Then(l => l.Literal("page") + .Then(l => l.Argument("Page", Arguments.Integer(min: 1)) + .Then(l => l.Argument("Text", Arguments.GreedyString()) + .Executes(r => EditPage(r.Source, Arguments.GetInteger(r, "Page"), Arguments.GetString(r, "Text")))))) + .Then(l => l.Literal("insert") + .Then(l => l.Argument("Page", Arguments.Integer(min: 1)) + .Then(l => l.Argument("Text", Arguments.GreedyString()) + .Executes(r => InsertPage(r.Source, Arguments.GetInteger(r, "Page"), Arguments.GetString(r, "Text")))))) + .Then(l => l.Literal("delete") + .Then(l => l.Argument("Page", Arguments.Integer(min: 1)) + .Executes(r => DeletePage(r.Source, Arguments.GetInteger(r, "Page")))))) + .Then(l => l.Literal("sign") + .Then(l => l.Argument("Title", Arguments.GreedyString()) + .Executes(r => SignBook(r.Source, Arguments.GetString(r, "Title"))))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))); + } + + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { + "read" => Translations.cmd_book_help_read, + "write" => Translations.cmd_book_help_write, + "edit" => Translations.cmd_book_help_edit, + "sign" => Translations.cmd_book_help_sign, + _ => GetCmdDescTranslated() + }); + } + + private int ReadBook(CmdResult r, int? page) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureInventory(r, handler)) + return -1; + + if (!handler.TryGetHeldBookContent(out BookContent content)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_not_holding_book); + + if (page is null && BookTuiHost.TryOpen(handler, BookHand.Main, editable: false)) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_tui_opened); + + handler.Log.Info(FormatBook(content, page)); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int OpenEditor(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed)) + return -1; + + return BookTuiHost.TryOpen(handler, BookHand.Main, editable: true) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_tui_opened) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_tui_required); + } + + private int WriteBook(CmdResult r, string text) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed)) + return -1; + + IReadOnlyList pages = SplitPages(text); + if (!Validate(r, handler, pages, title: null)) + return -1; + + return handler.SendBookEdit(pages) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_write_sent) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed); + } + + private int WriteBookFromFile(CmdResult r, string path) + { + if (!File.Exists(path)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_file_not_found, path)); + + return WriteBook(r, File.ReadAllText(path, Encoding.UTF8)); + } + + private int EditPage(CmdResult r, int page, string text) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed)) + return -1; + + List pages = content.Pages.ToList(); + if (page > pages.Count) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count)); + + pages[page - 1] = DecodeInlineText(text); + if (!Validate(r, handler, pages, title: null)) + return -1; + + return handler.SendBookEdit(pages) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed); + } + + private int InsertPage(CmdResult r, int page, string text) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed)) + return -1; + + List pages = content.Pages.ToList(); + if (page > pages.Count + 1) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count)); + + pages.Insert(page - 1, DecodeInlineText(text)); + if (!Validate(r, handler, pages, title: null)) + return -1; + + return handler.SendBookEdit(pages) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed); + } + + private int DeletePage(CmdResult r, int page) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed)) + return -1; + + List pages = content.Pages.ToList(); + if (page > pages.Count) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count)); + + pages.RemoveAt(page - 1); + if (pages.Count == 0) + pages.Add(string.Empty); + + if (!Validate(r, handler, pages, title: null)) + return -1; + + return handler.SendBookEdit(pages) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed); + } + + private int SignBook(CmdResult r, string title) + { + McClient handler = CmdResult.currentHandler!; + if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_already_signed)) + return -1; + + string normalizedTitle = title.Trim(); + if (!Validate(r, handler, content.Pages, normalizedTitle)) + return -1; + + return handler.SendBookEdit(content.Pages, normalizedTitle) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_sign_sent) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed); + } + + private static bool EnsureInventory(CmdResult r, McClient handler) + { + if (handler.GetInventoryEnabled()) + return true; + + r.SetAndReturn(CmdResult.Status.FailNeedInventory); + return false; + } + + private static bool EnsureWritable(CmdResult r, McClient handler, out BookContent content, string signedBookMessage) + { + content = BookContent.EmptyWritable; + if (!EnsureInventory(r, handler)) + return false; + + Item? item = handler.GetHeldBook(); + if (!BookContentHelper.IsWritableBook(item)) + { + r.SetAndReturn(CmdResult.Status.Fail, GetWritableBookFailureMessage(item, signedBookMessage)); + return false; + } + + return BookContentHelper.TryRead(item, out content); + } + + private static string GetWritableBookFailureMessage(Item? item, string signedBookMessage) + { + return BookContentHelper.TryRead(item, out BookContent content) && content.IsSigned + ? signedBookMessage + : Translations.cmd_book_not_holding_writable; + } + + private static IReadOnlyList SplitPages(string text) + { + return BookContentHelper.NormalizePages(DecodeInlineText(text).Split(PageDelimiter)); + } + + private static string DecodeInlineText(string text) + { + return text.Replace("\\f", PageDelimiter.ToString(), StringComparison.Ordinal) + .Replace("\\n", "\n", StringComparison.Ordinal); + } + + private static bool Validate(CmdResult r, McClient handler, IReadOnlyList pages, string? title) + { + BookLimits limits = BookLimits.ForProtocol(handler.GetProtocolVersion()); + + if (pages.Count > limits.MaxPages) + { + r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_too_many_pages, pages.Count, limits.MaxPages)); + return false; + } + + for (int i = 0; i < pages.Count; i++) + { + if (pages[i].Length > limits.MaxPageLength) + { + r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_too_long, i + 1, pages[i].Length, limits.MaxPageLength)); + return false; + } + } + + if (title is not null && (title.Length == 0 || title.Length > limits.MaxTitleLength)) + { + r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_title_invalid, limits.MaxTitleLength)); + return false; + } + + return true; + } + + private static string FormatBook(BookContent content, int? page) + { + StringBuilder sb = new(); + sb.AppendLine(content.IsSigned + ? string.Format(Translations.cmd_book_header_signed, content.Title ?? string.Empty, content.Author ?? string.Empty) + : Translations.cmd_book_header_writable); + + if (page is not null) + { + int index = page.Value - 1; + if (index < 0 || index >= content.Pages.Count) + return string.Format(Translations.cmd_book_page_out_of_range, page.Value, content.Pages.Count); + + sb.AppendLine(string.Format(Translations.cmd_book_page_header, page.Value, content.Pages.Count)); + sb.Append(content.Pages[index]); + return sb.ToString(); + } + + for (int i = 0; i < content.Pages.Count; i++) + { + sb.AppendLine(string.Format(Translations.cmd_book_page_header, i + 1, content.Pages.Count)); + sb.AppendLine(content.Pages[i]); + } + + return sb.ToString().TrimEnd(); + } + } +} diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index 73574a14..9a94615e 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -84,7 +84,7 @@ namespace MinecraftClient.Commands else { ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower()); - if (bot == null) + if (bot is null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_bots_notfound, botName)); else { diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index cdc26e14..cbf82173 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text; using Brigadier.NET; using Brigadier.NET.Builder; @@ -92,7 +92,7 @@ namespace MinecraftClient.Commands sb.Append('\n'); sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ)); - if (markedChunkPos != null) + if (markedChunkPos is not null) { sb.Append(Translations.cmd_chunk_marked); if (pos.HasValue) @@ -100,11 +100,15 @@ namespace MinecraftClient.Commands sb.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ)); ; } - int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); + int safeHeight; + int safeWidth; + try { safeHeight = Console.BufferHeight; } catch { safeHeight = 50; } + try { safeWidth = Console.BufferWidth; } catch { safeWidth = 120; } + int consoleHeight = Math.Max(Math.Max(safeHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); if (consoleHeight % 2 == 0) --consoleHeight; - int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); + int consoleWidth = Math.Max(Math.Max(safeWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); if (consoleWidth % 2 == 0) --consoleWidth; @@ -116,7 +120,7 @@ namespace MinecraftClient.Commands { for (int x = startX; x <= endX; ++x) { - if (world[x, z] != null) + if (world[x, z] is not null) { leftMost = Math.Min(leftMost, x); rightMost = Math.Max(rightMost, x); @@ -180,7 +184,7 @@ namespace MinecraftClient.Commands } // Try to include the marker chunk - if (markedChunkPos != null && + if (markedChunkPos is not null && (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) sb.AppendLine(Translations.cmd_chunk_outside); @@ -208,7 +212,7 @@ namespace MinecraftClient.Commands sb.Append("§§4"); // Marked chunk: background red ChunkColumn? chunkColumn = world[x, z]; - if (chunkColumn == null) + if (chunkColumn is null) sb.Append(chunkStatusStr[0]); else if (chunkColumn.FullyLoaded) sb.Append(chunkStatusStr[2]); @@ -238,10 +242,10 @@ namespace MinecraftClient.Commands handler.Log.Info(Translations.cmd_chunk_for_debug); (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; - if (chunkColumn != null) + if (chunkColumn is not null) chunkColumn.FullyLoaded = false; - if (chunkColumn == null) + if (chunkColumn is null) return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); else return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ)); @@ -258,10 +262,10 @@ namespace MinecraftClient.Commands handler.Log.Info(Translations.cmd_chunk_for_debug); (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; - if (chunkColumn != null) + if (chunkColumn is not null) chunkColumn.FullyLoaded = false; - if (chunkColumn == null) + if (chunkColumn is null) return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); else return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ)); diff --git a/MinecraftClient/Commands/ClearConsole.cs b/MinecraftClient/Commands/ClearConsole.cs new file mode 100644 index 00000000..68ff58cc --- /dev/null +++ b/MinecraftClient/Commands/ClearConsole.cs @@ -0,0 +1,45 @@ +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + public class ClearConsole : Command + { + public override string CmdName => "clear-console"; + public override string CmdUsage => "clear-console"; + public override string CmdDesc => Translations.cmd_clear_console_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source)) + ) + ); + + var clearConsole = dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => Execute(r.Source)) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + + dispatcher.Register(l => l.Literal("cc") + .Executes(r => Execute(r.Source)) + .Redirect(clearConsole) + ); + } + + private int GetUsage(CmdResult result) + { + return result.SetAndReturn(GetCmdDescTranslated()); + } + + private int Execute(CmdResult result) + { + ConsoleIO.ClearConsole(); + return result.SetAndReturn(CmdResult.Status.Done); + } + } +} diff --git a/MinecraftClient/Commands/ConsoleChat.cs b/MinecraftClient/Commands/ConsoleChat.cs new file mode 100644 index 00000000..fd52a6e4 --- /dev/null +++ b/MinecraftClient/Commands/ConsoleChat.cs @@ -0,0 +1,49 @@ +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + public class ConsoleChat : Command + { + public override string CmdName => "console-chat"; + public override string CmdUsage => "console-chat [on|off]"; + public override string CmdDesc => Translations.cmd_console_chat_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => SetChatVisibility(r.Source, null)) + .Then(l => l.Literal("on") + .Executes(r => SetChatVisibility(r.Source, true))) + .Then(l => l.Literal("off") + .Executes(r => SetChatVisibility(r.Source, false))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult result) + { + return result.SetAndReturn(GetCmdDescTranslated()); + } + + private int SetChatVisibility(CmdResult result, bool? visible) + { + ConsoleIO.ChatVisible = visible ?? !ConsoleIO.ChatVisible; + + return result.SetAndReturn( + CmdResult.Status.Done, + ConsoleIO.ChatVisible + ? Translations.cmd_console_chat_state_on + : Translations.cmd_console_chat_state_off); + } + } +} diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index 0302ea05..92184f1d 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -1,13 +1,17 @@ -using Brigadier.NET; +using System; +using System.Linq; +using System.Text; +using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; namespace MinecraftClient.Commands { public class Debug : Command { public override string CmdName { get { return "debug"; } } - public override string CmdUsage { get { return "debug [on|off]"; } } + public override string CmdUsage { get { return "debug [on|off|state]"; } } public override string CmdDesc { get { return Translations.cmd_debug_desc; } } public override void RegisterCommand(CommandDispatcher dispatcher) @@ -24,6 +28,8 @@ namespace MinecraftClient.Commands .Executes(r => SetDebugMode(r.Source, false, true))) .Then(l => l.Literal("off") .Executes(r => SetDebugMode(r.Source, false, false))) + .Then(l => l.Literal("state") + .Executes(r => ShowState(r.Source))) .Then(l => l.Literal("_help") .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) @@ -42,15 +48,57 @@ namespace MinecraftClient.Commands private int SetDebugMode(CmdResult r, bool flip, bool mode = false) { + McClient handler = CmdResult.currentHandler!; + if (flip) Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages; else Settings.Config.Logging.DebugMessages = mode; + handler.Log.DebugEnabled = Settings.Config.Logging.DebugMessages; + if (Settings.Config.Logging.DebugMessages) return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_on); else return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_off); } + + private int ShowState(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + var sb = new StringBuilder(); + + sb.AppendLine($"§e=== {Translations.cmd_debug_state_header} ==="); + sb.AppendLine($"§7{Translations.cmd_debug_state_server,-10}§f{handler.GetServerHost()}:{handler.GetServerPort()}"); + sb.AppendLine($"§7{Translations.cmd_debug_state_username,-10}§f{handler.GetUsername()}"); + sb.AppendLine($"§7{Translations.cmd_debug_state_protocol,-10}§f{handler.GetProtocolVersion()}"); + sb.AppendLine($"§7{Translations.cmd_debug_state_gamemode,-10}§f{handler.GetGamemode()}"); + sb.AppendLine($"§7{Translations.cmd_debug_state_health,-10}§f{handler.GetHealth():F1}"); + sb.AppendLine($"§7{Translations.cmd_debug_state_food,-10}§f{handler.GetSaturation()}"); + + var loc = handler.GetCurrentLocation(); + sb.AppendLine($"§7{Translations.cmd_debug_state_location,-10}§f{loc.X:F2}, {loc.Y:F2}, {loc.Z:F2}"); + + sb.AppendLine($"§7{Translations.cmd_debug_state_tps,-10}§f{handler.GetServerTPS():F1}"); + + sb.AppendLine($"§7{Translations.cmd_debug_state_console,-10}§f{(ConsoleIO.Backend?.GetType().Name ?? "null")}"); + + var features = new StringBuilder(); + features.Append(handler.GetTerrainEnabled() ? "§aTerrain " : "§8Terrain "); + features.Append(handler.GetInventoryEnabled() ? "§aInventory " : "§8Inventory "); + features.Append(handler.GetEntityHandlingEnabled() ? "§aEntity " : "§8Entity "); + sb.AppendLine($"§7{Translations.cmd_debug_state_features,-10}{features}"); + + sb.AppendLine($"§7{Translations.cmd_debug_state_debug,-10}§f{(Settings.Config.Logging.DebugMessages ? "§aON" : "§cOFF")}"); + + var bots = handler.GetLoadedChatBots(); + sb.AppendLine($"§7{Translations.cmd_debug_state_bots} ({bots.Count}): §f{string.Join(", ", bots.Select(b => b.GetType().Name))}"); + + var players = handler.GetOnlinePlayers(); + sb.AppendLine($"§7{Translations.cmd_debug_state_players,-10}§f{string.Format(Translations.cmd_debug_state_online, players.Length)}"); + + handler.Log.Info(sb.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } } } diff --git a/MinecraftClient/Commands/Dialog.cs b/MinecraftClient/Commands/Dialog.cs new file mode 100644 index 00000000..826fa592 --- /dev/null +++ b/MinecraftClient/Commands/Dialog.cs @@ -0,0 +1,108 @@ +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Dialogs; +using MinecraftClient.Tui; + +namespace MinecraftClient.Commands; + +public class Dialog : Command +{ + public override string CmdName => "dialog"; + public override string CmdUsage => Translations.cmd_dialog_usage; + public override string CmdDesc => Translations.cmd_dialog_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source)))); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => Show(r.Source)) + .Then(l => l.Literal("show") + .Executes(r => Show(r.Source))) + .Then(l => l.Literal("open") + .Executes(r => Open(r.Source))) + .Then(l => l.Literal("set") + .Then(l => l.Argument("Input", Arguments.String()) + .Then(l => l.Argument("Value", Arguments.GreedyString()) + .Executes(r => SetInput(r.Source, Arguments.GetString(r, "Input"), Arguments.GetString(r, "Value")))))) + .Then(l => l.Literal("input") + .Then(l => l.Argument("Input", Arguments.String()) + .Then(l => l.Argument("Value", Arguments.GreedyString()) + .Executes(r => SetInput(r.Source, Arguments.GetString(r, "Input"), Arguments.GetString(r, "Value")))))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Index", Arguments.Integer(min: 1)) + .Executes(r => Click(r.Source, Arguments.GetInteger(r, "Index"))))) + .Then(l => l.Literal("click-label") + .Then(l => l.Argument("Label", Arguments.GreedyString()) + .Executes(r => ClickLabel(r.Source, Arguments.GetString(r, "Label"))))) + .Then(l => l.Literal("cancel") + .Executes(r => Cancel(r.Source))) + .Then(l => l.Literal("dismiss") + .Executes(r => Dismiss(r.Source))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))); + } + + private int GetUsage(CmdResult r) => r.SetAndReturn(GetCmdDescTranslated()); + + private static int Show(CmdResult r) + { + var handler = CmdResult.currentHandler!; + var current = handler.Dialogs.Current; + if (current is null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_none); + + ConsoleIO.WriteLineFormatted(DialogFormatter.Render(current), acceptnewlines: true); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private static int Open(CmdResult r) + { + var handler = CmdResult.currentHandler!; + var current = handler.Dialogs.Current; + if (current is null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_none); + + if (ConsoleIO.Backend is not TuiConsoleBackend) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_tui_unavailable); + + return DialogTuiHost.TryOpen(handler, current, force: true) + ? r.SetAndReturn(CmdResult.Status.Done, Translations.dialog_tui_opened) + : r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_tui_unavailable); + } + + private static int SetInput(CmdResult r, string key, string value) + { + var result = CmdResult.currentHandler!.Dialogs.SetInput(key, value); + return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message); + } + + private static int Click(CmdResult r, int index) + { + var result = CmdResult.currentHandler!.Dialogs.Click(index); + return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message); + } + + private static int ClickLabel(CmdResult r, string label) + { + var result = CmdResult.currentHandler!.Dialogs.ClickLabel(label); + return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message); + } + + private static int Cancel(CmdResult r) + { + var result = CmdResult.currentHandler!.Dialogs.Cancel(); + return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message); + } + + private static int Dismiss(CmdResult r) + { + var result = CmdResult.currentHandler!.Dialogs.Dismiss(); + DialogTuiHost.CloseCurrent(); + return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message); + } +} diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index 3a00c44b..477e2c77 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) + // TODO Get blockFace direction from arguments .Executes(r => DigLookAt(r.Source)) .Then(l => l.Argument("Duration", Arguments.Double()) .Executes(r => DigLookAt(r.Source, Arguments.GetDouble(r, "Duration")))) @@ -58,7 +59,7 @@ namespace MinecraftClient.Commands Block block = handler.GetWorld().GetBlock(blockToBreak); if (block.Type == Material.Air) return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); - else if (handler.DigBlock(blockToBreak, duration: duration)) + else if (handler.DigBlock(blockToBreak, Direction.Down, duration: duration)) { blockToBreak = blockToBreak.ToCenter(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString())); @@ -78,7 +79,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); else if (block.Type == Material.Air) return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); - else if (handler.DigBlock(blockLoc, lookAtBlock: false, duration: duration)) + else if (handler.DigBlock(blockLoc, Direction.Down, lookAtBlock: false, duration: duration)) return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString())); else return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); diff --git a/MinecraftClient/Commands/EffectsCommand.cs b/MinecraftClient/Commands/EffectsCommand.cs new file mode 100644 index 00000000..700764aa --- /dev/null +++ b/MinecraftClient/Commands/EffectsCommand.cs @@ -0,0 +1,67 @@ +using System.Linq; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + public class EffectsCommand : Command + { + public override string CmdName { get { return "effects"; } } + public override string CmdUsage { get { return "effects"; } } + public override string CmdDesc { get { return Translations.cmd_effects_desc; } } + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ShowEffects(r.Source)) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int ShowEffects(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedEntity); + + var effects = handler.GetPlayerEffects() + .Values + .Where(effectData => !effectData.IsExpired) + .OrderBy(effectData => effectData.Effect) + .ToArray(); + + if (effects.Length == 0) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_effects_none); + + StringBuilder response = new(); + response.AppendLine(Translations.cmd_effects_header); + foreach (var effectData in effects) + { + response.AppendLine(string.Format(Translations.cmd_effects_entry, + effectData.GetDisplayName(), effectData.GetRemainingDurationText())); + } + + return r.SetAndReturn(CmdResult.Status.Done, response.ToString().TrimEnd()); + } + } +} diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index 813c734f..1fc361f7 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -66,7 +66,7 @@ namespace MinecraftClient.Commands } } - if (enchantingTable == null) + if (enchantingTable is null) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_table_not_opened); int[] emptySlots = enchantingTable.GetEmpytSlots(); @@ -84,7 +84,7 @@ namespace MinecraftClient.Commands EnchantmentData? enchantment = handler.GetLastEnchantments(); - if (enchantment == null) + if (enchantment is null) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_no_enchantments); short requiredLevel = slotId switch diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 258227cd..b0ed8a1b 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -210,7 +210,7 @@ namespace MinecraftClient.Commands Item item = entity.Item; string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; - if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket) + if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.SplashPotion || type == EntityType.LingeringPotion || type == EntityType.Fireball || type == EntityType.FireworkRocket) return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}"; else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname)) return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"; @@ -251,7 +251,7 @@ namespace MinecraftClient.Commands { sb.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}"); } - else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) + else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.SplashPotion || type == Mapping.EntityType.LingeringPotion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) { string? displayName = item.DisplayName; if (string.IsNullOrEmpty(displayName)) @@ -260,20 +260,20 @@ namespace MinecraftClient.Commands sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8"); } - if (entity.Equipment.Count >= 1 && entity.Equipment != null) + if (entity.Equipment is not null && entity.Equipment.Count >= 1) { sb.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:"); - if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null) + if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}"); - if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null) + if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}"); - if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null) + if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}"); - if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null) + if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}"); - if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null) + if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}"); - if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null) + if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] is not null) sb.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}"); } @@ -317,7 +317,7 @@ namespace MinecraftClient.Commands bool shouldInteractAt = entity.Type == EntityType.ArmorStand || entity.Type == EntityType.ChestMinecart || entity.Type == EntityType.ChestBoat; - + handler.InteractEntity(entity.ID, shouldInteractAt ? InteractType.InteractAt : InteractType.Interact); return Translations.cmd_entityCmd_used; case ActionType.List: diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index c0397b08..cc90f185 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,6 +6,7 @@ using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; +using MinecraftClient.Tui; namespace MinecraftClient.Commands { @@ -22,6 +23,8 @@ namespace MinecraftClient.Commands .Executes(r => GetUsage(r.Source, string.Empty)) .Then(l => l.Literal("list") .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("open") + .Executes(r => GetUsage(r.Source, "open"))) .Then(l => l.Literal("close") .Executes(r => GetUsage(r.Source, "close"))) .Then(l => l.Literal("click") @@ -59,6 +62,9 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Count", Arguments.Integer(0, 64)) .Executes(r => SearchItem(r.Source, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count")))))) .Then(l => l.Argument("InventoryId", MccArguments.InventoryId()) + .Executes(r => DoOpenOrList(r.Source, Arguments.GetInteger(r, "InventoryId"))) + .Then(l => l.Literal("open") + .Executes(r => DoOpenTui(r.Source, Arguments.GetInteger(r, "InventoryId")))) .Then(l => l.Literal("close") .Executes(r => DoCloseAction(r.Source, Arguments.GetInteger(r, "InventoryId")))) .Then(l => l.Literal("list") @@ -113,6 +119,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(cmd switch { #pragma warning disable format // @formatter:off + "open" => Translations.cmd_inventory_help_open + usageStr + "/inventory open", "list" => Translations.cmd_inventory_help_list + usageStr + "/inventory > list", "close" => Translations.cmd_inventory_help_close + usageStr + "/inventory > close", "click" => Translations.cmd_inventory_help_click + usageStr + "/inventory > click [left|right|middle|shift|shiftright]\nDefault is left click", @@ -276,7 +283,7 @@ namespace MinecraftClient.Commands } Container? inventory = handler.GetInventory(inventoryId.Value); - if (inventory == null) + if (inventory is null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); if (handler.CloseInventory(inventoryId.Value)) @@ -299,7 +306,7 @@ namespace MinecraftClient.Commands } Container? inventory = handler.GetInventory(inventoryId.Value); - if (inventory == null) + if (inventory is null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); StringBuilder response = new(); @@ -307,7 +314,7 @@ namespace MinecraftClient.Commands response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); string? asciiArt = inventory.Type.GetAsciiArt(); - if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) + if (asciiArt is not null && Settings.Config.Main.Advanced.ShowInventoryLayout) response.AppendLine(asciiArt); int selectedHotbar = handler.GetCurrentSlot() + 1; @@ -342,7 +349,7 @@ namespace MinecraftClient.Commands } Container? inventory = handler.GetInventory(inventoryId.Value); - if (inventory == null) + if (inventory is null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); string keyName = actionType switch @@ -373,7 +380,7 @@ namespace MinecraftClient.Commands } Container? inventory = handler.GetInventory(inventoryId.Value); - if (inventory == null) + if (inventory is null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); // check item exist @@ -394,6 +401,61 @@ namespace MinecraftClient.Commands } + private int DoOpenOrList(CmdResult r, int inventoryId) + { + if (ConsoleIO.Backend is TuiConsoleBackend) + return DoOpenTui(r, inventoryId); + return DoListAction(r, inventoryId); + } + + private int DoOpenTui(CmdResult r, int inventoryId) + { + McClient handler = CmdResult.currentHandler!; + + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (ConsoleIO.Backend is not TuiConsoleBackend) + { + handler.Log.Warn(Translations.cmd_inventory_tui_only); + return r.SetAndReturn(CmdResult.Status.Fail); + } + + if (InventoryTuiHost.IsRunning) + { + handler.Log.Warn(Translations.cmd_inventory_tui_already_running); + return r.SetAndReturn(CmdResult.Status.Fail); + } + + var container = handler.GetInventory(inventoryId); + if (container == null) + { + string msg = string.Format(Translations.cmd_inventory_not_exist, inventoryId); + handler.Log.Warn(msg); + return r.SetAndReturn(CmdResult.Status.Fail, msg); + } + + if (!Tui.ContainerViewBase.HasTuiSupport(container.Type)) + { + handler.Log.Warn(string.Format(Translations.cmd_inventory_tui_unsupported_container, inventoryId)); + return r.SetAndReturn(CmdResult.Status.Fail); + } + + handler.Log.Info(string.Format(Translations.cmd_inventory_tui_opening, inventoryId)); + + bool success = InventoryTuiHost.Launch(handler, inventoryId); + if (success) + { + handler.Log.Info(Translations.cmd_inventory_tui_opened); + return r.SetAndReturn(CmdResult.Status.Done); + } + else + { + handler.Log.Warn(Translations.cmd_inventory_tui_launch_failed); + return r.SetAndReturn(CmdResult.Status.Fail); + } + } + #region Methods for commands help private static string GetAvailableActions() diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index 69f0dc2d..aedc474b 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -92,6 +92,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.FailNeedTerrain); handler.UpdateLocation(handler.GetCurrentLocation(), direction); + handler.SendLocationUpdate(); return r.SetAndReturn(Status.Done, "Looking " + direction.ToString()); } @@ -102,6 +103,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.FailNeedTerrain); handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch); + handler.SendLocationUpdate(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00"))); } @@ -113,6 +115,7 @@ namespace MinecraftClient.Commands Location current = handler.GetCurrentLocation(); handler.UpdateLocation(current, location); + handler.SendLocationUpdate(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_block, location)); } } diff --git a/MinecraftClient/Commands/Minimap.cs b/MinecraftClient/Commands/Minimap.cs new file mode 100644 index 00000000..0b6ca00f --- /dev/null +++ b/MinecraftClient/Commands/Minimap.cs @@ -0,0 +1,284 @@ +using System; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Tui; +using Avalonia.Threading; +using static MinecraftClient.CommandHandler.CmdResult; + +namespace MinecraftClient.Commands +{ + class Minimap : Command + { + public override string CmdName => "minimap"; + public override string CmdUsage => "minimap [on|off] | minimap zoom [in|out|<1-16>] | minimap names [players|hostile|neutral|passive] [on|off] | minimap names [all_on|all_off] | minimap position [top_left|top_right|center|bottom_left|bottom_right] | minimap cave [auto|on|off]"; + public override string CmdDesc => Translations.cmd_minimap_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoToggle(r.Source)) + .Then(l => l.Literal("on") + .Executes(r => DoOn(r.Source))) + .Then(l => l.Literal("off") + .Executes(r => DoOff(r.Source))) + .Then(l => l.Literal("zoom") + .Executes(r => DoZoomInfo(r.Source)) + .Then(l => l.Literal("in") + .Executes(r => DoZoomIn(r.Source))) + .Then(l => l.Literal("out") + .Executes(r => DoZoomOut(r.Source))) + .Then(l => l.Argument("level", Arguments.Integer(MinimapControl.MinZoom, MinimapControl.MaxZoom)) + .Executes(r => DoZoomSet(r.Source, Arguments.GetInteger(r, "level"))))) + .Then(l => l.Literal("names") + .Executes(r => DoNamesInfo(r.Source)) + .Then(l => l.Literal("all_on") + .Executes(r => DoNamesAll(r.Source, true))) + .Then(l => l.Literal("all_off") + .Executes(r => DoNamesAll(r.Source, false))) + .Then(l => l.Literal("players") + .Executes(r => DoNamesCatInfo(r.Source, MobCategory.Player)) + .Then(l => l.Literal("on") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Player, true))) + .Then(l => l.Literal("off") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Player, false)))) + .Then(l => l.Literal("hostile") + .Executes(r => DoNamesCatInfo(r.Source, MobCategory.Hostile)) + .Then(l => l.Literal("on") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Hostile, true))) + .Then(l => l.Literal("off") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Hostile, false)))) + .Then(l => l.Literal("neutral") + .Executes(r => DoNamesCatInfo(r.Source, MobCategory.Neutral)) + .Then(l => l.Literal("on") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Neutral, true))) + .Then(l => l.Literal("off") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Neutral, false)))) + .Then(l => l.Literal("passive") + .Executes(r => DoNamesCatInfo(r.Source, MobCategory.Passive)) + .Then(l => l.Literal("on") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Passive, true))) + .Then(l => l.Literal("off") + .Executes(r => DoNamesCatSet(r.Source, MobCategory.Passive, false))))) + .Then(l => l.Literal("position") + .Executes(r => DoPositionInfo(r.Source)) + .Then(l => l.Literal("top_left") + .Executes(r => DoPositionSet(r.Source, MinimapPosition.top_left))) + .Then(l => l.Literal("top_right") + .Executes(r => DoPositionSet(r.Source, MinimapPosition.top_right))) + .Then(l => l.Literal("center") + .Executes(r => DoPositionSet(r.Source, MinimapPosition.center))) + .Then(l => l.Literal("bottom_left") + .Executes(r => DoPositionSet(r.Source, MinimapPosition.bottom_left))) + .Then(l => l.Literal("bottom_right") + .Executes(r => DoPositionSet(r.Source, MinimapPosition.bottom_right)))) + .Then(l => l.Literal("cave") + .Executes(r => DoCaveInfo(r.Source)) + .Then(l => l.Literal("auto") + .Executes(r => DoCaveSet(r.Source, CaveModeOption.auto))) + .Then(l => l.Literal("on") + .Executes(r => DoCaveSet(r.Source, CaveModeOption.on))) + .Then(l => l.Literal("off") + .Executes(r => DoCaveSet(r.Source, CaveModeOption.off)))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help")?.GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r, string _) => + r.SetAndReturn(GetCmdDescTranslated()); + + private static MainTuiView? GetTuiView(CmdResult r) + { + if (ConsoleIO.Backend is not TuiConsoleBackend) + { + r.SetAndReturn(Status.Fail, Translations.cmd_minimap_tui_only); + return null; + } + return TuiConsoleBackend.Instance?.GetView(); + } + + private static int DoToggle(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + bool wasVisible = view.IsMinimapVisible; + Dispatcher.UIThread.Post(() => view.ToggleMinimap()); + string msg = wasVisible + ? Translations.cmd_minimap_disabled + : Translations.cmd_minimap_enabled; + return r.SetAndReturn(Status.Done, msg); + } + + private static int DoOn(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => view.ShowMinimap()); + return r.SetAndReturn(Status.Done, Translations.cmd_minimap_enabled); + } + + private static int DoOff(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => view.HideMinimap()); + return r.SetAndReturn(Status.Done, Translations.cmd_minimap_disabled); + } + + private static int DoZoomInfo(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + int current = view.GetMinimapZoom(); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_zoom_current, current, MinimapControl.MaxZoom)); + } + + private static int DoZoomIn(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + int newLevel = Math.Max(view.GetMinimapZoom() - 1, MinimapControl.MinZoom); + Dispatcher.UIThread.Post(() => view.SetMinimapZoom(newLevel)); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, newLevel)); + } + + private static int DoZoomOut(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + int newLevel = Math.Min(view.GetMinimapZoom() + 1, MinimapControl.MaxZoom); + Dispatcher.UIThread.Post(() => view.SetMinimapZoom(newLevel)); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, newLevel)); + } + + private static int DoZoomSet(CmdResult r, int level) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => view.SetMinimapZoom(level)); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, level)); + } + + private static int DoNamesInfo(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + var nc = view.GetMinimapNameConfig(); + string status = string.Format(Translations.cmd_minimap_names_status, + BoolStr(nc.Players), BoolStr(nc.Hostile), BoolStr(nc.Neutral), BoolStr(nc.Passive)); + return r.SetAndReturn(Status.Done, status); + } + + private static int DoNamesAll(CmdResult r, bool on) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => + { + view.GetMinimapNameConfig().SetAll(on); + view.SyncMinimapNameConfig(); + }); + string msg = on ? Translations.cmd_minimap_names_all_on : Translations.cmd_minimap_names_all_off; + return r.SetAndReturn(Status.Done, msg); + } + + private static int DoNamesCatInfo(CmdResult r, MobCategory cat) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + var nc = view.GetMinimapNameConfig(); + bool val = cat switch + { + MobCategory.Player => nc.Players, + MobCategory.Hostile => nc.Hostile, + MobCategory.Neutral => nc.Neutral, + MobCategory.Passive => nc.Passive, + _ => false, + }; + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_names_cat, cat, BoolStr(val))); + } + + private static int DoNamesCatSet(CmdResult r, MobCategory cat, bool on) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => + { + var nc = view.GetMinimapNameConfig(); + switch (cat) + { + case MobCategory.Player: nc.Players = on; break; + case MobCategory.Hostile: nc.Hostile = on; break; + case MobCategory.Neutral: nc.Neutral = on; break; + case MobCategory.Passive: nc.Passive = on; break; + } + view.SyncMinimapNameConfig(); + }); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_names_cat_set, cat, BoolStr(on))); + } + + private static int DoPositionInfo(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + var pos = view.GetMinimapPosition(); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_position_current, pos)); + } + + private static int DoPositionSet(CmdResult r, MinimapPosition pos) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => view.SetMinimapPosition(pos)); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_position_set, pos)); + } + + private static int DoCaveInfo(CmdResult r) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + var mode = view.GetMinimapCaveMode(); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_cave_current, mode)); + } + + private static int DoCaveSet(CmdResult r, CaveModeOption mode) + { + var view = GetTuiView(r); + if (view is null) return (int)r.status; + + Dispatcher.UIThread.Post(() => view.SetMinimapCaveMode(mode)); + return r.SetAndReturn(Status.Done, + string.Format(Translations.cmd_minimap_cave_set, mode)); + } + + private static string BoolStr(bool v) => v ? "ON" : "OFF"; + } +} diff --git a/MinecraftClient/Commands/RecipeBook.cs b/MinecraftClient/Commands/RecipeBook.cs new file mode 100644 index 00000000..4cf0d873 --- /dev/null +++ b/MinecraftClient/Commands/RecipeBook.cs @@ -0,0 +1,98 @@ +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + public class RecipeBook : Command + { + public override string CmdName => "recipebook"; + public override string CmdUsage => "recipebook [recipe id]"; + public override string CmdDesc => Translations.cmd_recipebook_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("craft") + .Executes(r => GetUsage(r.Source, "craft"))) + .Then(l => l.Literal("craftall") + .Executes(r => GetUsage(r.Source, "craftall"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("list") + .Executes(r => ListRecipes(r.Source))) + .Then(l => l.Literal("craft") + .Then(l => l.Argument("RecipeId", Arguments.String()) + .Executes(r => CraftRecipe(r.Source, Arguments.GetString(r, "RecipeId"), makeAll: false)))) + .Then(l => l.Literal("craftall") + .Then(l => l.Argument("RecipeId", Arguments.String()) + .Executes(r => CraftRecipe(r.Source, Arguments.GetString(r, "RecipeId"), makeAll: true)))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + "list" => GetCmdDescTranslated(), + "craft" => GetCmdDescTranslated(), + "craftall" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int ListRecipes(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + RecipeBookRecipeEntry[] recipes = handler.GetUnlockedRecipes(); + if (recipes.Length == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_no_recipes); + + StringBuilder response = new(); + response.AppendLine(Translations.cmd_recipebook_list); + foreach (RecipeBookRecipeEntry recipe in recipes) + response.AppendLine("- " + recipe.DisplayText); + + handler.Log.Info(response.ToString().TrimEnd()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int CraftRecipe(CmdResult r, string recipeId, bool makeAll) + { + McClient handler = CmdResult.currentHandler!; + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (string.IsNullOrWhiteSpace(recipeId)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_recipe_id_empty); + + if (handler.GetProtocolVersion() < Protocol.Handlers.Protocol18Handler.MC_1_13_Version) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_unsupported); + + if (handler.GetActiveRecipeBookInventory() is null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_no_active_inventory); + + string normalizedRecipeId = McClient.NormalizeRecipeArgument(recipeId, handler.GetProtocolVersion()); + string successMessage = string.Format(makeAll ? Translations.cmd_recipebook_craftall_sent : Translations.cmd_recipebook_craft_sent, normalizedRecipeId); + + return handler.SendPlaceRecipe(recipeId, makeAll) + ? r.SetAndReturn(CmdResult.Status.Done, successMessage) + : r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_recipebook_craft_failed, normalizedRecipeId)); + } + } +} diff --git a/MinecraftClient/Commands/Tab.cs b/MinecraftClient/Commands/Tab.cs new file mode 100644 index 00000000..b5ea5c36 --- /dev/null +++ b/MinecraftClient/Commands/Tab.cs @@ -0,0 +1,50 @@ +using Avalonia.Threading; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Tui; + +namespace MinecraftClient.Commands +{ + public class Tab : Command + { + public override string CmdName => "tab"; + public override string CmdUsage => "tab"; + public override string CmdDesc => Translations.cmd_tab_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source))) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ShowTab(r.Source)) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r) => r.SetAndReturn(GetCmdDescTranslated()); + + private static int ShowTab(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + var snapshot = handler.GetTabListSnapshot(); + + if (ConsoleIO.Backend is TuiConsoleBackend) + { + var view = TuiConsoleBackend.Instance?.GetView(); + if (view is null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_tab_tui_unavailable); + + Dispatcher.UIThread.Post(() => view.ShowOverlay(new TabListOverlay(handler))); + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tab_tui_opened); + } + + return r.SetAndReturn(CmdResult.Status.Done, TabListFormatter.Render(snapshot)); + } + } +} diff --git a/MinecraftClient/Commands/Teams.cs b/MinecraftClient/Commands/Teams.cs new file mode 100644 index 00000000..af7a06d1 --- /dev/null +++ b/MinecraftClient/Commands/Teams.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Commands +{ + public class Teams : Command + { + public override string CmdName => "teams"; + public override string CmdUsage => "teams"; + public override string CmdDesc => Translations.cmd_teams_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoListTeams(r.Source)) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int DoListTeams(CmdResult r) + { + McClient handler = CmdResult.currentHandler!; + Dictionary snapshot = handler.GetTeams(); + + if (snapshot.Count == 0) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_teams_no_teams); + + var sb = new StringBuilder(); + foreach (var team in snapshot.Values.OrderBy(static t => t.Name, StringComparer.Ordinal)) + { + sb.AppendLine(string.Format(Translations.cmd_teams_team_header, + team.Name, + team.DisplayName, + team.Color, + team.Prefix, + team.Suffix, + team.NameTagVisibility, + team.CollisionRule, + team.AllowFriendlyFire, + team.SeeFriendlyInvisibles)); + + if (team.Members.Count == 0) + sb.AppendLine(Translations.cmd_teams_team_no_members); + else + sb.AppendLine(string.Format(Translations.cmd_teams_team_members, + team.Members.Count, + string.Join(", ", team.Members.OrderBy(static m => m, StringComparer.OrdinalIgnoreCase)))); + } + + return r.SetAndReturn(CmdResult.Status.Done, sb.ToString().TrimEnd()); + } + } +} diff --git a/MinecraftClient/Commands/Tryout.cs b/MinecraftClient/Commands/Tryout.cs new file mode 100644 index 00000000..8ae0829b --- /dev/null +++ b/MinecraftClient/Commands/Tryout.cs @@ -0,0 +1,63 @@ +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig; + +namespace MinecraftClient.Commands +{ + public class Tryout : Command + { + public override string CmdName => "tryout"; + public override string CmdUsage => "tryout [list|tui]"; + public override string CmdDesc => Translations.cmd_tryout_desc; + + public override void RegisterCommand(CommandDispatcher dispatcher) + { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ListTryouts(r.Source)) + .Then(l => l.Literal("list") + .Executes(r => ListTryouts(r.Source))) + .Then(l => l.Literal("tui") + .Executes(r => EnableTuiMode(r.Source))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + } + + private int GetUsage(CmdResult r) + { + return r.SetAndReturn(GetCmdDescTranslated()); + } + + private int ListTryouts(CmdResult r) + { + return r.SetAndReturn(string.Join('\n', + GetCmdDescTranslated(), + string.Empty, + Translations.cmd_tryout_list_header, + $" - {Translations.cmd_tryout_list_tui}")); + } + + private int EnableTuiMode(CmdResult r) + { + var previousMode = Settings.Config.Console.General.ConsoleMode; + if (previousMode == ConsoleModeType.tui) + { + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tryout_tui_already_enabled); + } + + Settings.Config.Console.General.ConsoleMode = ConsoleModeType.tui; + Program.WriteBackSettings(); + + return r.SetAndReturn(CmdResult.Status.Done, + string.Format(Translations.cmd_tryout_tui_enabled, previousMode, ConsoleModeType.tui)); + } + } +} diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 4a0fe1f6..70ad0003 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -1,6 +1,8 @@ using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; +using MinecraftClient.Inventory; +using MinecraftClient.Mapping; using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands @@ -8,7 +10,7 @@ namespace MinecraftClient.Commands class UseItem : Command { public override string CmdName { get { return "useitem"; } } - public override string CmdUsage { get { return "useitem"; } } + public override string CmdUsage { get { return "useitem [mainhand|offhand] | useitem [x] [y] [z] [mainhand|offhand]"; } } public override string CmdDesc { get { return Translations.cmd_useitem_desc; } } public override void RegisterCommand(CommandDispatcher dispatcher) @@ -21,6 +23,16 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoUseItem(r.Source)) + .Then(l => l.Literal("mainhand") + .Executes(r => DoUseItem(r.Source, Hand.MainHand))) + .Then(l => l.Literal("offhand") + .Executes(r => DoUseItem(r.Source, Hand.OffHand))) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand)) + .Then(l => l.Literal("mainhand") + .Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand))) + .Then(l => l.Literal("offhand") + .Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.OffHand)))) .Then(l => l.Literal("_help") .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) @@ -37,14 +49,64 @@ namespace MinecraftClient.Commands }); } - private int DoUseItem(CmdResult r) + private static bool ShouldUseOffhandFood(McClient handler) + { + Container? inventory = handler.GetInventory(0); + if (inventory is null) + return false; + + if (!inventory.Items.TryGetValue(45, out Item? offhandItem) + || offhandItem.IsEmpty + || !offhandItem.Type.IsFood()) + return false; + + int mainHandSlot = 36 + handler.GetCurrentSlot(); + return !inventory.Items.TryGetValue(mainHandSlot, out Item? mainHandItem) + || mainHandItem.IsEmpty + || !mainHandItem.Type.IsFood(); + } + + private int DoUseItem(CmdResult r, Hand? requestedHand = null) { McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedInventory); - handler.UseItemOnHand(); + Hand hand = requestedHand ?? (ShouldUseOffhandFood(handler) ? Hand.OffHand : Hand.MainHand); + bool useOffhandFood = !requestedHand.HasValue && hand == Hand.OffHand; + + if (!useOffhandFood && handler.GetTerrainEnabled()) + { + const double maxDistance = 4.5; + var raycast = RaycastHelper.RaycastBlock(handler, maxDistance, false); + if (raycast.Item1 && raycast.Item3.Type != Material.Air) + { + handler.PlaceBlock(raycast.Item2, Direction.Up, hand, lookAtBlock: true); + handler.DoAnimation((int)hand); + return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); + } + } + + if (hand == Hand.OffHand) + handler.UseItemOnLeftHand(); + else + handler.UseItemOnHand(); + return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); } + + private int DoUseItemAtLocation(CmdResult r, Location block, Hand hand) + { + McClient handler = CmdResult.currentHandler!; + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + block = block.ToAbsolute(current).ToFloor(); + handler.PlaceBlock(block, Direction.Up, hand, lookAtBlock: true); + handler.DoAnimation((int)hand); + return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); + } + } } diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 994e34ae..d4f964a6 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -1,6 +1,8 @@ -using Brigadier.NET; +using System; +using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; +using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.CommandHandler.CmdResult; @@ -9,7 +11,7 @@ namespace MinecraftClient.Commands class Useblock : Command { public override string CmdName { get { return "useblock"; } } - public override string CmdUsage { get { return "useblock "; } } + public override string CmdUsage { get { return "useblock [mainhand|offhand]"; } } public override string CmdDesc { get { return Translations.cmd_useblock_desc; } } public override void RegisterCommand(CommandDispatcher dispatcher) @@ -22,7 +24,11 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location")))) + .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand)) + .Then(l => l.Literal("mainhand") + .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand))) + .Then(l => l.Literal("offhand") + .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.OffHand)))) .Then(l => l.Literal("_help") .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) @@ -39,7 +45,7 @@ namespace MinecraftClient.Commands }); } - private int UseBlockAtLocation(CmdResult r, Location block) + private int UseBlockAtLocation(CmdResult r, Location block, Hand hand) { McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) @@ -48,8 +54,27 @@ namespace MinecraftClient.Commands Location current = handler.GetCurrentLocation(); block = block.ToAbsolute(current).ToFloor(); Location blockCenter = block.ToCenter(); - bool res = handler.PlaceBlock(block, Direction.Down); + bool res = handler.PlaceBlock(block, GetFaceNearestPlayer(current, blockCenter), hand, lookAtBlock: true); return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res); } + + private static Direction GetFaceNearestPlayer(Location playerLocation, Location blockCenter) + { + double dx = playerLocation.X - blockCenter.X; + double dy = playerLocation.Y - blockCenter.Y; + double dz = playerLocation.Z - blockCenter.Z; + + double absX = Math.Abs(dx); + double absY = Math.Abs(dy); + double absZ = Math.Abs(dz); + + if (absX >= absY && absX >= absZ) + return dx >= 0 ? Direction.East : Direction.West; + + if (absY >= absZ) + return dy >= 0 ? Direction.Up : Direction.Down; + + return dz >= 0 ? Direction.South : Direction.North; + } } } diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index c200a3d8..2c5a168c 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -8,6 +9,7 @@ using Brigadier.NET; using FuzzySharp; using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; +using MinecraftClient.Tui; using static MinecraftClient.Settings; namespace MinecraftClient @@ -16,18 +18,23 @@ namespace MinecraftClient /// Allows simultaneous console input and output without breaking user input /// (Without having this annoying behaviour : User inp[Some Console output]ut) /// Provide some fancy features such as formatted output, text pasting and tab-completion. - /// By ORelio - (c) 2012-2018 - Available under the CDDL-1.0 license + /// By ORelio - (c) 2012-2018 - Available under the CDDL-1.0 License /// public static class ConsoleIO { private static IAutoComplete? autocomplete_engine; + /// + /// The active console backend. Set once during startup. + /// + public static IConsoleBackend Backend { get; set; } = null!; + /// /// Reset the IO mechanism and clear all buffers /// public static void Reset() { - ClearLineAndBuffer(); + Backend?.ClearInputBuffer(); } /// @@ -40,9 +47,8 @@ namespace MinecraftClient } /// - /// Determines whether to use interactive IO or basic IO. - /// Set to true to disable interactive command prompt and use the default Console.Read|Write() methods. - /// Color codes are printed as is when BasicIO is enabled. + /// Determines whether to use basic IO (legacy flag, kept for compatibility). + /// In the new architecture this is true when Backend is BasicConsoleBackend. /// public static bool BasicIO = false; @@ -56,10 +62,15 @@ namespace MinecraftClient /// public static bool EnableTimestamps = false; + /// + /// Determine whether chat lines should be displayed in the console. + /// + public static bool ChatVisible = true; + /// /// Specify a generic log line prefix for WriteLogLine() /// - public static string LogPrefix = "§8[Log] "; + public static string LogPrefix = "§8[MCC] "; /// /// Read a password from the standard input @@ -68,13 +79,7 @@ namespace MinecraftClient { if (BasicIO) return Console.ReadLine(); - else - { - ConsoleInteractive.ConsoleReader.SetInputVisible(false); - var input = ConsoleInteractive.ConsoleReader.RequestImmediateInput(); - ConsoleInteractive.ConsoleReader.SetInputVisible(true); - return input; - } + return Backend.ReadPassword(); } /// @@ -84,8 +89,7 @@ namespace MinecraftClient { if (BasicIO) return Console.ReadLine() ?? String.Empty; - else - return ConsoleInteractive.ConsoleReader.RequestImmediateInput(); + return Backend.RequestImmediateInput(); } /// @@ -106,10 +110,10 @@ namespace MinecraftClient /// public static void WriteLine(string line) { - if (BasicIO) + if (BasicIO || Backend is null) Console.WriteLine(line); else - ConsoleInteractive.ConsoleWriter.WriteLine(line); + Backend.WriteLine(line); } /// @@ -139,7 +143,7 @@ namespace MinecraftClient { str = str.Replace('\n', ' '); } - if (BasicIO) + if (BasicIO || Backend is null) { if (BasicIO_NoColor) { @@ -153,10 +157,21 @@ namespace MinecraftClient return; } output.Append(str); - ConsoleInteractive.ConsoleWriter.WriteLineFormatted(output.ToString()); + Backend.WriteLineFormatted(output.ToString()); } } + /// + /// Write a formatted chat line to the console when chat output is enabled. + /// + public static void WriteChatLineIfVisible(string str, bool acceptnewlines = false, bool? displayTimestamp = null) + { + if (!ChatVisible) + return; + + WriteLineFormatted(str, acceptnewlines, displayTimestamp); + } + /// /// Write a prefixed log line. Prefix is set in LogPrefix. /// @@ -177,9 +192,34 @@ namespace MinecraftClient private static void ClearLineAndBuffer() { if (BasicIO) return; - ConsoleInteractive.ConsoleReader.ClearBuffer(); + Backend.ClearInputBuffer(); } + /// + /// Clear the visible console output. + /// + public static void ClearConsole() + { + if (BasicIO || Backend is null) + { + try + { + Console.Clear(); + } + catch (IOException ex) + { + System.Diagnostics.Debug.WriteLine(ex); + } + catch (PlatformNotSupportedException ex) + { + System.Diagnostics.Debug.WriteLine(ex); + } + + return; + } + + Backend.ClearScreen(); + } #endregion @@ -193,12 +233,37 @@ namespace MinecraftClient private static Task _latestTask = Task.CompletedTask; private static CancellationTokenSource? _cancellationTokenSource; - private static void MccAutocompleteHandler(ConsoleInteractive.ConsoleReader.Buffer buffer) + private static void SendSuggestions( + ConsoleInteractive.ConsoleSuggestion.Suggestion[] classicSugs, + Tuple range) + { + if (Backend is ClassicConsoleBackend classic) + { + classic.UpdateSuggestions(classicSugs, range); + } + else if (Backend is TuiConsoleBackend tui) + { + var tuiSugs = new CommandSuggestion[classicSugs.Length]; + for (int i = 0; i < classicSugs.Length; i++) + tuiSugs[i] = new CommandSuggestion(classicSugs[i].Text, classicSugs[i].Tooltip); + tui.UpdateSuggestions(tuiSugs, (range.Item1, range.Item2)); + } + } + + private static void DoClearSuggestions() + { + if (Backend is ClassicConsoleBackend classic) + classic.ClearSuggestions(); + else if (Backend is TuiConsoleBackend tui) + tui.ClearSuggestions(); + } + + private static void MccAutocompleteHandler(ConsoleInputBuffer buffer) { string fullCommand = buffer.Text; if (string.IsNullOrEmpty(fullCommand)) { - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + DoClearSuggestions(); return; } @@ -208,7 +273,7 @@ namespace MinecraftClient int offset = InternalCmdChar == MainConfigHelper.MainConfig.AdvancedConfig.InternalCmdCharType.none ? 0 : 1; if (buffer.CursorPosition - offset < 0) { - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + DoClearSuggestions(); return; } _cancellationTokenSource?.Cancel(); @@ -225,14 +290,14 @@ namespace MinecraftClient sugList.Add(new("/")); var childs = McClient.dispatcher.GetRoot().Children; - if (childs != null) + if (childs is not null) foreach (var child in childs) sugList.Add(new(child.Name)); foreach (var cmd in Commands) sugList.Add(new(cmd)); - ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList.ToArray(), new(offset, offset)); + SendSuggestions(sugList.ToArray(), new(offset, offset)); } else if (command.Length > 0 && command[0] == '/' && !command.Contains(' ')) { @@ -242,12 +307,12 @@ namespace MinecraftClient int index = 0; foreach (var sug in sorted) sugList[index++] = new(sug.Value); - ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset + command.Length)); + SendSuggestions(sugList, new(offset, offset + command.Length)); } else { CommandDispatcher? dispatcher = McClient.dispatcher; - if (dispatcher == null) + if (dispatcher is null) return; ParseResults parse = dispatcher.Parse(command, CmdResult.Empty); @@ -257,7 +322,7 @@ namespace MinecraftClient int sugLen = suggestions.List.Count; if (sugLen == 0) { - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + DoClearSuggestions(); return; } @@ -278,7 +343,7 @@ namespace MinecraftClient foreach (var sug in sorted) sugList[index++] = new(sug.Value, dictionary[sug.Value] ?? string.Empty); - ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, range); + SendSuggestions(sugList, range); } }, cts.Token); _latestTask = newTask; @@ -287,22 +352,68 @@ namespace MinecraftClient } else { - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + DoClearSuggestions(); return; } } - public static void AutocompleteHandler(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer) + public static void AutocompleteHandler(object? sender, ConsoleInputBuffer buffer) { if (Settings.Config.Console.CommandSuggestion.Enable) MccAutocompleteHandler(buffer); } + private static readonly string[] OfflineCommands = ["quit", "exit", "connect", "reco", "help"]; + + public static void OfflineAutocompleteHandler(object? sender, ConsoleInputBuffer buffer) + { + if (!Settings.Config.Console.CommandSuggestion.Enable) + return; + + string fullCommand = buffer.Text; + if (string.IsNullOrEmpty(fullCommand)) + { + DoClearSuggestions(); + return; + } + + var InternalCmdChar = Config.Main.Advanced.InternalCmdChar; + int offset = 0; + if (InternalCmdChar != MainConfigHelper.MainConfig.AdvancedConfig.InternalCmdCharType.none) + { + if (fullCommand[0] != InternalCmdChar.ToChar()) + { + DoClearSuggestions(); + return; + } + offset = 1; + } + + string command = fullCommand[offset..]; + if (command.Contains(' ')) + { + DoClearSuggestions(); + return; + } + + var sugList = new List(); + foreach (string cmd in OfflineCommands) + { + if (command.Length == 0 || cmd.StartsWith(command, StringComparison.OrdinalIgnoreCase)) + sugList.Add(new(cmd)); + } + + if (sugList.Count > 0) + SendSuggestions(sugList.ToArray(), new(offset, offset + command.Length)); + else + DoClearSuggestions(); + } + public static void CancelAutocomplete() { _cancellationTokenSource?.Cancel(); _latestTask = Task.CompletedTask; - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + DoClearSuggestions(); AutoCompleteDone = false; AutoCompleteResult = Array.Empty(); diff --git a/MinecraftClient/Crypto/AesCfb8Stream.cs b/MinecraftClient/Crypto/AesCfb8Stream.cs index dfa2dd78..b60eb845 100644 --- a/MinecraftClient/Crypto/AesCfb8Stream.cs +++ b/MinecraftClient/Crypto/AesCfb8Stream.cs @@ -89,7 +89,7 @@ namespace MinecraftClient.Crypto } Span blockOutput = stackalloc byte[blockSize]; - if (FastAes != null) + if (FastAes is not null) FastAes.EncryptEcb(ReadStreamIV, blockOutput); else Aes!.EncryptEcb(ReadStreamIV, blockOutput, PaddingMode.None); @@ -122,7 +122,7 @@ namespace MinecraftClient.Crypto } int processEnd = readed + curRead; - if (FastAes != null) + if (FastAes is not null) { for (int idx = readed; idx < processEnd; idx++) { @@ -161,7 +161,7 @@ namespace MinecraftClient.Crypto { Span blockOutput = stackalloc byte[blockSize]; - if (FastAes != null) + if (FastAes is not null) FastAes.EncryptEcb(WriteStreamIV, blockOutput); else Aes!.EncryptEcb(WriteStreamIV, blockOutput, PaddingMode.None); @@ -185,7 +185,7 @@ namespace MinecraftClient.Crypto for (int wirtten = 0; wirtten < required; ++wirtten) { ReadOnlySpan blockInput = new(outputBuf, wirtten, blockSize); - if (FastAes != null) + if (FastAes is not null) FastAes.EncryptEcb(blockInput, blockOutput); else Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None); diff --git a/MinecraftClient/Crypto/FastAes.cs b/MinecraftClient/Crypto/FastAes.cs index 41de7d54..e423caab 100644 --- a/MinecraftClient/Crypto/FastAes.cs +++ b/MinecraftClient/Crypto/FastAes.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -63,16 +63,16 @@ namespace MinecraftClient.Crypto keys[0] = Unsafe.ReadUnaligned>(ref key[0]); - MakeRoundKey(keys, 1, 0x01); - MakeRoundKey(keys, 2, 0x02); - MakeRoundKey(keys, 3, 0x04); - MakeRoundKey(keys, 4, 0x08); - MakeRoundKey(keys, 5, 0x10); - MakeRoundKey(keys, 6, 0x20); - MakeRoundKey(keys, 7, 0x40); - MakeRoundKey(keys, 8, 0x80); - MakeRoundKey(keys, 9, 0x1b); - MakeRoundKey(keys, 10, 0x36); + ExpandRound(keys, 1, Aes.KeygenAssist(keys[0], 0x01)); + ExpandRound(keys, 2, Aes.KeygenAssist(keys[1], 0x02)); + ExpandRound(keys, 3, Aes.KeygenAssist(keys[2], 0x04)); + ExpandRound(keys, 4, Aes.KeygenAssist(keys[3], 0x08)); + ExpandRound(keys, 5, Aes.KeygenAssist(keys[4], 0x10)); + ExpandRound(keys, 6, Aes.KeygenAssist(keys[5], 0x20)); + ExpandRound(keys, 7, Aes.KeygenAssist(keys[6], 0x40)); + ExpandRound(keys, 8, Aes.KeygenAssist(keys[7], 0x80)); + ExpandRound(keys, 9, Aes.KeygenAssist(keys[8], 0x1b)); + ExpandRound(keys, 10, Aes.KeygenAssist(keys[9], 0x36)); for (int i = 1; i < 10; i++) { @@ -82,13 +82,11 @@ namespace MinecraftClient.Crypto return keys; } - private static void MakeRoundKey(Vector128[] keys, int i, byte rcon) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ExpandRound(Vector128[] keys, int i, Vector128 assist) { Vector128 s = keys[i - 1]; - Vector128 t = keys[i - 1]; - - t = Aes.KeygenAssist(t, rcon); - t = Sse2.Shuffle(t.AsUInt32(), 0xFF).AsByte(); + Vector128 t = Sse2.Shuffle(assist.AsUInt32(), 0xFF).AsByte(); s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 4)); s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 8)); diff --git a/MinecraftClient/Dialogs/DialogFormatter.cs b/MinecraftClient/Dialogs/DialogFormatter.cs new file mode 100644 index 00000000..9e836e3e --- /dev/null +++ b/MinecraftClient/Dialogs/DialogFormatter.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Dialogs; + +public static class DialogFormatter +{ + public static string DisplayTitle(this DialogDefinition definition) + { + if (!string.IsNullOrWhiteSpace(definition.ExternalTitle)) + return definition.ExternalTitle!; + + return string.IsNullOrWhiteSpace(definition.Title) ? DisplayType(definition.Type) : definition.Title; + } + + private const int BoxWidth = 50; + + public static string Render(DialogInstance instance) + { + StringBuilder builder = new(); + string border = new('-', BoxWidth); + builder.AppendLine(border); + builder.AppendLine(" " + string.Format(Translations.dialog_render_header, instance.Revision, instance.Phase, instance.Definition.DisplayTitle())); + builder.AppendLine(border); + + foreach (var body in instance.Definition.Body.Where(static body => !string.IsNullOrWhiteSpace(body.Text))) + builder.AppendLine(body.Text); + + if (instance.Definition.Inputs.Count > 0) + { + builder.AppendLine(Translations.dialog_render_inputs); + foreach (var input in instance.Definition.Inputs) + { + instance.Values.TryGetValue(input.Key, out var value); + value ??= input.InitialValue; + builder.AppendLine(string.Format(Translations.dialog_render_input, input.Key, DescribeKind(input.Kind), input.Label, value, DescribeInput(input))); + } + } + + if (instance.Definition.Actions.Count > 0) + { + builder.AppendLine(Translations.dialog_render_actions); + foreach (var action in instance.Definition.Actions) + builder.AppendLine(string.Format(Translations.dialog_render_action, action.Index, action.Label, DescribeAction(action.Action))); + } + + builder.AppendLine(); + builder.AppendLine("§o" + Translations.dialog_render_help_hint + "§r"); + builder.Append(border); + return builder.ToString(); + } + + public static string DisplayType(string rawType) + { + return rawType switch + { + "minecraft:notice" => Translations.dialog_type_notice, + "minecraft:confirmation" => Translations.dialog_type_confirmation, + "minecraft:multi_action" => Translations.dialog_type_multi_action, + "minecraft:dialog_list" => Translations.dialog_type_dialog_list, + "minecraft:server_links" => Translations.dialog_type_server_links, + _ => string.IsNullOrEmpty(rawType) ? Translations.dialog_type_unknown : rawType + }; + } + + private static string DescribeKind(DialogInputKind kind) + { + return kind switch + { + DialogInputKind.Text => Translations.dialog_input_kind_text, + DialogInputKind.Boolean => Translations.dialog_input_kind_boolean, + DialogInputKind.SingleOption => Translations.dialog_input_kind_options, + DialogInputKind.NumberRange => Translations.dialog_input_kind_number, + _ => Translations.dialog_input_kind_unknown + }; + } + + private static string DescribeInput(DialogInput input) + { + return input.Kind switch + { + DialogInputKind.Text => string.Format(Translations.dialog_input_desc_text, input.MaxLength), + DialogInputKind.Boolean => string.Format(Translations.dialog_input_desc_boolean, input.OnTrue, input.OnFalse), + DialogInputKind.SingleOption => string.Format(Translations.dialog_input_desc_options, + string.Join(", ", input.Options?.Select(static option => option.Id) ?? [])), + DialogInputKind.NumberRange => string.Format(Translations.dialog_input_desc_number, input.Start, input.End), + _ => input.Type ?? Translations.dialog_input_desc_unknown + }; + } + + private static string DescribeAction(DialogActionDefinition? action) + { + if (action is null) + return Translations.dialog_action_desc_close; + + return action.Kind switch + { + DialogActionKind.RunCommand => Translations.dialog_action_desc_command, + DialogActionKind.CustomClick => Translations.dialog_action_desc_custom, + DialogActionKind.ShowDialog => Translations.dialog_action_desc_show_dialog, + DialogActionKind.OpenUrl => Translations.dialog_action_desc_open_url, + DialogActionKind.SuggestCommand => Translations.dialog_action_desc_suggest, + DialogActionKind.CopyToClipboard => Translations.dialog_action_desc_copy, + _ => action.Type ?? Translations.dialog_action_desc_unknown + }; + } +} diff --git a/MinecraftClient/Dialogs/DialogManager.cs b/MinecraftClient/Dialogs/DialogManager.cs new file mode 100644 index 00000000..40876b61 --- /dev/null +++ b/MinecraftClient/Dialogs/DialogManager.cs @@ -0,0 +1,445 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; + +namespace MinecraftClient.Dialogs; + +public sealed class DialogManager +{ + private readonly McClient _client; + private readonly Lock _lock = new(); + private readonly Dictionary _registryById = new(); + private readonly Dictionary _registryByName = new(StringComparer.Ordinal); + private readonly List _serverLinks = []; + private DialogInstance? _current; + private int _revision; + + public DialogManager(McClient client) + { + ArgumentNullException.ThrowIfNull(client); + _client = client; + } + + public event Action? DialogShown; + public event Action? DialogCleared; + + public DialogInstance? Current + { + get + { + lock (_lock) + return _current; + } + } + + public void StoreRegistryDialog(int protocolId, string resourceId, DialogDefinition definition) + { + lock (_lock) + { + _registryById[protocolId] = definition; + _registryByName[resourceId] = definition; + } + } + + public void ClearRegistry() + { + lock (_lock) + { + _registryById.Clear(); + _registryByName.Clear(); + } + } + + public void SetServerLinks(IEnumerable links) + { + lock (_lock) + { + _serverLinks.Clear(); + _serverLinks.AddRange(links); + } + } + + public DialogInstance Show(DialogDefinition definition, DialogPhase phase) + { + DialogInstance instance; + lock (_lock) + { + var expanded = ExpandServerLinks(definition); + var values = expanded.Inputs.ToDictionary(static input => input.Key, static input => input.InitialValue, StringComparer.Ordinal); + instance = new DialogInstance(++_revision, phase, expanded, values, DateTimeOffset.UtcNow); + _current = instance; + } + + _client.Log.Info("§e" + string.Format(Translations.dialog_received, instance.Definition.DisplayTitle())); + DialogShown?.Invoke(instance); + return instance; + } + + public DialogInstance ShowRegistryReference(int protocolId, DialogPhase phase) + { + DialogDefinition? definition; + lock (_lock) + _registryById.TryGetValue(protocolId, out definition); + + if (definition is not null) + return Show(definition, phase); + + var unresolved = new DialogDefinition( + "minecraft:unresolved", + string.Format(CultureInfo.InvariantCulture, Translations.dialog_unresolved_title, protocolId), + null, + CanCloseWithEscape: true, + Pause: false, + DialogAfterAction.Close, + [new DialogBody(DialogBodyKind.Unknown, string.Format(CultureInfo.InvariantCulture, Translations.dialog_unresolved_body, protocolId))], + [], + [], + null, + IsResolved: false, + UnresolvedReference: protocolId.ToString(CultureInfo.InvariantCulture)); + return Show(unresolved, phase); + } + + public void Clear() + { + int revision; + lock (_lock) + { + revision = _current?.Revision ?? _revision; + _current = null; + } + + _client.Log.Info(Translations.dialog_cleared); + DialogCleared?.Invoke(revision); + } + + public DialogActionResult Dismiss() + { + int revision; + lock (_lock) + { + if (_current is null) + return new DialogActionResult(false, Translations.dialog_none); + + revision = _current.Revision; + _current = null; + } + + DialogCleared?.Invoke(revision); + return new DialogActionResult(true, Translations.dialog_dismissed); + } + + public DialogActionResult SetInput(string key, string value) + { + lock (_lock) + { + if (_current is null) + return new DialogActionResult(false, Translations.dialog_none); + + var input = _current.Definition.Inputs.FirstOrDefault(input => input.Key.Equals(key, StringComparison.Ordinal)); + if (input is null) + return new DialogActionResult(false, string.Format(Translations.dialog_input_unknown, key)); + + var normalized = NormalizeInputValue(input, value, out var error); + if (error is not null) + return new DialogActionResult(false, error); + + var values = _current.Values.ToDictionary(static pair => pair.Key, static pair => pair.Value, StringComparer.Ordinal); + values[key] = normalized; + _current = _current with { Values = values }; + return new DialogActionResult(true, string.Format(Translations.dialog_input_set, key, normalized)); + } + } + + public DialogActionResult Click(int index) + { + DialogButton? button; + DialogInstance? instance; + lock (_lock) + { + instance = _current; + button = instance?.Definition.Actions.FirstOrDefault(action => action.Index == index); + } + + if (instance is null) + return new DialogActionResult(false, Translations.dialog_none); + + if (button is null) + return new DialogActionResult(false, string.Format(Translations.dialog_action_unknown, index)); + + return Execute(instance, button.Action, ShouldCloseAfterAction(instance.Definition.AfterAction)); + } + + public DialogActionResult ClickLabel(string label) + { + DialogButton[] matches; + DialogInstance? instance; + lock (_lock) + { + instance = _current; + matches = instance?.Definition.Actions + .Where(action => action.Label.Equals(label, StringComparison.OrdinalIgnoreCase)) + .ToArray() ?? []; + } + + if (instance is null) + return new DialogActionResult(false, Translations.dialog_none); + + return matches.Length switch + { + 0 => new DialogActionResult(false, string.Format(Translations.dialog_action_label_unknown, label)), + > 1 => new DialogActionResult(false, string.Format(Translations.dialog_action_label_ambiguous, label)), + _ => Execute(instance, matches[0].Action, ShouldCloseAfterAction(instance.Definition.AfterAction)) + }; + } + + public DialogActionResult Cancel() + { + DialogInstance? instance; + lock (_lock) + instance = _current; + + if (instance is null) + return new DialogActionResult(false, Translations.dialog_none); + + if (!instance.Definition.CanCloseWithEscape && instance.Definition.CancelAction is null) + return new DialogActionResult(false, Translations.dialog_cannot_cancel); + + return Execute(instance, instance.Definition.CancelAction, closeWhenDone: true); + } + + private DialogActionResult Execute(DialogInstance instance, DialogActionDefinition? action, bool closeWhenDone) + { + if (!instance.Definition.IsResolved) + return new DialogActionResult(false, Translations.dialog_unresolved_action_disabled); + + if (action is null || action.Kind == DialogActionKind.None) + { + if (closeWhenDone) + _ = Dismiss(); + return new DialogActionResult(true, Translations.dialog_action_closed); + } + + var values = BuildActionValues(instance); + switch (action.Kind) + { + case DialogActionKind.RunCommand: + if (instance.Phase != DialogPhase.Play) + return new DialogActionResult(false, Translations.dialog_action_command_not_in_play); + + var command = ApplyTemplate(action.Value ?? string.Empty, values.TemplateValues); + _client.SendText(command); + if (closeWhenDone) + _ = Dismiss(); + return new DialogActionResult(true, string.Format(Translations.dialog_action_command_sent, command)); + + case DialogActionKind.CustomClick: + if (action.Id is null) + return new DialogActionResult(false, Translations.dialog_action_invalid); + + var payload = action.Type == "minecraft:custom" && action.Payload is null && values.TagValues.Count == 0 + ? null + : MergePayload(action.Payload, values.TagValues); + if (!_client.SendCustomClickAction(action.Id, payload)) + return new DialogActionResult(false, Translations.dialog_action_custom_failed); + + if (closeWhenDone) + _ = Dismiss(); + return new DialogActionResult(true, string.Format(Translations.dialog_action_custom_sent, action.Id)); + + case DialogActionKind.ShowDialog: + if (action.NestedDialog is not null) + { + Show(action.NestedDialog, instance.Phase); + return new DialogActionResult(true, Translations.dialog_action_nested_opened); + } + + if (action.DialogReferenceId is int referenceId) + { + ShowRegistryReference(referenceId, instance.Phase); + return new DialogActionResult(true, Translations.dialog_action_nested_opened); + } + + if (action.Value is not null) + { + DialogDefinition? referencedDialog; + lock (_lock) + _registryByName.TryGetValue(action.Value, out referencedDialog); + + if (referencedDialog is not null) + { + Show(referencedDialog, instance.Phase); + return new DialogActionResult(true, Translations.dialog_action_nested_opened); + } + } + + return new DialogActionResult(false, Translations.dialog_action_invalid); + + case DialogActionKind.OpenUrl: + return new DialogActionResult(true, string.Format(Translations.dialog_action_open_url, action.Value ?? string.Empty)); + + case DialogActionKind.SuggestCommand: + return new DialogActionResult(true, string.Format(Translations.dialog_action_suggest_command, action.Value ?? string.Empty)); + + case DialogActionKind.CopyToClipboard: + return new DialogActionResult(true, string.Format(Translations.dialog_action_copy, action.Value ?? string.Empty)); + + default: + return new DialogActionResult(false, string.Format(Translations.dialog_action_unsupported, action.Type ?? action.Kind.ToString())); + } + } + + private static bool ShouldCloseAfterAction(DialogAfterAction afterAction) + { + return afterAction == DialogAfterAction.Close; + } + + private DialogDefinition ExpandServerLinks(DialogDefinition definition) + { + if (!definition.Type.Equals("minecraft:server_links", StringComparison.Ordinal)) + return definition; + + var linkActions = _serverLinks + .Select((link, index) => new DialogButton( + index + 1, + link.Label, + new DialogActionDefinition(DialogActionKind.OpenUrl, Value: link.Url))) + .ToList(); + + if (definition.Actions.Count > 0) + linkActions.AddRange(definition.Actions.Select((button, i) => button with { Index = linkActions.Count + i + 1 })); + + return definition with { Actions = linkActions }; + } + + private static DialogActionValues BuildActionValues(DialogInstance instance) + { + Dictionary templateValues = new(StringComparer.Ordinal); + Dictionary tagValues = new(StringComparer.Ordinal); + + foreach (var input in instance.Definition.Inputs) + { + instance.Values.TryGetValue(input.Key, out var value); + value ??= input.InitialValue; + templateValues[input.Key] = ToTemplateValue(input, value); + tagValues[input.Key] = ToNbtValue(input, value); + } + + return new DialogActionValues(templateValues, tagValues); + } + + private static Dictionary MergePayload(Dictionary? basePayload, Dictionary inputTags) + { + Dictionary payload = basePayload is null + ? new(StringComparer.Ordinal) + : new(basePayload, StringComparer.Ordinal); + + foreach (var (key, value) in inputTags) + payload[key] = value; + + return payload; + } + + private static string NormalizeInputValue(DialogInput input, string value, out string? error) + { + error = null; + switch (input.Kind) + { + case DialogInputKind.Text: + if (value.Length > input.MaxLength) + { + error = string.Format(Translations.dialog_input_too_long, input.Key, input.MaxLength); + return input.InitialValue; + } + return value; + + case DialogInputKind.Boolean: + if (bool.TryParse(value, out var boolValue)) + return boolValue ? "true" : "false"; + + if (value.Equals(input.OnTrue, StringComparison.OrdinalIgnoreCase)) + return "true"; + + if (value.Equals(input.OnFalse, StringComparison.OrdinalIgnoreCase)) + return "false"; + + error = string.Format(Translations.dialog_input_boolean_invalid, input.Key); + return input.InitialValue; + + case DialogInputKind.SingleOption: + if (input.Options?.Any(option => option.Id.Equals(value, StringComparison.Ordinal)) == true) + return value; + + error = string.Format(Translations.dialog_input_option_invalid, input.Key); + return input.InitialValue; + + case DialogInputKind.NumberRange: + if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var number)) + { + error = string.Format(Translations.dialog_input_number_invalid, input.Key); + return input.InitialValue; + } + + var min = Math.Min(input.Start, input.End); + var max = Math.Max(input.Start, input.End); + if (number < min || number > max) + { + error = string.Format(CultureInfo.InvariantCulture, Translations.dialog_input_number_range_invalid, input.Key, min, max); + return input.InitialValue; + } + + return NumberToString(number); + + default: + return value; + } + } + + private static string ToTemplateValue(DialogInput input, string value) + { + return input.Kind switch + { + DialogInputKind.Boolean => value.Equals("true", StringComparison.OrdinalIgnoreCase) ? input.OnTrue : input.OnFalse, + DialogInputKind.Text => EscapeStringTagWithoutQuotes(value), + _ => value + }; + } + + private static object ToNbtValue(DialogInput input, string value) + { + return input.Kind switch + { + DialogInputKind.Boolean => (byte)(value.Equals("true", StringComparison.OrdinalIgnoreCase) ? 1 : 0), + DialogInputKind.NumberRange when float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var number) => number, + _ => value + }; + } + + private static string ApplyTemplate(string template, IReadOnlyDictionary values) + { + var result = template; + foreach (var (key, value) in values) + result = result.Replace("$(" + key + ")", value, StringComparison.Ordinal); + + return result; + } + + private static string EscapeStringTagWithoutQuotes(string value) + { + return value.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("\"", "\\\"", StringComparison.Ordinal); + } + + private static string NumberToString(float value) + { + var integer = (int)value; + return integer == value + ? integer.ToString(CultureInfo.InvariantCulture) + : value.ToString(CultureInfo.InvariantCulture); + } + + private sealed record DialogActionValues( + IReadOnlyDictionary TemplateValues, + Dictionary TagValues); +} diff --git a/MinecraftClient/Dialogs/DialogModels.cs b/MinecraftClient/Dialogs/DialogModels.cs new file mode 100644 index 00000000..fc40b480 --- /dev/null +++ b/MinecraftClient/Dialogs/DialogModels.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient.Dialogs; + +public enum DialogPhase +{ + Configuration, + Play +} + +public enum DialogAfterAction +{ + Close, + None, + WaitForResponse +} + +public enum DialogBodyKind +{ + PlainMessage, + Item, + Unknown +} + +public enum DialogInputKind +{ + Text, + Boolean, + SingleOption, + NumberRange, + Unknown +} + +public enum DialogActionKind +{ + None, + RunCommand, + CustomClick, + ShowDialog, + OpenUrl, + SuggestCommand, + CopyToClipboard, + Unknown +} + +public sealed record DialogBody(DialogBodyKind Kind, string Text, string? Type = null); + +public sealed record DialogOption(string Id, string Display, bool Initial) +{ + public override string ToString() + { + return string.IsNullOrWhiteSpace(Display) ? Id : Display; + } +} + +public sealed record DialogInput( + string Key, + DialogInputKind Kind, + string Label, + string InitialValue, + int MaxLength = 32, + bool LabelVisible = true, + bool Multiline = false, + IReadOnlyList? Options = null, + string OnTrue = "true", + string OnFalse = "false", + float Start = 0, + float End = 1, + float? InitialNumber = null, + float? Step = null, + string? Type = null); + +public sealed record DialogActionDefinition( + DialogActionKind Kind, + string? Value = null, + string? Id = null, + Dictionary? Payload = null, + DialogDefinition? NestedDialog = null, + int? DialogReferenceId = null, + string? Type = null); + +public sealed record DialogButton(int Index, string Label, DialogActionDefinition? Action, bool IsCancel = false); + +public sealed record DialogServerLink(string Label, string Url); + +public sealed record DialogDefinition( + string Type, + string Title, + string? ExternalTitle, + bool CanCloseWithEscape, + bool Pause, + DialogAfterAction AfterAction, + IReadOnlyList Body, + IReadOnlyList Inputs, + IReadOnlyList Actions, + DialogActionDefinition? CancelAction, + int Columns = 1, + int ButtonWidth = 150, + bool IsResolved = true, + string? UnresolvedReference = null); + +public sealed record DialogInstance( + int Revision, + DialogPhase Phase, + DialogDefinition Definition, + IReadOnlyDictionary Values, + DateTimeOffset ReceivedAt); + +public sealed record DialogActionResult(bool Success, string Message); diff --git a/MinecraftClient/FileMonitor.cs b/MinecraftClient/FileMonitor.cs index 36e89ec1..16f590a4 100644 --- a/MinecraftClient/FileMonitor.cs +++ b/MinecraftClient/FileMonitor.cs @@ -59,9 +59,9 @@ namespace MinecraftClient /// public void Dispose() { - if (monitor != null) + if (monitor is not null) monitor.Item1.Dispose(); - if (polling != null) + if (polling is not null) polling.Item2.Cancel(); } diff --git a/MinecraftClient/IConsoleBackend.cs b/MinecraftClient/IConsoleBackend.cs new file mode 100644 index 00000000..0fd23389 --- /dev/null +++ b/MinecraftClient/IConsoleBackend.cs @@ -0,0 +1,73 @@ +using System; + +namespace MinecraftClient +{ + /// + /// Input buffer state passed with OnInputChange events. + /// + public readonly struct ConsoleInputBuffer + { + public string Text { get; } + public int CursorPosition { get; } + + public ConsoleInputBuffer(string text, int cursorPosition) + { + Text = text; + CursorPosition = cursorPosition; + } + } + + /// + /// Backend-independent suggestion item used by the TUI autocomplete popup. + /// Mirrors the shape of ConsoleInteractive.ConsoleSuggestion.Suggestion + /// without requiring a dependency on the ConsoleInteractive assembly. + /// + public readonly struct CommandSuggestion + { + public string Text { get; } + public string Tooltip { get; } + + public CommandSuggestion(string text, string tooltip = "") + { + Text = text; + Tooltip = tooltip; + } + } + + /// + /// Abstraction over the console I/O backend. + /// Implementations: ClassicConsoleBackend (ConsoleInteractive), TuiConsoleBackend (Avalonia/Consolonia), BasicConsoleBackend (stdio). + /// + public interface IConsoleBackend + { + void Init(); + + void WriteLine(string text); + + void WriteLineFormatted(string text); + + void BeginReadThread(); + + void StopReadThread(); + + event EventHandler? MessageReceived; + + event EventHandler? OnInputChange; + + string RequestImmediateInput(); + + string? ReadPassword(); + + void ClearInputBuffer(); + + void ClearScreen(); + + bool DisplayUserInput { get; set; } + + void SetInputVisible(bool visible); + + void SetBackreadBufferLimit(int limit); + + void Shutdown(); + } +} diff --git a/MinecraftClient/Inventory/BookContent.cs b/MinecraftClient/Inventory/BookContent.cs new file mode 100644 index 00000000..262e4680 --- /dev/null +++ b/MinecraftClient/Inventory/BookContent.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Inventory; + +public enum BookHand +{ + Main = 0, + Off = 1 +} + +public sealed record BookLimits(int MaxPages, int MaxPageLength, int MaxTitleLength) +{ + public static BookLimits ForProtocol(int protocolVersion) + { + int maxPageLength = protocolVersion switch + { + >= Protocol18Handler.MC_1_21_2_Version => 1024, + >= Protocol18Handler.MC_1_17_Version => 8192, + _ => 32767 + }; + + int maxTitleLength = protocolVersion switch + { + >= Protocol18Handler.MC_1_21_2_Version => 32, + >= Protocol18Handler.MC_1_17_Version => 128, + _ => 16 + }; + + return new BookLimits(100, maxPageLength, maxTitleLength); + } +} + +public sealed record BookContent( + IReadOnlyList Pages, + string? Title, + string? Author, + int Generation, + bool IsSigned) +{ + public static BookContent EmptyWritable { get; } = new([string.Empty], null, null, 0, false); +} + +public static class BookContentHelper +{ + public static bool IsBook(Item? item) => item?.Type is ItemType.WritableBook or ItemType.WrittenBook; + + public static bool IsWritableBook(Item? item) => item?.Type == ItemType.WritableBook; + + public static bool TryRead(Item? item, out BookContent content) + { + content = BookContent.EmptyWritable; + + if (item is null || item.IsEmpty) + return false; + + return item.Type switch + { + ItemType.WritableBook => TryReadWritable(item, out content), + ItemType.WrittenBook => TryReadWritten(item, out content), + _ => false + }; + } + + public static Item CreateWritablePayload(Item currentBook, IReadOnlyList pages) + { + return new Item(ItemType.WritableBook, 1, currentBook.Data, new Dictionary + { + ["pages"] = pages.Cast().ToArray() + }); + } + + public static Item CreateWrittenPayload(Item currentBook, IReadOnlyList pages, string title, string author, bool encodePagesAsJson) + { + object[] encodedPages = pages + .Select(page => encodePagesAsJson ? ToJsonTextComponent(page) : page) + .Cast() + .ToArray(); + + return new Item(ItemType.WrittenBook, 1, currentBook.Data, new Dictionary + { + ["author"] = author, + ["title"] = title, + ["pages"] = encodedPages + }); + } + + public static IReadOnlyList NormalizePages(IEnumerable pages) + { + string[] normalized = pages.Select(page => page ?? string.Empty).ToArray(); + return normalized.Length == 0 ? [string.Empty] : normalized; + } + + private static bool TryReadWritable(Item item, out BookContent content) + { + if (item.Components is not null) + { + var component = item.Components.OfType().FirstOrDefault(); + if (component is not null) + { + content = new BookContent( + NormalizePages(component.Pages.Select(page => page.RawContent)), + null, + null, + 0, + IsSigned: false); + return true; + } + } + + content = new BookContent(ReadStringList(item.NBT, "pages", parseJson: false), null, null, 0, IsSigned: false); + return true; + } + + private static bool TryReadWritten(Item item, out BookContent content) + { + if (item.Components is not null) + { + var component = item.Components.OfType().FirstOrDefault(); + if (component is not null) + { + content = new BookContent( + NormalizePages(component.Pages.Select(page => page.RawContent)), + component.RawTitle, + component.Author, + component.Generation, + IsSigned: true); + return true; + } + } + + string? title = ReadString(item.NBT, "title"); + string? author = ReadString(item.NBT, "author"); + int generation = ReadInt(item.NBT, "generation"); + content = new BookContent(ReadStringList(item.NBT, "pages", parseJson: true), title, author, generation, IsSigned: true); + return true; + } + + private static IReadOnlyList ReadStringList(Dictionary? nbt, string key, bool parseJson) + { + if (nbt is null || !nbt.TryGetValue(key, out object? value) || value is not object[] values) + return [string.Empty]; + + string[] pages = values + .Select(value => value?.ToString() ?? string.Empty) + .Select(value => parseJson ? ChatParser.ParseText(value) : value) + .ToArray(); + + return pages.Length == 0 ? [string.Empty] : pages; + } + + private static string? ReadString(Dictionary? nbt, string key) + { + return nbt is not null && nbt.TryGetValue(key, out object? value) + ? value?.ToString() + : null; + } + + private static int ReadInt(Dictionary? nbt, string key) + { + if (nbt is null || !nbt.TryGetValue(key, out object? value) || value is null) + return 0; + + return value switch + { + int i => i, + short s => s, + byte b => b, + _ when int.TryParse(value.ToString(), out int parsed) => parsed, + _ => 0 + }; + } + + private static string ToJsonTextComponent(string text) + { + return JsonSerializer.Serialize(new Dictionary { ["text"] = text }); + } +} diff --git a/MinecraftClient/Inventory/BookPage.cs b/MinecraftClient/Inventory/BookPage.cs new file mode 100644 index 00000000..9240d77e --- /dev/null +++ b/MinecraftClient/Inventory/BookPage.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory; + +public record BookPage( + string RawContent, + bool HasFilteredContent, + string? FilteredContent, + Dictionary? RawContentNbt = null, + Dictionary? FilteredContentNbt = null); \ No newline at end of file diff --git a/MinecraftClient/Inventory/Container.cs b/MinecraftClient/Inventory/Container.cs index f1201275..f2258fee 100644 --- a/MinecraftClient/Inventory/Container.cs +++ b/MinecraftClient/Inventory/Container.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace MinecraftClient.Inventory { @@ -50,8 +50,8 @@ namespace MinecraftClient.Inventory ID = id; Type = type; Title = title; - Items = new Dictionary(); - Properties = new Dictionary(); + Items = new(); + Properties = new(); } /// @@ -67,7 +67,7 @@ namespace MinecraftClient.Inventory Type = type; Title = title; Items = items; - Properties = new Dictionary(); + Properties = new(); } /// @@ -81,8 +81,8 @@ namespace MinecraftClient.Inventory ID = id; Title = title; Type = ConvertType.ToNew(type); - Items = new Dictionary(); - Properties = new Dictionary(); + Items = new(); + Properties = new(); } /// @@ -91,13 +91,14 @@ namespace MinecraftClient.Inventory /// Container ID /// Container Type /// Container Title - public Container(int id, int typeID, string title) + /// Protocol version for version-specific mapping + public Container(int id, int typeID, string title, int protocolVersion = 0) { ID = id; - Type = GetContainerType(typeID); + Type = GetContainerType(typeID, protocolVersion); Title = title; - Items = new Dictionary(); - Properties = new Dictionary(); + Items = new(); + Properties = new(); } /// @@ -109,8 +110,8 @@ namespace MinecraftClient.Inventory ID = -1; Type = type; Title = null; - Items = new Dictionary(); - Properties = new Dictionary(); + Items = new(); + Properties = new(); } /// @@ -124,29 +125,69 @@ namespace MinecraftClient.Inventory Type = type; Title = null; Items = items; - Properties = new Dictionary(); + Properties = new(); } /// /// Get container type from Type ID /// /// Container Type ID + /// Protocol version (menu registry changed across versions) /// Container Type - public static ContainerType GetContainerType(int typeID) + public static ContainerType GetContainerType(int typeID, int protocolVersion = 0) { - // https://wiki.vg/Inventory didn't state the inventory ID, assume that list start with 0 + // MC 1.20.4 (protocol 765) added crafter_3x3 at index 7, shifting all subsequent IDs by +1. + // Registry order from decompiled MenuType.java: + // 1.14-1.20.2: generic_9x1..generic_3x3(6), anvil(7), beacon(8), ... stonecutter(22) + // 1.20.4+: generic_9x1..generic_3x3(6), crafter_3x3(7), anvil(8), beacon(9), ... stonecutter(24) + if (protocolVersion >= 765) + { + return typeID switch + { +#pragma warning disable format // @formatter:off + 0 => ContainerType.Generic_9x1, + 1 => ContainerType.Generic_9x2, + 2 => ContainerType.Generic_9x3, + 3 => ContainerType.Generic_9x4, + 4 => ContainerType.Generic_9x5, + 5 => ContainerType.Generic_9x6, + 6 => ContainerType.Generic_3x3, + 7 => ContainerType.Crafter, + 8 => ContainerType.Anvil, + 9 => ContainerType.Beacon, + 10 => ContainerType.BlastFurnace, + 11 => ContainerType.BrewingStand, + 12 => ContainerType.Crafting, + 13 => ContainerType.Enchantment, + 14 => ContainerType.Furnace, + 15 => ContainerType.Grindstone, + 16 => ContainerType.Hopper, + 17 => ContainerType.Lectern, + 18 => ContainerType.Loom, + 19 => ContainerType.Merchant, + 20 => ContainerType.ShulkerBox, + 21 => ContainerType.SmightingTable, + 22 => ContainerType.Smoker, + 23 => ContainerType.Cartography, + 24 => ContainerType.Stonecutter, + _ => ContainerType.Unknown, +#pragma warning restore format // @formatter:on + }; + } + return typeID switch { - 0 => ContainerType.Generic_9x1, - 1 => ContainerType.Generic_9x2, - 2 => ContainerType.Generic_9x3, - 3 => ContainerType.Generic_9x4, - 4 => ContainerType.Generic_9x5, - 5 => ContainerType.Generic_9x6, - 6 => ContainerType.Generic_3x3, - 7 => ContainerType.Anvil, - 8 => ContainerType.Beacon, - 9 => ContainerType.BlastFurnace, +#pragma warning disable format // @formatter:off + 0 => ContainerType.Generic_9x1, + 1 => ContainerType.Generic_9x2, + 2 => ContainerType.Generic_9x3, + 3 => ContainerType.Generic_9x4, + 4 => ContainerType.Generic_9x5, + 5 => ContainerType.Generic_9x6, + 6 => ContainerType.Generic_3x3, + 7 => ContainerType.Anvil, + 8 => ContainerType.Beacon, + 9 => ContainerType.BlastFurnace, 10 => ContainerType.BrewingStand, 11 => ContainerType.Crafting, 12 => ContainerType.Enchantment, @@ -160,7 +201,8 @@ namespace MinecraftClient.Inventory 20 => ContainerType.Smoker, 21 => ContainerType.Cartography, 22 => ContainerType.Stonecutter, - _ => ContainerType.Unknown, + _ => ContainerType.Unknown, +#pragma warning restore format // @formatter:on }; } @@ -172,7 +214,7 @@ namespace MinecraftClient.Inventory public int[] SearchItem(ItemType itemType) { List result = new(); - if (Items != null) + if (Items is not null) { foreach (var item in Items) { diff --git a/MinecraftClient/Inventory/ContainerType.cs b/MinecraftClient/Inventory/ContainerType.cs index 76d05416..e82878fe 100644 --- a/MinecraftClient/Inventory/ContainerType.cs +++ b/MinecraftClient/Inventory/ContainerType.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Inventory +namespace MinecraftClient.Inventory { // For MC 1.14 after ONLY public enum ContainerType @@ -10,6 +10,7 @@ Generic_9x5, Generic_9x6, Generic_3x3, + Crafter, Anvil, Beacon, BlastFurnace, diff --git a/MinecraftClient/Inventory/ContainerTypeExtensions.cs b/MinecraftClient/Inventory/ContainerTypeExtensions.cs index 4fe16373..4644e553 100644 --- a/MinecraftClient/Inventory/ContainerTypeExtensions.cs +++ b/MinecraftClient/Inventory/ContainerTypeExtensions.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Inventory +namespace MinecraftClient.Inventory { public static class ContainerTypeExtensions { @@ -13,9 +13,14 @@ { #pragma warning disable format // @formatter:off ContainerType.PlayerInventory => 46, + ContainerType.Generic_9x1 => 45, + ContainerType.Generic_9x2 => 54, ContainerType.Generic_9x3 => 63, + ContainerType.Generic_9x4 => 72, + ContainerType.Generic_9x5 => 81, ContainerType.Generic_9x6 => 90, ContainerType.Generic_3x3 => 45, + ContainerType.Crafter => 45, ContainerType.Crafting => 46, ContainerType.BlastFurnace => 39, ContainerType.Furnace => 39, @@ -27,6 +32,7 @@ ContainerType.Anvil => 39, ContainerType.Hopper => 41, ContainerType.ShulkerBox => 63, + ContainerType.SmightingTable => 39, ContainerType.Loom => 40, ContainerType.Stonecutter => 38, ContainerType.Lectern => 37, @@ -52,6 +58,7 @@ ContainerType.Generic_9x3 => AsciiArt.Container_Generic_9x3, ContainerType.Generic_9x6 => AsciiArt.Container_Generic_9x6, ContainerType.Generic_3x3 => AsciiArt.Container_Generic_3x3, + ContainerType.Crafter => AsciiArt.Container_Generic_3x3, ContainerType.Crafting => AsciiArt.Container_Crafting, ContainerType.BlastFurnace => AsciiArt.Container_Furnace, ContainerType.Furnace => AsciiArt.Container_Furnace, diff --git a/MinecraftClient/Inventory/EffectData.cs b/MinecraftClient/Inventory/EffectData.cs new file mode 100644 index 00000000..c13e4312 --- /dev/null +++ b/MinecraftClient/Inventory/EffectData.cs @@ -0,0 +1,194 @@ +namespace MinecraftClient.Inventory; + +using System; +using System.Collections.Generic; +using System.Linq; +using MinecraftClient.Protocol; +using MinecraftClient.Protocol.Message; + +/// +/// Represents an active status effect on an entity +/// +public class EffectData +{ + /// + /// The type of effect + /// + public Effects Effect { get; set; } + + /// + /// Effect amplifier (level - 1, e.g., 0 = level I, 1 = level II) + /// + public int Amplifier { get; set; } + + /// + /// Duration in ticks (20 ticks = 1 second). -1 for infinite. + /// + public int Duration { get; set; } + + /// + /// Effect flags (ambient, show particles, show icon) + /// + public byte Flags { get; set; } + + /// + /// Time when the effect was applied + /// + public DateTime StartTime { get; set; } + + public EffectData(Effects effect, int amplifier, int duration, byte flags) + { + Effect = effect; + Amplifier = amplifier; + Duration = duration; + Flags = flags; + StartTime = DateTime.UtcNow; + } + + /// + /// Check if this is an infinite duration effect + /// + public bool IsInfinite => Duration == -1 || Duration == int.MaxValue; + + /// + /// Check if the effect has expired + /// + public bool IsExpired + { + get + { + if (IsInfinite) return false; + return GetElapsedTicks() >= Duration; + } + } + + /// + /// Get remaining duration in ticks + /// + public int RemainingTicks + { + get + { + if (IsInfinite) return -1; + return Math.Max(0, Duration - GetElapsedTicks()); + } + } + + /// + /// Get remaining duration in seconds + /// + public int RemainingSeconds + { + get + { + if (IsInfinite) return -1; + return (RemainingTicks + 19) / 20; + } + } + + /// + /// Get the translated effect name from Minecraft translations + /// + public string GetTranslatedName() + { + var key = $"effect.minecraft.{Effect.ToString().ToUnderscoreCase()}"; + var translated = ChatParser.TranslateString(key); + return string.IsNullOrEmpty(translated) ? Effect.ToString() : translated; + } + + /// + /// Get the translated effect name with level when applicable + /// + public string GetDisplayName() + { + string translatedName = GetTranslatedName(); + if (Amplifier <= 0) + return translatedName; + + return string.Format(Translations.effect_name_with_amplifier, translatedName, + EnchantmentMapping.ConvertLevelToRomanNumbers(Amplifier + 1)); + } + + /// + /// Get the translated effect name prefixed with the best-fit indefinite article + /// + public string GetDisplayNameWithArticle() + { + string displayName = GetDisplayName(); + char? firstLetter = displayName + .TrimStart() + .FirstOrDefault(char.IsLetter); + + if (firstLetter is null) + return displayName; + + string article = "AEIOUaeiou".Contains(firstLetter.Value) + ? Translations.effect_article_an + : Translations.effect_article_a; + return $"{article} {displayName}"; + } + + /// + /// Get the configured short duration label for the remaining time + /// + public string GetRemainingDurationText() + { + return FormatShortDuration(RemainingSeconds); + } + + /// + /// Get the configured short duration label for the initial effect duration + /// + public string GetInitialDurationText() + { + if (IsInfinite) + return Translations.effect_duration_unlimited; + + int durationSeconds = (Duration + 19) / 20; + return FormatShortDuration(durationSeconds); + } + + /// + /// Format a duration for compact UI output + /// + /// Duration in seconds, -1 for unlimited + public static string FormatShortDuration(int seconds) + { + if (seconds < 0) + return Translations.effect_duration_short_unlimited; + + if (seconds < 60) + return string.Format(Translations.effect_duration_short_seconds, seconds); + + int minutes = seconds / 60; + int remainingSeconds = seconds % 60; + if (seconds < 3600) + { + return remainingSeconds == 0 + ? string.Format(Translations.effect_duration_short_minutes, minutes) + : string.Format(Translations.effect_duration_short_minutes_seconds, minutes, remainingSeconds); + } + + int hours = seconds / 3600; + int remainingMinutes = (seconds % 3600) / 60; + return remainingMinutes == 0 + ? string.Format(Translations.effect_duration_short_hours, hours) + : string.Format(Translations.effect_duration_short_hours_minutes, hours, remainingMinutes); + } + + private int GetElapsedTicks() + { + return (int)((DateTime.UtcNow - StartTime).TotalMilliseconds / 50); + } +} + +/// +/// Extension method for converting PascalCase to snake_case +/// +public static class StringExtensions +{ + public static string ToUnderscoreCase(this string str) + { + return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower(); + } +} diff --git a/MinecraftClient/Inventory/Enchantment.cs b/MinecraftClient/Inventory/Enchantment.cs new file mode 100644 index 00000000..09b513f9 --- /dev/null +++ b/MinecraftClient/Inventory/Enchantment.cs @@ -0,0 +1,3 @@ +namespace MinecraftClient.Inventory; + +public record Enchantment(Enchantments Type, int Level); \ No newline at end of file diff --git a/MinecraftClient/Inventory/EnchantmentData.cs b/MinecraftClient/Inventory/EnchantmentData.cs index c42dd920..55571d0c 100644 --- a/MinecraftClient/Inventory/EnchantmentData.cs +++ b/MinecraftClient/Inventory/EnchantmentData.cs @@ -1,10 +1,10 @@ namespace MinecraftClient.Inventory { - public class EnchantmentData + public record EnchantmentData { - public Enchantment TopEnchantment { get; set; } - public Enchantment MiddleEnchantment { get; set; } - public Enchantment BottomEnchantment { get; set; } + public Enchantments TopEnchantment { get; set; } + public Enchantments MiddleEnchantment { get; set; } + public Enchantments BottomEnchantment { get; set; } // Seed for rendering Standard Galactic Language (symbols in the enchanting table) (Useful for poeple who use MCC for the protocol) public short Seed { get; set; } diff --git a/MinecraftClient/Inventory/EnchantmentMapping.cs b/MinecraftClient/Inventory/EnchantmentMapping.cs index badadb99..f09bd420 100644 --- a/MinecraftClient/Inventory/EnchantmentMapping.cs +++ b/MinecraftClient/Inventory/EnchantmentMapping.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using MinecraftClient.Protocol.Handlers; @@ -10,168 +10,355 @@ namespace MinecraftClient.Inventory { #pragma warning disable format // @formatter:off // 1.14 - 1.15.2 - private static Dictionary enchantmentMappings114 = new Dictionary() + private static Dictionary enchantmentMappings114 = new() { //id type - { 0, Enchantment.Protection }, - { 1, Enchantment.FireProtection }, - { 2, Enchantment.FeatherFalling }, - { 3, Enchantment.BlastProtection }, - { 4, Enchantment.ProjectileProtection }, - { 5, Enchantment.Respiration }, - { 6, Enchantment.AquaAffinity }, - { 7, Enchantment.Thorns }, - { 8, Enchantment.DepthStrieder }, - { 9, Enchantment.FrostWalker }, - { 10, Enchantment.BindingCurse }, - { 11, Enchantment.Sharpness }, - { 12, Enchantment.Smite }, - { 13, Enchantment.BaneOfArthropods }, - { 14, Enchantment.Knockback }, - { 15, Enchantment.FireAspect }, - { 16, Enchantment.Looting }, - { 17, Enchantment.Sweeping }, - { 18, Enchantment.Efficency }, - { 19, Enchantment.SilkTouch }, - { 20, Enchantment.Unbreaking }, - { 21, Enchantment.Fortune }, - { 22, Enchantment.Power }, - { 23, Enchantment.Punch }, - { 24, Enchantment.Flame }, - { 25, Enchantment.Infinity }, - { 26, Enchantment.LuckOfTheSea }, - { 27, Enchantment.Lure }, - { 28, Enchantment.Loyality }, - { 29, Enchantment.Impaling }, - { 30, Enchantment.Riptide }, - { 31, Enchantment.Channeling }, - { 32, Enchantment.Mending }, - { 33, Enchantment.VanishingCurse } + { 0, Enchantments.Protection }, + { 1, Enchantments.FireProtection }, + { 2, Enchantments.FeatherFalling }, + { 3, Enchantments.BlastProtection }, + { 4, Enchantments.ProjectileProtection }, + { 5, Enchantments.Respiration }, + { 6, Enchantments.AquaAffinity }, + { 7, Enchantments.Thorns }, + { 8, Enchantments.DepthStrider }, + { 9, Enchantments.FrostWalker }, + { 10, Enchantments.BindingCurse }, + { 11, Enchantments.Sharpness }, + { 12, Enchantments.Smite }, + { 13, Enchantments.BaneOfArthropods }, + { 14, Enchantments.Knockback }, + { 15, Enchantments.FireAspect }, + { 16, Enchantments.Looting }, + { 17, Enchantments.Sweeping }, + { 18, Enchantments.Efficiency }, + { 19, Enchantments.SilkTouch }, + { 20, Enchantments.Unbreaking }, + { 21, Enchantments.Fortune }, + { 22, Enchantments.Power }, + { 23, Enchantments.Punch }, + { 24, Enchantments.Flame }, + { 25, Enchantments.Infinity }, + { 26, Enchantments.LuckOfTheSea }, + { 27, Enchantments.Lure }, + { 28, Enchantments.Loyalty }, + { 29, Enchantments.Impaling }, + { 30, Enchantments.Riptide }, + { 31, Enchantments.Channeling }, + { 32, Enchantments.Mending }, + { 33, Enchantments.VanishingCurse } }; // 1.16 - 1.18 - private static Dictionary enchantmentMappings116 = new Dictionary() + private static Dictionary enchantmentMappings116 = new() { //id type - { 0, Enchantment.Protection }, - { 1, Enchantment.FireProtection }, - { 2, Enchantment.FeatherFalling }, - { 3, Enchantment.BlastProtection }, - { 4, Enchantment.ProjectileProtection }, - { 5, Enchantment.Respiration }, - { 6, Enchantment.AquaAffinity }, - { 7, Enchantment.Thorns }, - { 8, Enchantment.DepthStrieder }, - { 9, Enchantment.FrostWalker }, - { 10, Enchantment.BindingCurse }, - { 11, Enchantment.SoulSpeed }, - { 12, Enchantment.Sharpness }, - { 13, Enchantment.Smite }, - { 14, Enchantment.BaneOfArthropods }, - { 15, Enchantment.Knockback }, - { 16, Enchantment.FireAspect }, - { 17, Enchantment.Looting }, - { 18, Enchantment.Sweeping }, - { 19, Enchantment.Efficency }, - { 20, Enchantment.SilkTouch }, - { 21, Enchantment.Unbreaking }, - { 22, Enchantment.Fortune }, - { 23, Enchantment.Power }, - { 24, Enchantment.Punch }, - { 25, Enchantment.Flame }, - { 26, Enchantment.Infinity }, - { 27, Enchantment.LuckOfTheSea }, - { 28, Enchantment.Lure }, - { 29, Enchantment.Loyality }, - { 30, Enchantment.Impaling }, - { 31, Enchantment.Riptide }, - { 32, Enchantment.Channeling }, - { 33, Enchantment.Multishot }, - { 34, Enchantment.QuickCharge }, - { 35, Enchantment.Piercing }, - { 36, Enchantment.Mending }, - { 37, Enchantment.VanishingCurse } + { 0, Enchantments.Protection }, + { 1, Enchantments.FireProtection }, + { 2, Enchantments.FeatherFalling }, + { 3, Enchantments.BlastProtection }, + { 4, Enchantments.ProjectileProtection }, + { 5, Enchantments.Respiration }, + { 6, Enchantments.AquaAffinity }, + { 7, Enchantments.Thorns }, + { 8, Enchantments.DepthStrider }, + { 9, Enchantments.FrostWalker }, + { 10, Enchantments.BindingCurse }, + { 11, Enchantments.SoulSpeed }, + { 12, Enchantments.Sharpness }, + { 13, Enchantments.Smite }, + { 14, Enchantments.BaneOfArthropods }, + { 15, Enchantments.Knockback }, + { 16, Enchantments.FireAspect }, + { 17, Enchantments.Looting }, + { 18, Enchantments.Sweeping }, + { 19, Enchantments.Efficiency }, + { 20, Enchantments.SilkTouch }, + { 21, Enchantments.Unbreaking }, + { 22, Enchantments.Fortune }, + { 23, Enchantments.Power }, + { 24, Enchantments.Punch }, + { 25, Enchantments.Flame }, + { 26, Enchantments.Infinity }, + { 27, Enchantments.LuckOfTheSea }, + { 28, Enchantments.Lure }, + { 29, Enchantments.Loyalty }, + { 30, Enchantments.Impaling }, + { 31, Enchantments.Riptide }, + { 32, Enchantments.Channeling }, + { 33, Enchantments.Multishot }, + { 34, Enchantments.QuickCharge }, + { 35, Enchantments.Piercing }, + { 36, Enchantments.Mending }, + { 37, Enchantments.VanishingCurse } }; - // 1.19+ - private static Dictionary enchantmentMappings = new Dictionary() + // 1.19 - 1.20.4 + private static Dictionary enchantmentMappings119 = new() { //id type - { 0, Enchantment.Protection }, - { 1, Enchantment.FireProtection }, - { 2, Enchantment.FeatherFalling }, - { 3, Enchantment.BlastProtection }, - { 4, Enchantment.ProjectileProtection }, - { 5, Enchantment.Respiration }, - { 6, Enchantment.AquaAffinity }, - { 7, Enchantment.Thorns }, - { 8, Enchantment.DepthStrieder }, - { 9, Enchantment.FrostWalker }, - { 10, Enchantment.BindingCurse }, - { 11, Enchantment.SoulSpeed }, - { 12, Enchantment.SwiftSneak }, - { 13, Enchantment.Sharpness }, - { 14, Enchantment.Smite }, - { 15, Enchantment.BaneOfArthropods }, - { 16, Enchantment.Knockback }, - { 17, Enchantment.FireAspect }, - { 18, Enchantment.Looting }, - { 19, Enchantment.Sweeping }, - { 20, Enchantment.Efficency }, - { 21, Enchantment.SilkTouch }, - { 22, Enchantment.Unbreaking }, - { 23, Enchantment.Fortune }, - { 24, Enchantment.Power }, - { 25, Enchantment.Punch }, - { 26, Enchantment.Flame }, - { 27, Enchantment.Infinity }, - { 28, Enchantment.LuckOfTheSea }, - { 29, Enchantment.Lure }, - { 30, Enchantment.Loyality }, - { 31, Enchantment.Impaling }, - { 32, Enchantment.Riptide }, - { 33, Enchantment.Channeling }, - { 34, Enchantment.Multishot }, - { 35, Enchantment.QuickCharge }, - { 36, Enchantment.Piercing }, - { 37, Enchantment.Mending }, - { 38, Enchantment.VanishingCurse } + { 0, Enchantments.Protection }, + { 1, Enchantments.FireProtection }, + { 2, Enchantments.FeatherFalling }, + { 3, Enchantments.BlastProtection }, + { 4, Enchantments.ProjectileProtection }, + { 5, Enchantments.Respiration }, + { 6, Enchantments.AquaAffinity }, + { 7, Enchantments.Thorns }, + { 8, Enchantments.DepthStrider }, + { 9, Enchantments.FrostWalker }, + { 10, Enchantments.BindingCurse }, + { 11, Enchantments.SoulSpeed }, + { 12, Enchantments.SwiftSneak }, + { 13, Enchantments.Sharpness }, + { 14, Enchantments.Smite }, + { 15, Enchantments.BaneOfArthropods }, + { 16, Enchantments.Knockback }, + { 17, Enchantments.FireAspect }, + { 18, Enchantments.Looting }, + { 19, Enchantments.Sweeping }, + { 20, Enchantments.Efficiency }, + { 21, Enchantments.SilkTouch }, + { 22, Enchantments.Unbreaking }, + { 23, Enchantments.Fortune }, + { 24, Enchantments.Power }, + { 25, Enchantments.Punch }, + { 26, Enchantments.Flame }, + { 27, Enchantments.Infinity }, + { 28, Enchantments.LuckOfTheSea }, + { 29, Enchantments.Lure }, + { 30, Enchantments.Loyalty }, + { 31, Enchantments.Impaling }, + { 32, Enchantments.Riptide }, + { 33, Enchantments.Channeling }, + { 34, Enchantments.Multishot }, + { 35, Enchantments.QuickCharge }, + { 36, Enchantments.Piercing }, + { 37, Enchantments.Mending }, + { 38, Enchantments.VanishingCurse } + }; + + // 1.20.6 - 1.21.10 + private static Dictionary enchantmentMappings1206 = new() + { + //id type + { 0, Enchantments.Protection }, + { 1, Enchantments.FireProtection }, + { 2, Enchantments.FeatherFalling }, + { 3, Enchantments.BlastProtection }, + { 4, Enchantments.ProjectileProtection }, + { 5, Enchantments.Respiration }, + { 6, Enchantments.AquaAffinity }, + { 7, Enchantments.Thorns }, + { 8, Enchantments.DepthStrider }, + { 9, Enchantments.FrostWalker }, + { 10, Enchantments.BindingCurse }, + { 11, Enchantments.SoulSpeed }, + { 12, Enchantments.SwiftSneak }, + { 13, Enchantments.Sharpness }, + { 14, Enchantments.Smite }, + { 15, Enchantments.BaneOfArthropods }, + { 16, Enchantments.Knockback }, + { 17, Enchantments.FireAspect }, + { 18, Enchantments.Looting }, + { 19, Enchantments.Sweeping }, + { 20, Enchantments.Efficiency }, + { 21, Enchantments.SilkTouch }, + { 22, Enchantments.Unbreaking }, + { 23, Enchantments.Fortune }, + { 24, Enchantments.Power }, + { 25, Enchantments.Punch }, + { 26, Enchantments.Flame }, + { 27, Enchantments.Infinity }, + { 28, Enchantments.LuckOfTheSea }, + { 29, Enchantments.Lure }, + { 30, Enchantments.Loyalty }, + { 31, Enchantments.Impaling }, + { 32, Enchantments.Riptide }, + { 33, Enchantments.Channeling }, + { 34, Enchantments.Multishot }, + { 35, Enchantments.QuickCharge }, + { 36, Enchantments.Piercing }, + { 37, Enchantments.Density }, + { 38, Enchantments.Breach }, + { 39, Enchantments.WindBurst }, + { 40, Enchantments.Mending }, + { 41, Enchantments.VanishingCurse } + }; + + // 1.21.11+ + private static Dictionary enchantmentMappings12111 = new() + { + //id type + { 0, Enchantments.Protection }, + { 1, Enchantments.FireProtection }, + { 2, Enchantments.FeatherFalling }, + { 3, Enchantments.BlastProtection }, + { 4, Enchantments.ProjectileProtection }, + { 5, Enchantments.Respiration }, + { 6, Enchantments.AquaAffinity }, + { 7, Enchantments.Thorns }, + { 8, Enchantments.DepthStrider }, + { 9, Enchantments.FrostWalker }, + { 10, Enchantments.BindingCurse }, + { 11, Enchantments.SoulSpeed }, + { 12, Enchantments.SwiftSneak }, + { 13, Enchantments.Sharpness }, + { 14, Enchantments.Smite }, + { 15, Enchantments.BaneOfArthropods }, + { 16, Enchantments.Knockback }, + { 17, Enchantments.FireAspect }, + { 18, Enchantments.Looting }, + { 19, Enchantments.Sweeping }, + { 20, Enchantments.Efficiency }, + { 21, Enchantments.SilkTouch }, + { 22, Enchantments.Unbreaking }, + { 23, Enchantments.Fortune }, + { 24, Enchantments.Power }, + { 25, Enchantments.Punch }, + { 26, Enchantments.Flame }, + { 27, Enchantments.Infinity }, + { 28, Enchantments.LuckOfTheSea }, + { 29, Enchantments.Lure }, + { 30, Enchantments.Loyalty }, + { 31, Enchantments.Impaling }, + { 32, Enchantments.Riptide }, + { 33, Enchantments.Channeling }, + { 34, Enchantments.Multishot }, + { 35, Enchantments.QuickCharge }, + { 36, Enchantments.Piercing }, + { 37, Enchantments.Density }, + { 38, Enchantments.Breach }, + { 39, Enchantments.WindBurst }, + { 40, Enchantments.Lunge }, + { 41, Enchantments.Mending }, + { 42, Enchantments.VanishingCurse } }; #pragma warning restore format // @formatter:on - public static Enchantment GetEnchantmentById(int protocolVersion, short id) + public static Enchantments GetEnchantmentById(int protocolVersion, short id) { if (protocolVersion < Protocol18Handler.MC_1_14_Version) - throw new Exception("Enchantments mappings are not implemented bellow 1.14"); + throw new Exception("Enchantments mappings are not implemented below 1.14"); - Dictionary map = enchantmentMappings; + var map = GetMapForProtocolVersion(protocolVersion); - if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion < Protocol18Handler.MC_1_16_Version) - map = enchantmentMappings114; - else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion < Protocol18Handler.MC_1_19_Version) - map = enchantmentMappings116; + if (!map.TryGetValue(id, out var value)) + throw new Exception($"Got an Unknown Enchantment ID {id}, please update the Mappings!"); - if (!map.ContainsKey(id)) - throw new Exception("Got an Unknown Enchantment ID '" + id + "', please update the Mappings!"); - - return map[id]; + return value; } - public static string GetEnchantmentName(Enchantment enchantment) + private static Dictionary? reverseDynamicEnchantmentMappings; + private static readonly Dictionary reverseEnchantmentMappings114 = CreateReverseMap(enchantmentMappings114); + private static readonly Dictionary reverseEnchantmentMappings116 = CreateReverseMap(enchantmentMappings116); + private static readonly Dictionary reverseEnchantmentMappings119 = CreateReverseMap(enchantmentMappings119); + private static readonly Dictionary reverseEnchantmentMappings1206 = CreateReverseMap(enchantmentMappings1206); + private static readonly Dictionary reverseEnchantmentMappings12111 = CreateReverseMap(enchantmentMappings12111); + private static Dictionary? dynamicEnchantmentIdMap; + + private static readonly Dictionary nameToEnchantment = new() { - string? trans = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); - if (string.IsNullOrEmpty(trans)) - return "Unknown Enchantment with ID: " + ((short)enchantment) + " (Probably not named in the code yet)"; - else - return trans; + { "protection", Enchantments.Protection }, + { "fire_protection", Enchantments.FireProtection }, + { "feather_falling", Enchantments.FeatherFalling }, + { "blast_protection", Enchantments.BlastProtection }, + { "projectile_protection", Enchantments.ProjectileProtection }, + { "respiration", Enchantments.Respiration }, + { "aqua_affinity", Enchantments.AquaAffinity }, + { "thorns", Enchantments.Thorns }, + { "depth_strider", Enchantments.DepthStrider }, + { "frost_walker", Enchantments.FrostWalker }, + { "binding_curse", Enchantments.BindingCurse }, + { "soul_speed", Enchantments.SoulSpeed }, + { "swift_sneak", Enchantments.SwiftSneak }, + { "sharpness", Enchantments.Sharpness }, + { "smite", Enchantments.Smite }, + { "bane_of_arthropods", Enchantments.BaneOfArthropods }, + { "knockback", Enchantments.Knockback }, + { "fire_aspect", Enchantments.FireAspect }, + { "looting", Enchantments.Looting }, + { "sweeping_edge", Enchantments.Sweeping }, + { "efficiency", Enchantments.Efficiency }, + { "silk_touch", Enchantments.SilkTouch }, + { "unbreaking", Enchantments.Unbreaking }, + { "fortune", Enchantments.Fortune }, + { "power", Enchantments.Power }, + { "punch", Enchantments.Punch }, + { "flame", Enchantments.Flame }, + { "infinity", Enchantments.Infinity }, + { "luck_of_the_sea", Enchantments.LuckOfTheSea }, + { "lure", Enchantments.Lure }, + { "loyalty", Enchantments.Loyalty }, + { "lunge", Enchantments.Lunge }, + { "impaling", Enchantments.Impaling }, + { "riptide", Enchantments.Riptide }, + { "channeling", Enchantments.Channeling }, + { "multishot", Enchantments.Multishot }, + { "quick_charge", Enchantments.QuickCharge }, + { "piercing", Enchantments.Piercing }, + { "density", Enchantments.Density }, + { "breach", Enchantments.Breach }, + { "wind_burst", Enchantments.WindBurst }, + { "mending", Enchantments.Mending }, + { "vanishing_curse", Enchantments.VanishingCurse }, + }; + + /// + /// Set the dynamic enchantment ID map from server RegistryData. + /// Called during configuration phase when receiving minecraft:enchantment registry. + /// + public static void SetDynamicEnchantmentIdMap(Dictionary idMap) + { + dynamicEnchantmentIdMap = new(); + foreach (var kvp in idMap) + { + var name = kvp.Value.StartsWith("minecraft:") ? kvp.Value.Substring("minecraft:".Length) : kvp.Value; + if (nameToEnchantment.TryGetValue(name, out var enchantment)) + dynamicEnchantmentIdMap[kvp.Key] = enchantment; + } + reverseDynamicEnchantmentMappings = null; + } + + public static Enchantments GetEnchantmentByRegistryId1206(int protocolVersion, int id) + { + if (dynamicEnchantmentIdMap is not null && dynamicEnchantmentIdMap.TryGetValue(id, out var dynValue)) + return dynValue; + if (GetMapForProtocolVersion(protocolVersion).TryGetValue((short)id, out var value)) + return value; + return (Enchantments)(-1); + } + + public static int GetRegistryId1206ByEnchantment(int protocolVersion, Enchantments enchantment) + { + if (dynamicEnchantmentIdMap is not null) + { + if (reverseDynamicEnchantmentMappings is null) + { + reverseDynamicEnchantmentMappings = new(); + foreach (var kvp in dynamicEnchantmentIdMap) + reverseDynamicEnchantmentMappings[kvp.Value] = (short)kvp.Key; + } + + return reverseDynamicEnchantmentMappings.TryGetValue(enchantment, out var dynamicId) ? dynamicId : -1; + } + + var reverseMap = GetReverseMapForProtocolVersion(protocolVersion); + return reverseMap.TryGetValue(enchantment, out var id) ? id : -1; + } + + public static string GetEnchantmentName(Enchantments enchantment) + { + var translation = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); + return string.IsNullOrEmpty(translation) ? $"Unknown Enchantment with ID: {(short)enchantment} (Probably not named in the code yet)" : translation; } public static string ConvertLevelToRomanNumbers(int num) { - string result = string.Empty; - Dictionary romanNumbers = new Dictionary + var result = string.Empty; + var romanNumbers = new Dictionary { - {"M", 1000 }, + {"M", 1000}, {"CM", 900}, {"D", 500}, {"CD", 400}, @@ -194,5 +381,40 @@ namespace MinecraftClient.Inventory return result; } + + private static Dictionary GetMapForProtocolVersion(int protocolVersion) + { + return protocolVersion switch + { + >= Protocol18Handler.MC_1_14_Version and < Protocol18Handler.MC_1_16_Version => enchantmentMappings114, + >= Protocol18Handler.MC_1_16_Version and < Protocol18Handler.MC_1_19_Version => enchantmentMappings116, + >= Protocol18Handler.MC_1_19_Version and < Protocol18Handler.MC_1_20_6_Version => enchantmentMappings119, + >= Protocol18Handler.MC_1_20_6_Version and < Protocol18Handler.MC_1_21_11_Version => enchantmentMappings1206, + >= Protocol18Handler.MC_1_21_11_Version => enchantmentMappings12111, + _ => throw new Exception("Enchantments mappings are not implemented below 1.14") + }; + } + + private static Dictionary GetReverseMapForProtocolVersion(int protocolVersion) + { + return protocolVersion switch + { + >= Protocol18Handler.MC_1_14_Version and < Protocol18Handler.MC_1_16_Version => reverseEnchantmentMappings114, + >= Protocol18Handler.MC_1_16_Version and < Protocol18Handler.MC_1_19_Version => reverseEnchantmentMappings116, + >= Protocol18Handler.MC_1_19_Version and < Protocol18Handler.MC_1_20_6_Version => reverseEnchantmentMappings119, + >= Protocol18Handler.MC_1_20_6_Version and < Protocol18Handler.MC_1_21_11_Version => reverseEnchantmentMappings1206, + >= Protocol18Handler.MC_1_21_11_Version => reverseEnchantmentMappings12111, + _ => throw new Exception("Enchantments mappings are not implemented below 1.14") + }; + } + + private static Dictionary CreateReverseMap(Dictionary map) + { + Dictionary reverseMap = new(); + foreach (var kvp in map) + reverseMap[kvp.Value] = kvp.Key; + + return reverseMap; + } } } diff --git a/MinecraftClient/Inventory/Enchantments.cs b/MinecraftClient/Inventory/Enchantments.cs index 34279de0..173ea995 100644 --- a/MinecraftClient/Inventory/Enchantments.cs +++ b/MinecraftClient/Inventory/Enchantments.cs @@ -1,46 +1,50 @@ -namespace MinecraftClient.Inventory +namespace MinecraftClient.Inventory { // Not implemented for 1.14 - public enum Enchantment : short + public enum Enchantments : short { - Protection = 0, - FireProtection, - FeatherFalling, - BlastProtection, - ProjectileProtection, - Respiration, - AquaAffinity, - Thorns, - DepthStrieder, - FrostWalker, - BindingCurse, - SoulSpeed, - SwiftSneak, - Sharpness, - Smite, + AquaAffinity = 0, BaneOfArthropods, - Knockback, - FireAspect, - Looting, - Sweeping, - Efficency, - SilkTouch, - Unbreaking, - Fortune, - Power, - Punch, - Flame, - Infinity, - LuckOfTheSea, - Lure, - Loyality, - Impaling, - Riptide, + BindingCurse, + BlastProtection, + Breach, Channeling, - Multishot, - QuickCharge, - Piercing, + DepthStrider, + Density, + Efficiency, + FeatherFalling, + FireAspect, + FireProtection, + Flame, + Fortune, + FrostWalker, + Impaling, + Infinity, + Knockback, + Looting, + LuckOfTheSea, + Loyalty, + Lunge, + Lure, Mending, - VanishingCurse + Multishot, + Piercing, + Power, + ProjectileProtection, + Protection, + Punch, + QuickCharge, + Respiration, + Riptide, + Sharpness, + SilkTouch, + Smite, + SoulSpeed, + Sweeping, + SwiftSneak, + Thorns, + Unbreaking, + VanishingCurse, + WindBurst } } diff --git a/MinecraftClient/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs index b9d86755..95f95b37 100644 --- a/MinecraftClient/Inventory/Item.cs +++ b/MinecraftClient/Inventory/Item.cs @@ -1,8 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; using MinecraftClient.Protocol.Message; namespace MinecraftClient.Inventory @@ -32,6 +34,11 @@ namespace MinecraftClient.Inventory /// public Dictionary? NBT; + /// + /// 1.20.6+ structured components (raw list for round-trip serialization) + /// + public List? Components; + /// /// Create an item with ItemType, Count and Metadata /// @@ -44,12 +51,20 @@ namespace MinecraftClient.Inventory Count = count; NBT = nbt; } - + public Item(ItemType itemType, int count, int data, Dictionary? nbt) : this(itemType, count, nbt) { Data = data; } + /// + /// Create a shallow clone with a specific count (preserves NBT and Components). + /// + public Item CloneWithCount(int count) + { + return new Item(Type, count, Data, NBT) { Components = Components }; + } + /// /// Check if the item slot is empty /// @@ -60,13 +75,27 @@ namespace MinecraftClient.Inventory } /// - /// Retrieve item display name from NBT properties. NULL if no display name is defined. + /// Retrieve item display name. For 1.20.6+ reads from structured components + /// (CustomNameComponent, then ItemNameComponent as fallback); for older versions reads from NBT. /// public string? DisplayName { get { - if (NBT != null && NBT.ContainsKey("display")) + if (Components is not null) + { + var customName = Components.OfType().FirstOrDefault(); + if (customName is not null && !string.IsNullOrEmpty(customName.CustomName)) + return customName.CustomName; + + var itemName = Components.OfType().FirstOrDefault(); + if (itemName is not null && !string.IsNullOrEmpty(itemName.ItemName)) + return itemName.ItemName; + + return null; + } + + if (NBT is not null && NBT.ContainsKey("display")) { if (NBT["display"] is Dictionary displayProperties && displayProperties.ContainsKey("Name")) @@ -82,22 +111,31 @@ namespace MinecraftClient.Inventory } /// - /// Retrieve item lores from NBT properties. Returns null if no lores is defined. + /// Retrieve item lores. For 1.20.6+ reads from LoreNameComponent1206; for older versions reads from NBT. /// public string[]? Lores { get { + if (Components is not null) + { + var loreComponent = Components.OfType().FirstOrDefault(); + if (loreComponent is not null && loreComponent.Lines.Count > 0) + return loreComponent.Lines.ToArray(); + + return null; + } + List lores = new(); - if (NBT != null && NBT.ContainsKey("display")) + if (NBT is not null && NBT.ContainsKey("display")) { if (NBT["display"] is Dictionary displayProperties && displayProperties.ContainsKey("Lore")) { object[] displayName = (object[])displayProperties["Lore"]; lores.AddRange(from string st in displayName - let str = ChatParser.ParseText(st.ToString()) - select str); + let str = ChatParser.ParseText(st.ToString()) + select str); return lores.ToArray(); } } @@ -107,16 +145,25 @@ namespace MinecraftClient.Inventory } /// - /// Retrieve item damage from NBT properties. Returns 0 if no damage is defined. + /// Retrieve item damage. For 1.20.6+ reads from DamageComponent; for older versions reads from NBT. /// public int Damage { get { - if (NBT != null && NBT.ContainsKey("Damage")) + if (Components is not null) + { + var damageComponent = Components.OfType().FirstOrDefault(); + if (damageComponent is not null) + return damageComponent.Damage; + + return 0; + } + + if (NBT is not null && NBT.ContainsKey("Damage")) { object damage = NBT["Damage"]; - if (damage != null) + if (damage is not null) { return int.Parse(damage.ToString() ?? string.Empty, NumberStyles.Any, CultureInfo.CurrentCulture); @@ -127,6 +174,26 @@ namespace MinecraftClient.Inventory } } + /// + /// Retrieve enchantments from structured components (1.20.6+). Returns null for older versions. + /// Both normal enchantments (EnchantmentsComponent) and stored enchantments + /// (StoredEnchantmentsComponent, e.g. enchanted books) are checked. + /// + public List? EnchantmentList + { + get + { + if (Components is null) + return null; + + var enchComp = Components.OfType().FirstOrDefault(); + if (enchComp is not null && enchComp.Enchantments.Count > 0) + return enchComp.Enchantments; + + return null; + } + } + public static string GetTypeString(ItemType type) { string type_str = type.ToString(); @@ -152,8 +219,18 @@ namespace MinecraftClient.Inventory try { - if (NBT != null && (NBT.TryGetValue("Enchantments", out object? enchantments) || - NBT.TryGetValue("StoredEnchantments", out enchantments))) + var enchList = EnchantmentList; + if (enchList is not null) + { + foreach (var ench in enchList) + { + string name = EnchantmentMapping.GetEnchantmentName(ench.Type); + string level = EnchantmentMapping.ConvertLevelToRomanNumbers(ench.Level); + sb.AppendFormat(" | {0} {1}", name, level); + } + } + else if (NBT is not null && (NBT.TryGetValue("Enchantments", out object? enchantments) || + NBT.TryGetValue("StoredEnchantments", out enchantments))) { foreach (Dictionary enchantment in (object[])enchantments) { @@ -165,7 +242,7 @@ namespace MinecraftClient.Inventory } } - if (Lores != null && Lores.Length > 0) + if (Lores is not null && Lores.Length > 0) { foreach (var lore in Lores) sb.AppendFormat(" | {0}", lore); @@ -195,4 +272,4 @@ namespace MinecraftClient.Inventory return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs index da9a0097..16d7d4de 100644 --- a/MinecraftClient/Inventory/ItemMovingHelper.cs +++ b/MinecraftClient/Inventory/ItemMovingHelper.cs @@ -7,24 +7,10 @@ namespace MinecraftClient.Inventory /// /// Class that contains useful methods to move item around in a container /// - public class ItemMovingHelper + public class ItemMovingHelper(Container c, McClient mc) { - private readonly Container c; - private readonly McClient mc; - - /// - /// Create a helper that contains useful methods to move item around in container - /// - /// Source container to use. All method will use this container for handling first slot parameter - /// McClient handler. Needed for sending WindowAction packet to the server - /// - /// If you are using ChatBot API and cannot have direct access to McClient handler, use as second parameter - /// - public ItemMovingHelper(Container c, McClient mc) - { - this.c = c; - this.mc = mc; - } + private readonly Container c = c; + private readonly McClient mc = mc; /// /// Move an item fron source to dest. Source should contain an item and dest slot should be empty @@ -38,9 +24,9 @@ namespace MinecraftClient.Inventory // Condition: source has item and dest has no item if (ValidateSlots(source, dest, destContainer) && HasItem(source) && - ((destContainer != null && !HasItem(dest, destContainer)) || (destContainer == null && !HasItem(dest)))) + ((destContainer is not null && !HasItem(dest, destContainer)) || (destContainer is null && !HasItem(dest)))) return mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick) - && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick); + && mc.DoWindowAction(destContainer is null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick); else return false; } @@ -56,9 +42,9 @@ namespace MinecraftClient.Inventory // Condition: Both slot1 and slot2 has item if (ValidateSlots(slot1, slot2, destContainer) && HasItem(slot1) && - (destContainer != null && HasItem(slot2, destContainer) || (destContainer == null && HasItem(slot2)))) + (destContainer is not null && HasItem(slot2, destContainer) || (destContainer is null && HasItem(slot2)))) return mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick) - && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick) + && mc.DoWindowAction(destContainer is null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick) && mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick); else return false; } @@ -126,7 +112,7 @@ namespace MinecraftClient.Inventory /// The compare result private bool ValidateSlots(int s1, int s2, Container? s2Container = null) { - if (s2Container == null) + if (s2Container is null) return (s1 != s2 && s1 < c.Type.SlotCount() && s2 < c.Type.SlotCount()); else return (s1 < c.Type.SlotCount() && s2 < s2Container.Type.SlotCount()); @@ -153,7 +139,7 @@ namespace MinecraftClient.Inventory /// True if they are equal private bool ItemTypeEqual(int slot1, int slot2, Container? s2Container = null) { - if (s2Container == null) + if (s2Container is null) { if (HasItem(slot1) && HasItem(slot2)) return c.Items[slot1].Type == c.Items[slot2].Type; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette.cs index d6770fcd..c8cbe316 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette.cs @@ -14,7 +14,7 @@ namespace MinecraftClient.Inventory.ItemPalettes { if (DictReverse.ContainsKey(entry.Value)) continue; - + DictReverse.Add(entry.Value, entry.Key); } diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette110.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette110.cs index f284d949..7a6a6d79 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette110.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette110.cs @@ -71,7 +71,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[1835008] = ItemType.DetectorRail; mappings[1900544] = ItemType.StickyPiston; mappings[1966080] = ItemType.Cobweb; - mappings[2031617] = ItemType.Grass; + mappings[2031617] = ItemType.ShortGrass; mappings[2031618] = ItemType.Fern; mappings[2097152] = ItemType.DeadBush; mappings[2162688] = ItemType.Piston; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette111.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette111.cs index 9d9f20bb..02f26948 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette111.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette111.cs @@ -71,7 +71,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[1835008] = ItemType.DetectorRail; mappings[1900544] = ItemType.StickyPiston; mappings[1966080] = ItemType.Cobweb; - mappings[2031617] = ItemType.Grass; + mappings[2031617] = ItemType.ShortGrass; mappings[2031618] = ItemType.Fern; mappings[2097152] = ItemType.DeadBush; mappings[2162688] = ItemType.Piston; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette112.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette112.cs index 2826641d..b6758b62 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette112.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette112.cs @@ -62,7 +62,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[1769472] = ItemType.PoweredRail; mappings[1835008] = ItemType.DetectorRail; mappings[1900544] = ItemType.StickyPiston; - mappings[2031617] = ItemType.Grass; + mappings[2031617] = ItemType.ShortGrass; mappings[2031618] = ItemType.Fern; mappings[2097152] = ItemType.DeadBush; mappings[2162688] = ItemType.Piston; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette113.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette113.cs new file mode 100644 index 00000000..af292fa1 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette113.cs @@ -0,0 +1,803 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette113 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette113() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.GrassBlock; + mappings[9] = ItemType.Dirt; + mappings[10] = ItemType.CoarseDirt; + mappings[11] = ItemType.Podzol; + mappings[12] = ItemType.Cobblestone; + mappings[13] = ItemType.OakPlanks; + mappings[14] = ItemType.SprucePlanks; + mappings[15] = ItemType.BirchPlanks; + mappings[16] = ItemType.JunglePlanks; + mappings[17] = ItemType.AcaciaPlanks; + mappings[18] = ItemType.DarkOakPlanks; + mappings[19] = ItemType.OakSapling; + mappings[20] = ItemType.SpruceSapling; + mappings[21] = ItemType.BirchSapling; + mappings[22] = ItemType.JungleSapling; + mappings[23] = ItemType.AcaciaSapling; + mappings[24] = ItemType.DarkOakSapling; + mappings[25] = ItemType.Bedrock; + mappings[26] = ItemType.Sand; + mappings[27] = ItemType.RedSand; + mappings[28] = ItemType.Gravel; + mappings[29] = ItemType.GoldOre; + mappings[30] = ItemType.IronOre; + mappings[31] = ItemType.CoalOre; + mappings[32] = ItemType.OakLog; + mappings[33] = ItemType.SpruceLog; + mappings[34] = ItemType.BirchLog; + mappings[35] = ItemType.JungleLog; + mappings[36] = ItemType.AcaciaLog; + mappings[37] = ItemType.DarkOakLog; + mappings[38] = ItemType.StrippedOakLog; + mappings[39] = ItemType.StrippedSpruceLog; + mappings[40] = ItemType.StrippedBirchLog; + mappings[41] = ItemType.StrippedJungleLog; + mappings[42] = ItemType.StrippedAcaciaLog; + mappings[43] = ItemType.StrippedDarkOakLog; + mappings[44] = ItemType.StrippedOakWood; + mappings[45] = ItemType.StrippedSpruceWood; + mappings[46] = ItemType.StrippedBirchWood; + mappings[47] = ItemType.StrippedJungleWood; + mappings[48] = ItemType.StrippedAcaciaWood; + mappings[49] = ItemType.StrippedDarkOakWood; + mappings[50] = ItemType.OakWood; + mappings[51] = ItemType.SpruceWood; + mappings[52] = ItemType.BirchWood; + mappings[53] = ItemType.JungleWood; + mappings[54] = ItemType.AcaciaWood; + mappings[55] = ItemType.DarkOakWood; + mappings[56] = ItemType.OakLeaves; + mappings[57] = ItemType.SpruceLeaves; + mappings[58] = ItemType.BirchLeaves; + mappings[59] = ItemType.JungleLeaves; + mappings[60] = ItemType.AcaciaLeaves; + mappings[61] = ItemType.DarkOakLeaves; + mappings[62] = ItemType.Sponge; + mappings[63] = ItemType.WetSponge; + mappings[64] = ItemType.Glass; + mappings[65] = ItemType.LapisOre; + mappings[66] = ItemType.LapisBlock; + mappings[67] = ItemType.Dispenser; + mappings[68] = ItemType.Sandstone; + mappings[69] = ItemType.ChiseledSandstone; + mappings[70] = ItemType.CutSandstone; + mappings[71] = ItemType.NoteBlock; + mappings[72] = ItemType.PoweredRail; + mappings[73] = ItemType.DetectorRail; + mappings[74] = ItemType.StickyPiston; + mappings[75] = ItemType.Cobweb; + mappings[76] = ItemType.ShortGrass; + mappings[77] = ItemType.Fern; + mappings[78] = ItemType.DeadBush; + mappings[79] = ItemType.Seagrass; + mappings[80] = ItemType.SeaPickle; + mappings[81] = ItemType.Piston; + mappings[82] = ItemType.WhiteWool; + mappings[83] = ItemType.OrangeWool; + mappings[84] = ItemType.MagentaWool; + mappings[85] = ItemType.LightBlueWool; + mappings[86] = ItemType.YellowWool; + mappings[87] = ItemType.LimeWool; + mappings[88] = ItemType.PinkWool; + mappings[89] = ItemType.GrayWool; + mappings[90] = ItemType.LightGrayWool; + mappings[91] = ItemType.CyanWool; + mappings[92] = ItemType.PurpleWool; + mappings[93] = ItemType.BlueWool; + mappings[94] = ItemType.BrownWool; + mappings[95] = ItemType.GreenWool; + mappings[96] = ItemType.RedWool; + mappings[97] = ItemType.BlackWool; + mappings[98] = ItemType.Dandelion; + mappings[99] = ItemType.Poppy; + mappings[100] = ItemType.BlueOrchid; + mappings[101] = ItemType.Allium; + mappings[102] = ItemType.AzureBluet; + mappings[103] = ItemType.RedTulip; + mappings[104] = ItemType.OrangeTulip; + mappings[105] = ItemType.WhiteTulip; + mappings[106] = ItemType.PinkTulip; + mappings[107] = ItemType.OxeyeDaisy; + mappings[108] = ItemType.BrownMushroom; + mappings[109] = ItemType.RedMushroom; + mappings[110] = ItemType.GoldBlock; + mappings[111] = ItemType.IronBlock; + mappings[112] = ItemType.OakSlab; + mappings[113] = ItemType.SpruceSlab; + mappings[114] = ItemType.BirchSlab; + mappings[115] = ItemType.JungleSlab; + mappings[116] = ItemType.AcaciaSlab; + mappings[117] = ItemType.DarkOakSlab; + mappings[118] = ItemType.StoneSlab; + mappings[119] = ItemType.SandstoneSlab; + mappings[120] = ItemType.PetrifiedOakSlab; + mappings[121] = ItemType.CobblestoneSlab; + mappings[122] = ItemType.BrickSlab; + mappings[123] = ItemType.StoneBrickSlab; + mappings[124] = ItemType.NetherBrickSlab; + mappings[125] = ItemType.QuartzSlab; + mappings[126] = ItemType.RedSandstoneSlab; + mappings[127] = ItemType.PurpurSlab; + mappings[128] = ItemType.PrismarineSlab; + mappings[129] = ItemType.PrismarineBrickSlab; + mappings[130] = ItemType.DarkPrismarineSlab; + mappings[131] = ItemType.SmoothQuartz; + mappings[132] = ItemType.SmoothRedSandstone; + mappings[133] = ItemType.SmoothSandstone; + mappings[134] = ItemType.SmoothStone; + mappings[135] = ItemType.Bricks; + mappings[136] = ItemType.Tnt; + mappings[137] = ItemType.Bookshelf; + mappings[138] = ItemType.MossyCobblestone; + mappings[139] = ItemType.Obsidian; + mappings[140] = ItemType.Torch; + mappings[141] = ItemType.EndRod; + mappings[142] = ItemType.ChorusPlant; + mappings[143] = ItemType.ChorusFlower; + mappings[144] = ItemType.PurpurBlock; + mappings[145] = ItemType.PurpurPillar; + mappings[146] = ItemType.PurpurStairs; + mappings[147] = ItemType.Spawner; + mappings[148] = ItemType.OakStairs; + mappings[149] = ItemType.Chest; + mappings[150] = ItemType.DiamondOre; + mappings[151] = ItemType.DiamondBlock; + mappings[152] = ItemType.CraftingTable; + mappings[153] = ItemType.Farmland; + mappings[154] = ItemType.Furnace; + mappings[155] = ItemType.Ladder; + mappings[156] = ItemType.Rail; + mappings[157] = ItemType.CobblestoneStairs; + mappings[158] = ItemType.Lever; + mappings[159] = ItemType.StonePressurePlate; + mappings[160] = ItemType.OakPressurePlate; + mappings[161] = ItemType.SprucePressurePlate; + mappings[162] = ItemType.BirchPressurePlate; + mappings[163] = ItemType.JunglePressurePlate; + mappings[164] = ItemType.AcaciaPressurePlate; + mappings[165] = ItemType.DarkOakPressurePlate; + mappings[166] = ItemType.RedstoneOre; + mappings[167] = ItemType.RedstoneTorch; + mappings[168] = ItemType.StoneButton; + mappings[169] = ItemType.Snow; + mappings[170] = ItemType.Ice; + mappings[171] = ItemType.SnowBlock; + mappings[172] = ItemType.Cactus; + mappings[173] = ItemType.Clay; + mappings[174] = ItemType.Jukebox; + mappings[175] = ItemType.OakFence; + mappings[176] = ItemType.SpruceFence; + mappings[177] = ItemType.BirchFence; + mappings[178] = ItemType.JungleFence; + mappings[179] = ItemType.AcaciaFence; + mappings[180] = ItemType.DarkOakFence; + mappings[181] = ItemType.Pumpkin; + mappings[182] = ItemType.CarvedPumpkin; + mappings[183] = ItemType.Netherrack; + mappings[184] = ItemType.SoulSand; + mappings[185] = ItemType.Glowstone; + mappings[186] = ItemType.JackOLantern; + mappings[187] = ItemType.OakTrapdoor; + mappings[188] = ItemType.SpruceTrapdoor; + mappings[189] = ItemType.BirchTrapdoor; + mappings[190] = ItemType.JungleTrapdoor; + mappings[191] = ItemType.AcaciaTrapdoor; + mappings[192] = ItemType.DarkOakTrapdoor; + mappings[193] = ItemType.InfestedStone; + mappings[194] = ItemType.InfestedCobblestone; + mappings[195] = ItemType.InfestedStoneBricks; + mappings[196] = ItemType.InfestedMossyStoneBricks; + mappings[197] = ItemType.InfestedCrackedStoneBricks; + mappings[198] = ItemType.InfestedChiseledStoneBricks; + mappings[199] = ItemType.StoneBricks; + mappings[200] = ItemType.MossyStoneBricks; + mappings[201] = ItemType.CrackedStoneBricks; + mappings[202] = ItemType.ChiseledStoneBricks; + mappings[203] = ItemType.BrownMushroomBlock; + mappings[204] = ItemType.RedMushroomBlock; + mappings[205] = ItemType.MushroomStem; + mappings[206] = ItemType.IronBars; + mappings[207] = ItemType.GlassPane; + mappings[208] = ItemType.Melon; + mappings[209] = ItemType.Vine; + mappings[210] = ItemType.OakFenceGate; + mappings[211] = ItemType.SpruceFenceGate; + mappings[212] = ItemType.BirchFenceGate; + mappings[213] = ItemType.JungleFenceGate; + mappings[214] = ItemType.AcaciaFenceGate; + mappings[215] = ItemType.DarkOakFenceGate; + mappings[216] = ItemType.BrickStairs; + mappings[217] = ItemType.StoneBrickStairs; + mappings[218] = ItemType.Mycelium; + mappings[219] = ItemType.LilyPad; + mappings[220] = ItemType.NetherBricks; + mappings[221] = ItemType.NetherBrickFence; + mappings[222] = ItemType.NetherBrickStairs; + mappings[223] = ItemType.EnchantingTable; + mappings[224] = ItemType.EndPortalFrame; + mappings[225] = ItemType.EndStone; + mappings[226] = ItemType.EndStoneBricks; + mappings[227] = ItemType.DragonEgg; + mappings[228] = ItemType.RedstoneLamp; + mappings[229] = ItemType.SandstoneStairs; + mappings[230] = ItemType.EmeraldOre; + mappings[231] = ItemType.EnderChest; + mappings[232] = ItemType.TripwireHook; + mappings[233] = ItemType.EmeraldBlock; + mappings[234] = ItemType.SpruceStairs; + mappings[235] = ItemType.BirchStairs; + mappings[236] = ItemType.JungleStairs; + mappings[237] = ItemType.CommandBlock; + mappings[238] = ItemType.Beacon; + mappings[239] = ItemType.CobblestoneWall; + mappings[240] = ItemType.MossyCobblestoneWall; + mappings[241] = ItemType.OakButton; + mappings[242] = ItemType.SpruceButton; + mappings[243] = ItemType.BirchButton; + mappings[244] = ItemType.JungleButton; + mappings[245] = ItemType.AcaciaButton; + mappings[246] = ItemType.DarkOakButton; + mappings[247] = ItemType.Anvil; + mappings[248] = ItemType.ChippedAnvil; + mappings[249] = ItemType.DamagedAnvil; + mappings[250] = ItemType.TrappedChest; + mappings[251] = ItemType.LightWeightedPressurePlate; + mappings[252] = ItemType.HeavyWeightedPressurePlate; + mappings[253] = ItemType.DaylightDetector; + mappings[254] = ItemType.RedstoneBlock; + mappings[255] = ItemType.NetherQuartzOre; + mappings[256] = ItemType.Hopper; + mappings[257] = ItemType.ChiseledQuartzBlock; + mappings[258] = ItemType.QuartzBlock; + mappings[259] = ItemType.QuartzPillar; + mappings[260] = ItemType.QuartzStairs; + mappings[261] = ItemType.ActivatorRail; + mappings[262] = ItemType.Dropper; + mappings[263] = ItemType.WhiteTerracotta; + mappings[264] = ItemType.OrangeTerracotta; + mappings[265] = ItemType.MagentaTerracotta; + mappings[266] = ItemType.LightBlueTerracotta; + mappings[267] = ItemType.YellowTerracotta; + mappings[268] = ItemType.LimeTerracotta; + mappings[269] = ItemType.PinkTerracotta; + mappings[270] = ItemType.GrayTerracotta; + mappings[271] = ItemType.LightGrayTerracotta; + mappings[272] = ItemType.CyanTerracotta; + mappings[273] = ItemType.PurpleTerracotta; + mappings[274] = ItemType.BlueTerracotta; + mappings[275] = ItemType.BrownTerracotta; + mappings[276] = ItemType.GreenTerracotta; + mappings[277] = ItemType.RedTerracotta; + mappings[278] = ItemType.BlackTerracotta; + mappings[279] = ItemType.Barrier; + mappings[280] = ItemType.IronTrapdoor; + mappings[281] = ItemType.HayBlock; + mappings[282] = ItemType.WhiteCarpet; + mappings[283] = ItemType.OrangeCarpet; + mappings[284] = ItemType.MagentaCarpet; + mappings[285] = ItemType.LightBlueCarpet; + mappings[286] = ItemType.YellowCarpet; + mappings[287] = ItemType.LimeCarpet; + mappings[288] = ItemType.PinkCarpet; + mappings[289] = ItemType.GrayCarpet; + mappings[290] = ItemType.LightGrayCarpet; + mappings[291] = ItemType.CyanCarpet; + mappings[292] = ItemType.PurpleCarpet; + mappings[293] = ItemType.BlueCarpet; + mappings[294] = ItemType.BrownCarpet; + mappings[295] = ItemType.GreenCarpet; + mappings[296] = ItemType.RedCarpet; + mappings[297] = ItemType.BlackCarpet; + mappings[298] = ItemType.Terracotta; + mappings[299] = ItemType.CoalBlock; + mappings[300] = ItemType.PackedIce; + mappings[301] = ItemType.AcaciaStairs; + mappings[302] = ItemType.DarkOakStairs; + mappings[303] = ItemType.SlimeBlock; + mappings[304] = ItemType.DirtPath; + mappings[305] = ItemType.Sunflower; + mappings[306] = ItemType.Lilac; + mappings[307] = ItemType.RoseBush; + mappings[308] = ItemType.Peony; + mappings[309] = ItemType.TallGrass; + mappings[310] = ItemType.LargeFern; + mappings[311] = ItemType.WhiteStainedGlass; + mappings[312] = ItemType.OrangeStainedGlass; + mappings[313] = ItemType.MagentaStainedGlass; + mappings[314] = ItemType.LightBlueStainedGlass; + mappings[315] = ItemType.YellowStainedGlass; + mappings[316] = ItemType.LimeStainedGlass; + mappings[317] = ItemType.PinkStainedGlass; + mappings[318] = ItemType.GrayStainedGlass; + mappings[319] = ItemType.LightGrayStainedGlass; + mappings[320] = ItemType.CyanStainedGlass; + mappings[321] = ItemType.PurpleStainedGlass; + mappings[322] = ItemType.BlueStainedGlass; + mappings[323] = ItemType.BrownStainedGlass; + mappings[324] = ItemType.GreenStainedGlass; + mappings[325] = ItemType.RedStainedGlass; + mappings[326] = ItemType.BlackStainedGlass; + mappings[327] = ItemType.WhiteStainedGlassPane; + mappings[328] = ItemType.OrangeStainedGlassPane; + mappings[329] = ItemType.MagentaStainedGlassPane; + mappings[330] = ItemType.LightBlueStainedGlassPane; + mappings[331] = ItemType.YellowStainedGlassPane; + mappings[332] = ItemType.LimeStainedGlassPane; + mappings[333] = ItemType.PinkStainedGlassPane; + mappings[334] = ItemType.GrayStainedGlassPane; + mappings[335] = ItemType.LightGrayStainedGlassPane; + mappings[336] = ItemType.CyanStainedGlassPane; + mappings[337] = ItemType.PurpleStainedGlassPane; + mappings[338] = ItemType.BlueStainedGlassPane; + mappings[339] = ItemType.BrownStainedGlassPane; + mappings[340] = ItemType.GreenStainedGlassPane; + mappings[341] = ItemType.RedStainedGlassPane; + mappings[342] = ItemType.BlackStainedGlassPane; + mappings[343] = ItemType.Prismarine; + mappings[344] = ItemType.PrismarineBricks; + mappings[345] = ItemType.DarkPrismarine; + mappings[346] = ItemType.PrismarineStairs; + mappings[347] = ItemType.PrismarineBrickStairs; + mappings[348] = ItemType.DarkPrismarineStairs; + mappings[349] = ItemType.SeaLantern; + mappings[350] = ItemType.RedSandstone; + mappings[351] = ItemType.ChiseledRedSandstone; + mappings[352] = ItemType.CutRedSandstone; + mappings[353] = ItemType.RedSandstoneStairs; + mappings[354] = ItemType.RepeatingCommandBlock; + mappings[355] = ItemType.ChainCommandBlock; + mappings[356] = ItemType.MagmaBlock; + mappings[357] = ItemType.NetherWartBlock; + mappings[358] = ItemType.RedNetherBricks; + mappings[359] = ItemType.BoneBlock; + mappings[360] = ItemType.StructureVoid; + mappings[361] = ItemType.Observer; + mappings[362] = ItemType.ShulkerBox; + mappings[363] = ItemType.WhiteShulkerBox; + mappings[364] = ItemType.OrangeShulkerBox; + mappings[365] = ItemType.MagentaShulkerBox; + mappings[366] = ItemType.LightBlueShulkerBox; + mappings[367] = ItemType.YellowShulkerBox; + mappings[368] = ItemType.LimeShulkerBox; + mappings[369] = ItemType.PinkShulkerBox; + mappings[370] = ItemType.GrayShulkerBox; + mappings[371] = ItemType.LightGrayShulkerBox; + mappings[372] = ItemType.CyanShulkerBox; + mappings[373] = ItemType.PurpleShulkerBox; + mappings[374] = ItemType.BlueShulkerBox; + mappings[375] = ItemType.BrownShulkerBox; + mappings[376] = ItemType.GreenShulkerBox; + mappings[377] = ItemType.RedShulkerBox; + mappings[378] = ItemType.BlackShulkerBox; + mappings[379] = ItemType.WhiteGlazedTerracotta; + mappings[380] = ItemType.OrangeGlazedTerracotta; + mappings[381] = ItemType.MagentaGlazedTerracotta; + mappings[382] = ItemType.LightBlueGlazedTerracotta; + mappings[383] = ItemType.YellowGlazedTerracotta; + mappings[384] = ItemType.LimeGlazedTerracotta; + mappings[385] = ItemType.PinkGlazedTerracotta; + mappings[386] = ItemType.GrayGlazedTerracotta; + mappings[387] = ItemType.LightGrayGlazedTerracotta; + mappings[388] = ItemType.CyanGlazedTerracotta; + mappings[389] = ItemType.PurpleGlazedTerracotta; + mappings[390] = ItemType.BlueGlazedTerracotta; + mappings[391] = ItemType.BrownGlazedTerracotta; + mappings[392] = ItemType.GreenGlazedTerracotta; + mappings[393] = ItemType.RedGlazedTerracotta; + mappings[394] = ItemType.BlackGlazedTerracotta; + mappings[395] = ItemType.WhiteConcrete; + mappings[396] = ItemType.OrangeConcrete; + mappings[397] = ItemType.MagentaConcrete; + mappings[398] = ItemType.LightBlueConcrete; + mappings[399] = ItemType.YellowConcrete; + mappings[400] = ItemType.LimeConcrete; + mappings[401] = ItemType.PinkConcrete; + mappings[402] = ItemType.GrayConcrete; + mappings[403] = ItemType.LightGrayConcrete; + mappings[404] = ItemType.CyanConcrete; + mappings[405] = ItemType.PurpleConcrete; + mappings[406] = ItemType.BlueConcrete; + mappings[407] = ItemType.BrownConcrete; + mappings[408] = ItemType.GreenConcrete; + mappings[409] = ItemType.RedConcrete; + mappings[410] = ItemType.BlackConcrete; + mappings[411] = ItemType.WhiteConcretePowder; + mappings[412] = ItemType.OrangeConcretePowder; + mappings[413] = ItemType.MagentaConcretePowder; + mappings[414] = ItemType.LightBlueConcretePowder; + mappings[415] = ItemType.YellowConcretePowder; + mappings[416] = ItemType.LimeConcretePowder; + mappings[417] = ItemType.PinkConcretePowder; + mappings[418] = ItemType.GrayConcretePowder; + mappings[419] = ItemType.LightGrayConcretePowder; + mappings[420] = ItemType.CyanConcretePowder; + mappings[421] = ItemType.PurpleConcretePowder; + mappings[422] = ItemType.BlueConcretePowder; + mappings[423] = ItemType.BrownConcretePowder; + mappings[424] = ItemType.GreenConcretePowder; + mappings[425] = ItemType.RedConcretePowder; + mappings[426] = ItemType.BlackConcretePowder; + mappings[427] = ItemType.TurtleEgg; + mappings[428] = ItemType.DeadTubeCoralBlock; + mappings[429] = ItemType.DeadBrainCoralBlock; + mappings[430] = ItemType.DeadBubbleCoralBlock; + mappings[431] = ItemType.DeadFireCoralBlock; + mappings[432] = ItemType.DeadHornCoralBlock; + mappings[433] = ItemType.TubeCoralBlock; + mappings[434] = ItemType.BrainCoralBlock; + mappings[435] = ItemType.BubbleCoralBlock; + mappings[436] = ItemType.FireCoralBlock; + mappings[437] = ItemType.HornCoralBlock; + mappings[438] = ItemType.TubeCoral; + mappings[439] = ItemType.BrainCoral; + mappings[440] = ItemType.BubbleCoral; + mappings[441] = ItemType.FireCoral; + mappings[442] = ItemType.HornCoral; + mappings[443] = ItemType.TubeCoralFan; + mappings[444] = ItemType.BrainCoralFan; + mappings[445] = ItemType.BubbleCoralFan; + mappings[446] = ItemType.FireCoralFan; + mappings[447] = ItemType.HornCoralFan; + mappings[448] = ItemType.DeadTubeCoralFan; + mappings[449] = ItemType.DeadBrainCoralFan; + mappings[450] = ItemType.DeadBubbleCoralFan; + mappings[451] = ItemType.DeadFireCoralFan; + mappings[452] = ItemType.DeadHornCoralFan; + mappings[453] = ItemType.BlueIce; + mappings[454] = ItemType.Conduit; + mappings[455] = ItemType.IronDoor; + mappings[456] = ItemType.OakDoor; + mappings[457] = ItemType.SpruceDoor; + mappings[458] = ItemType.BirchDoor; + mappings[459] = ItemType.JungleDoor; + mappings[460] = ItemType.AcaciaDoor; + mappings[461] = ItemType.DarkOakDoor; + mappings[462] = ItemType.Repeater; + mappings[463] = ItemType.Comparator; + mappings[464] = ItemType.StructureBlock; + mappings[465] = ItemType.TurtleHelmet; + mappings[466] = ItemType.TurtleScute; + mappings[467] = ItemType.IronShovel; + mappings[468] = ItemType.IronPickaxe; + mappings[469] = ItemType.IronAxe; + mappings[470] = ItemType.FlintAndSteel; + mappings[471] = ItemType.Apple; + mappings[472] = ItemType.Bow; + mappings[473] = ItemType.Arrow; + mappings[474] = ItemType.Coal; + mappings[475] = ItemType.Charcoal; + mappings[476] = ItemType.Diamond; + mappings[477] = ItemType.IronIngot; + mappings[478] = ItemType.GoldIngot; + mappings[479] = ItemType.IronSword; + mappings[480] = ItemType.WoodenSword; + mappings[481] = ItemType.WoodenShovel; + mappings[482] = ItemType.WoodenPickaxe; + mappings[483] = ItemType.WoodenAxe; + mappings[484] = ItemType.StoneSword; + mappings[485] = ItemType.StoneShovel; + mappings[486] = ItemType.StonePickaxe; + mappings[487] = ItemType.StoneAxe; + mappings[488] = ItemType.DiamondSword; + mappings[489] = ItemType.DiamondShovel; + mappings[490] = ItemType.DiamondPickaxe; + mappings[491] = ItemType.DiamondAxe; + mappings[492] = ItemType.Stick; + mappings[493] = ItemType.Bowl; + mappings[494] = ItemType.MushroomStew; + mappings[495] = ItemType.GoldenSword; + mappings[496] = ItemType.GoldenShovel; + mappings[497] = ItemType.GoldenPickaxe; + mappings[498] = ItemType.GoldenAxe; + mappings[499] = ItemType.String; + mappings[500] = ItemType.Feather; + mappings[501] = ItemType.Gunpowder; + mappings[502] = ItemType.WoodenHoe; + mappings[503] = ItemType.StoneHoe; + mappings[504] = ItemType.IronHoe; + mappings[505] = ItemType.DiamondHoe; + mappings[506] = ItemType.GoldenHoe; + mappings[507] = ItemType.WheatSeeds; + mappings[508] = ItemType.Wheat; + mappings[509] = ItemType.Bread; + mappings[510] = ItemType.LeatherHelmet; + mappings[511] = ItemType.LeatherChestplate; + mappings[512] = ItemType.LeatherLeggings; + mappings[513] = ItemType.LeatherBoots; + mappings[514] = ItemType.ChainmailHelmet; + mappings[515] = ItemType.ChainmailChestplate; + mappings[516] = ItemType.ChainmailLeggings; + mappings[517] = ItemType.ChainmailBoots; + mappings[518] = ItemType.IronHelmet; + mappings[519] = ItemType.IronChestplate; + mappings[520] = ItemType.IronLeggings; + mappings[521] = ItemType.IronBoots; + mappings[522] = ItemType.DiamondHelmet; + mappings[523] = ItemType.DiamondChestplate; + mappings[524] = ItemType.DiamondLeggings; + mappings[525] = ItemType.DiamondBoots; + mappings[526] = ItemType.GoldenHelmet; + mappings[527] = ItemType.GoldenChestplate; + mappings[528] = ItemType.GoldenLeggings; + mappings[529] = ItemType.GoldenBoots; + mappings[530] = ItemType.Flint; + mappings[531] = ItemType.Porkchop; + mappings[532] = ItemType.CookedPorkchop; + mappings[533] = ItemType.Painting; + mappings[534] = ItemType.GoldenApple; + mappings[535] = ItemType.EnchantedGoldenApple; + mappings[536] = ItemType.OakSign; + mappings[537] = ItemType.Bucket; + mappings[538] = ItemType.WaterBucket; + mappings[539] = ItemType.LavaBucket; + mappings[540] = ItemType.Minecart; + mappings[541] = ItemType.Saddle; + mappings[542] = ItemType.Redstone; + mappings[543] = ItemType.Snowball; + mappings[544] = ItemType.OakBoat; + mappings[545] = ItemType.Leather; + mappings[546] = ItemType.MilkBucket; + mappings[547] = ItemType.PufferfishBucket; + mappings[548] = ItemType.SalmonBucket; + mappings[549] = ItemType.CodBucket; + mappings[550] = ItemType.TropicalFishBucket; + mappings[551] = ItemType.Brick; + mappings[552] = ItemType.ClayBall; + mappings[553] = ItemType.SugarCane; + mappings[554] = ItemType.Kelp; + mappings[555] = ItemType.DriedKelpBlock; + mappings[556] = ItemType.Paper; + mappings[557] = ItemType.Book; + mappings[558] = ItemType.SlimeBall; + mappings[559] = ItemType.ChestMinecart; + mappings[560] = ItemType.FurnaceMinecart; + mappings[561] = ItemType.Egg; + mappings[562] = ItemType.Compass; + mappings[563] = ItemType.FishingRod; + mappings[564] = ItemType.Clock; + mappings[565] = ItemType.GlowstoneDust; + mappings[566] = ItemType.Cod; + mappings[567] = ItemType.Salmon; + mappings[568] = ItemType.TropicalFish; + mappings[569] = ItemType.Pufferfish; + mappings[570] = ItemType.CookedCod; + mappings[571] = ItemType.CookedSalmon; + mappings[572] = ItemType.InkSac; + mappings[573] = ItemType.RedDye; + mappings[574] = ItemType.GreenDye; + mappings[575] = ItemType.CocoaBeans; + mappings[576] = ItemType.LapisLazuli; + mappings[577] = ItemType.PurpleDye; + mappings[578] = ItemType.CyanDye; + mappings[579] = ItemType.LightGrayDye; + mappings[580] = ItemType.GrayDye; + mappings[581] = ItemType.PinkDye; + mappings[582] = ItemType.LimeDye; + mappings[583] = ItemType.YellowDye; + mappings[584] = ItemType.LightBlueDye; + mappings[585] = ItemType.MagentaDye; + mappings[586] = ItemType.OrangeDye; + mappings[587] = ItemType.BoneMeal; + mappings[588] = ItemType.Bone; + mappings[589] = ItemType.Sugar; + mappings[590] = ItemType.Cake; + mappings[591] = ItemType.WhiteBed; + mappings[592] = ItemType.OrangeBed; + mappings[593] = ItemType.MagentaBed; + mappings[594] = ItemType.LightBlueBed; + mappings[595] = ItemType.YellowBed; + mappings[596] = ItemType.LimeBed; + mappings[597] = ItemType.PinkBed; + mappings[598] = ItemType.GrayBed; + mappings[599] = ItemType.LightGrayBed; + mappings[600] = ItemType.CyanBed; + mappings[601] = ItemType.PurpleBed; + mappings[602] = ItemType.BlueBed; + mappings[603] = ItemType.BrownBed; + mappings[604] = ItemType.GreenBed; + mappings[605] = ItemType.RedBed; + mappings[606] = ItemType.BlackBed; + mappings[607] = ItemType.Cookie; + mappings[608] = ItemType.FilledMap; + mappings[609] = ItemType.Shears; + mappings[610] = ItemType.MelonSlice; + mappings[611] = ItemType.DriedKelp; + mappings[612] = ItemType.PumpkinSeeds; + mappings[613] = ItemType.MelonSeeds; + mappings[614] = ItemType.Beef; + mappings[615] = ItemType.CookedBeef; + mappings[616] = ItemType.Chicken; + mappings[617] = ItemType.CookedChicken; + mappings[618] = ItemType.RottenFlesh; + mappings[619] = ItemType.EnderPearl; + mappings[620] = ItemType.BlazeRod; + mappings[621] = ItemType.GhastTear; + mappings[622] = ItemType.GoldNugget; + mappings[623] = ItemType.NetherWart; + mappings[624] = ItemType.Potion; + mappings[625] = ItemType.GlassBottle; + mappings[626] = ItemType.SpiderEye; + mappings[627] = ItemType.FermentedSpiderEye; + mappings[628] = ItemType.BlazePowder; + mappings[629] = ItemType.MagmaCream; + mappings[630] = ItemType.BrewingStand; + mappings[631] = ItemType.Cauldron; + mappings[632] = ItemType.EnderEye; + mappings[633] = ItemType.GlisteringMelonSlice; + mappings[634] = ItemType.BatSpawnEgg; + mappings[635] = ItemType.BlazeSpawnEgg; + mappings[636] = ItemType.CaveSpiderSpawnEgg; + mappings[637] = ItemType.ChickenSpawnEgg; + mappings[638] = ItemType.CodSpawnEgg; + mappings[639] = ItemType.CowSpawnEgg; + mappings[640] = ItemType.CreeperSpawnEgg; + mappings[641] = ItemType.DolphinSpawnEgg; + mappings[642] = ItemType.DonkeySpawnEgg; + mappings[643] = ItemType.DrownedSpawnEgg; + mappings[644] = ItemType.ElderGuardianSpawnEgg; + mappings[645] = ItemType.EndermanSpawnEgg; + mappings[646] = ItemType.EndermiteSpawnEgg; + mappings[647] = ItemType.EvokerSpawnEgg; + mappings[648] = ItemType.GhastSpawnEgg; + mappings[649] = ItemType.GuardianSpawnEgg; + mappings[650] = ItemType.HorseSpawnEgg; + mappings[651] = ItemType.HuskSpawnEgg; + mappings[652] = ItemType.LlamaSpawnEgg; + mappings[653] = ItemType.MagmaCubeSpawnEgg; + mappings[654] = ItemType.MooshroomSpawnEgg; + mappings[655] = ItemType.MuleSpawnEgg; + mappings[656] = ItemType.OcelotSpawnEgg; + mappings[657] = ItemType.ParrotSpawnEgg; + mappings[658] = ItemType.PhantomSpawnEgg; + mappings[659] = ItemType.PigSpawnEgg; + mappings[660] = ItemType.PolarBearSpawnEgg; + mappings[661] = ItemType.PufferfishSpawnEgg; + mappings[662] = ItemType.RabbitSpawnEgg; + mappings[663] = ItemType.SalmonSpawnEgg; + mappings[664] = ItemType.SheepSpawnEgg; + mappings[665] = ItemType.ShulkerSpawnEgg; + mappings[666] = ItemType.SilverfishSpawnEgg; + mappings[667] = ItemType.SkeletonSpawnEgg; + mappings[668] = ItemType.SkeletonHorseSpawnEgg; + mappings[669] = ItemType.SlimeSpawnEgg; + mappings[670] = ItemType.SpiderSpawnEgg; + mappings[671] = ItemType.SquidSpawnEgg; + mappings[672] = ItemType.StraySpawnEgg; + mappings[673] = ItemType.TropicalFishSpawnEgg; + mappings[674] = ItemType.TurtleSpawnEgg; + mappings[675] = ItemType.VexSpawnEgg; + mappings[676] = ItemType.VillagerSpawnEgg; + mappings[677] = ItemType.VindicatorSpawnEgg; + mappings[678] = ItemType.WitchSpawnEgg; + mappings[679] = ItemType.WitherSkeletonSpawnEgg; + mappings[680] = ItemType.WolfSpawnEgg; + mappings[681] = ItemType.ZombieSpawnEgg; + mappings[682] = ItemType.ZombieHorseSpawnEgg; + mappings[683] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[684] = ItemType.ZombieVillagerSpawnEgg; + mappings[685] = ItemType.ExperienceBottle; + mappings[686] = ItemType.FireCharge; + mappings[687] = ItemType.WritableBook; + mappings[688] = ItemType.WrittenBook; + mappings[689] = ItemType.Emerald; + mappings[690] = ItemType.ItemFrame; + mappings[691] = ItemType.FlowerPot; + mappings[692] = ItemType.Carrot; + mappings[693] = ItemType.Potato; + mappings[694] = ItemType.BakedPotato; + mappings[695] = ItemType.PoisonousPotato; + mappings[696] = ItemType.Map; + mappings[697] = ItemType.GoldenCarrot; + mappings[698] = ItemType.SkeletonSkull; + mappings[699] = ItemType.WitherSkeletonSkull; + mappings[700] = ItemType.PlayerHead; + mappings[701] = ItemType.ZombieHead; + mappings[702] = ItemType.CreeperHead; + mappings[703] = ItemType.DragonHead; + mappings[704] = ItemType.CarrotOnAStick; + mappings[705] = ItemType.NetherStar; + mappings[706] = ItemType.PumpkinPie; + mappings[707] = ItemType.FireworkRocket; + mappings[708] = ItemType.FireworkStar; + mappings[709] = ItemType.EnchantedBook; + mappings[710] = ItemType.NetherBrick; + mappings[711] = ItemType.Quartz; + mappings[712] = ItemType.TntMinecart; + mappings[713] = ItemType.HopperMinecart; + mappings[714] = ItemType.PrismarineShard; + mappings[715] = ItemType.PrismarineCrystals; + mappings[716] = ItemType.Rabbit; + mappings[717] = ItemType.CookedRabbit; + mappings[718] = ItemType.RabbitStew; + mappings[719] = ItemType.RabbitFoot; + mappings[720] = ItemType.RabbitHide; + mappings[721] = ItemType.ArmorStand; + mappings[722] = ItemType.IronHorseArmor; + mappings[723] = ItemType.GoldenHorseArmor; + mappings[724] = ItemType.DiamondHorseArmor; + mappings[725] = ItemType.Lead; + mappings[726] = ItemType.NameTag; + mappings[727] = ItemType.CommandBlockMinecart; + mappings[728] = ItemType.Mutton; + mappings[729] = ItemType.CookedMutton; + mappings[730] = ItemType.WhiteBanner; + mappings[731] = ItemType.OrangeBanner; + mappings[732] = ItemType.MagentaBanner; + mappings[733] = ItemType.LightBlueBanner; + mappings[734] = ItemType.YellowBanner; + mappings[735] = ItemType.LimeBanner; + mappings[736] = ItemType.PinkBanner; + mappings[737] = ItemType.GrayBanner; + mappings[738] = ItemType.LightGrayBanner; + mappings[739] = ItemType.CyanBanner; + mappings[740] = ItemType.PurpleBanner; + mappings[741] = ItemType.BlueBanner; + mappings[742] = ItemType.BrownBanner; + mappings[743] = ItemType.GreenBanner; + mappings[744] = ItemType.RedBanner; + mappings[745] = ItemType.BlackBanner; + mappings[746] = ItemType.EndCrystal; + mappings[747] = ItemType.ChorusFruit; + mappings[748] = ItemType.PoppedChorusFruit; + mappings[749] = ItemType.Beetroot; + mappings[750] = ItemType.BeetrootSeeds; + mappings[751] = ItemType.BeetrootSoup; + mappings[752] = ItemType.DragonBreath; + mappings[753] = ItemType.SplashPotion; + mappings[754] = ItemType.SpectralArrow; + mappings[755] = ItemType.TippedArrow; + mappings[756] = ItemType.LingeringPotion; + mappings[757] = ItemType.Shield; + mappings[758] = ItemType.Elytra; + mappings[759] = ItemType.SpruceBoat; + mappings[760] = ItemType.BirchBoat; + mappings[761] = ItemType.JungleBoat; + mappings[762] = ItemType.AcaciaBoat; + mappings[763] = ItemType.DarkOakBoat; + mappings[764] = ItemType.TotemOfUndying; + mappings[765] = ItemType.ShulkerShell; + mappings[766] = ItemType.IronNugget; + mappings[767] = ItemType.KnowledgeBook; + mappings[768] = ItemType.DebugStick; + mappings[769] = ItemType.MusicDisc13; + mappings[770] = ItemType.MusicDiscCat; + mappings[771] = ItemType.MusicDiscBlocks; + mappings[772] = ItemType.MusicDiscChirp; + mappings[773] = ItemType.MusicDiscFar; + mappings[774] = ItemType.MusicDiscMall; + mappings[775] = ItemType.MusicDiscMellohi; + mappings[776] = ItemType.MusicDiscStal; + mappings[777] = ItemType.MusicDiscStrad; + mappings[778] = ItemType.MusicDiscWard; + mappings[779] = ItemType.MusicDisc11; + mappings[780] = ItemType.MusicDiscWait; + mappings[781] = ItemType.Trident; + mappings[782] = ItemType.PhantomMembrane; + mappings[783] = ItemType.NautilusShell; + mappings[784] = ItemType.HeartOfTheSea; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1132.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1132.cs new file mode 100644 index 00000000..da38b041 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1132.cs @@ -0,0 +1,808 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1132 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1132() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.GrassBlock; + mappings[9] = ItemType.Dirt; + mappings[10] = ItemType.CoarseDirt; + mappings[11] = ItemType.Podzol; + mappings[12] = ItemType.Cobblestone; + mappings[13] = ItemType.OakPlanks; + mappings[14] = ItemType.SprucePlanks; + mappings[15] = ItemType.BirchPlanks; + mappings[16] = ItemType.JunglePlanks; + mappings[17] = ItemType.AcaciaPlanks; + mappings[18] = ItemType.DarkOakPlanks; + mappings[19] = ItemType.OakSapling; + mappings[20] = ItemType.SpruceSapling; + mappings[21] = ItemType.BirchSapling; + mappings[22] = ItemType.JungleSapling; + mappings[23] = ItemType.AcaciaSapling; + mappings[24] = ItemType.DarkOakSapling; + mappings[25] = ItemType.Bedrock; + mappings[26] = ItemType.Sand; + mappings[27] = ItemType.RedSand; + mappings[28] = ItemType.Gravel; + mappings[29] = ItemType.GoldOre; + mappings[30] = ItemType.IronOre; + mappings[31] = ItemType.CoalOre; + mappings[32] = ItemType.OakLog; + mappings[33] = ItemType.SpruceLog; + mappings[34] = ItemType.BirchLog; + mappings[35] = ItemType.JungleLog; + mappings[36] = ItemType.AcaciaLog; + mappings[37] = ItemType.DarkOakLog; + mappings[38] = ItemType.StrippedOakLog; + mappings[39] = ItemType.StrippedSpruceLog; + mappings[40] = ItemType.StrippedBirchLog; + mappings[41] = ItemType.StrippedJungleLog; + mappings[42] = ItemType.StrippedAcaciaLog; + mappings[43] = ItemType.StrippedDarkOakLog; + mappings[44] = ItemType.StrippedOakWood; + mappings[45] = ItemType.StrippedSpruceWood; + mappings[46] = ItemType.StrippedBirchWood; + mappings[47] = ItemType.StrippedJungleWood; + mappings[48] = ItemType.StrippedAcaciaWood; + mappings[49] = ItemType.StrippedDarkOakWood; + mappings[50] = ItemType.OakWood; + mappings[51] = ItemType.SpruceWood; + mappings[52] = ItemType.BirchWood; + mappings[53] = ItemType.JungleWood; + mappings[54] = ItemType.AcaciaWood; + mappings[55] = ItemType.DarkOakWood; + mappings[56] = ItemType.OakLeaves; + mappings[57] = ItemType.SpruceLeaves; + mappings[58] = ItemType.BirchLeaves; + mappings[59] = ItemType.JungleLeaves; + mappings[60] = ItemType.AcaciaLeaves; + mappings[61] = ItemType.DarkOakLeaves; + mappings[62] = ItemType.Sponge; + mappings[63] = ItemType.WetSponge; + mappings[64] = ItemType.Glass; + mappings[65] = ItemType.LapisOre; + mappings[66] = ItemType.LapisBlock; + mappings[67] = ItemType.Dispenser; + mappings[68] = ItemType.Sandstone; + mappings[69] = ItemType.ChiseledSandstone; + mappings[70] = ItemType.CutSandstone; + mappings[71] = ItemType.NoteBlock; + mappings[72] = ItemType.PoweredRail; + mappings[73] = ItemType.DetectorRail; + mappings[74] = ItemType.StickyPiston; + mappings[75] = ItemType.Cobweb; + mappings[76] = ItemType.ShortGrass; + mappings[77] = ItemType.Fern; + mappings[78] = ItemType.DeadBush; + mappings[79] = ItemType.Seagrass; + mappings[80] = ItemType.SeaPickle; + mappings[81] = ItemType.Piston; + mappings[82] = ItemType.WhiteWool; + mappings[83] = ItemType.OrangeWool; + mappings[84] = ItemType.MagentaWool; + mappings[85] = ItemType.LightBlueWool; + mappings[86] = ItemType.YellowWool; + mappings[87] = ItemType.LimeWool; + mappings[88] = ItemType.PinkWool; + mappings[89] = ItemType.GrayWool; + mappings[90] = ItemType.LightGrayWool; + mappings[91] = ItemType.CyanWool; + mappings[92] = ItemType.PurpleWool; + mappings[93] = ItemType.BlueWool; + mappings[94] = ItemType.BrownWool; + mappings[95] = ItemType.GreenWool; + mappings[96] = ItemType.RedWool; + mappings[97] = ItemType.BlackWool; + mappings[98] = ItemType.Dandelion; + mappings[99] = ItemType.Poppy; + mappings[100] = ItemType.BlueOrchid; + mappings[101] = ItemType.Allium; + mappings[102] = ItemType.AzureBluet; + mappings[103] = ItemType.RedTulip; + mappings[104] = ItemType.OrangeTulip; + mappings[105] = ItemType.WhiteTulip; + mappings[106] = ItemType.PinkTulip; + mappings[107] = ItemType.OxeyeDaisy; + mappings[108] = ItemType.BrownMushroom; + mappings[109] = ItemType.RedMushroom; + mappings[110] = ItemType.GoldBlock; + mappings[111] = ItemType.IronBlock; + mappings[112] = ItemType.OakSlab; + mappings[113] = ItemType.SpruceSlab; + mappings[114] = ItemType.BirchSlab; + mappings[115] = ItemType.JungleSlab; + mappings[116] = ItemType.AcaciaSlab; + mappings[117] = ItemType.DarkOakSlab; + mappings[118] = ItemType.StoneSlab; + mappings[119] = ItemType.SandstoneSlab; + mappings[120] = ItemType.PetrifiedOakSlab; + mappings[121] = ItemType.CobblestoneSlab; + mappings[122] = ItemType.BrickSlab; + mappings[123] = ItemType.StoneBrickSlab; + mappings[124] = ItemType.NetherBrickSlab; + mappings[125] = ItemType.QuartzSlab; + mappings[126] = ItemType.RedSandstoneSlab; + mappings[127] = ItemType.PurpurSlab; + mappings[128] = ItemType.PrismarineSlab; + mappings[129] = ItemType.PrismarineBrickSlab; + mappings[130] = ItemType.DarkPrismarineSlab; + mappings[131] = ItemType.SmoothQuartz; + mappings[132] = ItemType.SmoothRedSandstone; + mappings[133] = ItemType.SmoothSandstone; + mappings[134] = ItemType.SmoothStone; + mappings[135] = ItemType.Bricks; + mappings[136] = ItemType.Tnt; + mappings[137] = ItemType.Bookshelf; + mappings[138] = ItemType.MossyCobblestone; + mappings[139] = ItemType.Obsidian; + mappings[140] = ItemType.Torch; + mappings[141] = ItemType.EndRod; + mappings[142] = ItemType.ChorusPlant; + mappings[143] = ItemType.ChorusFlower; + mappings[144] = ItemType.PurpurBlock; + mappings[145] = ItemType.PurpurPillar; + mappings[146] = ItemType.PurpurStairs; + mappings[147] = ItemType.Spawner; + mappings[148] = ItemType.OakStairs; + mappings[149] = ItemType.Chest; + mappings[150] = ItemType.DiamondOre; + mappings[151] = ItemType.DiamondBlock; + mappings[152] = ItemType.CraftingTable; + mappings[153] = ItemType.Farmland; + mappings[154] = ItemType.Furnace; + mappings[155] = ItemType.Ladder; + mappings[156] = ItemType.Rail; + mappings[157] = ItemType.CobblestoneStairs; + mappings[158] = ItemType.Lever; + mappings[159] = ItemType.StonePressurePlate; + mappings[160] = ItemType.OakPressurePlate; + mappings[161] = ItemType.SprucePressurePlate; + mappings[162] = ItemType.BirchPressurePlate; + mappings[163] = ItemType.JunglePressurePlate; + mappings[164] = ItemType.AcaciaPressurePlate; + mappings[165] = ItemType.DarkOakPressurePlate; + mappings[166] = ItemType.RedstoneOre; + mappings[167] = ItemType.RedstoneTorch; + mappings[168] = ItemType.StoneButton; + mappings[169] = ItemType.Snow; + mappings[170] = ItemType.Ice; + mappings[171] = ItemType.SnowBlock; + mappings[172] = ItemType.Cactus; + mappings[173] = ItemType.Clay; + mappings[174] = ItemType.Jukebox; + mappings[175] = ItemType.OakFence; + mappings[176] = ItemType.SpruceFence; + mappings[177] = ItemType.BirchFence; + mappings[178] = ItemType.JungleFence; + mappings[179] = ItemType.AcaciaFence; + mappings[180] = ItemType.DarkOakFence; + mappings[181] = ItemType.Pumpkin; + mappings[182] = ItemType.CarvedPumpkin; + mappings[183] = ItemType.Netherrack; + mappings[184] = ItemType.SoulSand; + mappings[185] = ItemType.Glowstone; + mappings[186] = ItemType.JackOLantern; + mappings[187] = ItemType.OakTrapdoor; + mappings[188] = ItemType.SpruceTrapdoor; + mappings[189] = ItemType.BirchTrapdoor; + mappings[190] = ItemType.JungleTrapdoor; + mappings[191] = ItemType.AcaciaTrapdoor; + mappings[192] = ItemType.DarkOakTrapdoor; + mappings[193] = ItemType.InfestedStone; + mappings[194] = ItemType.InfestedCobblestone; + mappings[195] = ItemType.InfestedStoneBricks; + mappings[196] = ItemType.InfestedMossyStoneBricks; + mappings[197] = ItemType.InfestedCrackedStoneBricks; + mappings[198] = ItemType.InfestedChiseledStoneBricks; + mappings[199] = ItemType.StoneBricks; + mappings[200] = ItemType.MossyStoneBricks; + mappings[201] = ItemType.CrackedStoneBricks; + mappings[202] = ItemType.ChiseledStoneBricks; + mappings[203] = ItemType.BrownMushroomBlock; + mappings[204] = ItemType.RedMushroomBlock; + mappings[205] = ItemType.MushroomStem; + mappings[206] = ItemType.IronBars; + mappings[207] = ItemType.GlassPane; + mappings[208] = ItemType.Melon; + mappings[209] = ItemType.Vine; + mappings[210] = ItemType.OakFenceGate; + mappings[211] = ItemType.SpruceFenceGate; + mappings[212] = ItemType.BirchFenceGate; + mappings[213] = ItemType.JungleFenceGate; + mappings[214] = ItemType.AcaciaFenceGate; + mappings[215] = ItemType.DarkOakFenceGate; + mappings[216] = ItemType.BrickStairs; + mappings[217] = ItemType.StoneBrickStairs; + mappings[218] = ItemType.Mycelium; + mappings[219] = ItemType.LilyPad; + mappings[220] = ItemType.NetherBricks; + mappings[221] = ItemType.NetherBrickFence; + mappings[222] = ItemType.NetherBrickStairs; + mappings[223] = ItemType.EnchantingTable; + mappings[224] = ItemType.EndPortalFrame; + mappings[225] = ItemType.EndStone; + mappings[226] = ItemType.EndStoneBricks; + mappings[227] = ItemType.DragonEgg; + mappings[228] = ItemType.RedstoneLamp; + mappings[229] = ItemType.SandstoneStairs; + mappings[230] = ItemType.EmeraldOre; + mappings[231] = ItemType.EnderChest; + mappings[232] = ItemType.TripwireHook; + mappings[233] = ItemType.EmeraldBlock; + mappings[234] = ItemType.SpruceStairs; + mappings[235] = ItemType.BirchStairs; + mappings[236] = ItemType.JungleStairs; + mappings[237] = ItemType.CommandBlock; + mappings[238] = ItemType.Beacon; + mappings[239] = ItemType.CobblestoneWall; + mappings[240] = ItemType.MossyCobblestoneWall; + mappings[241] = ItemType.OakButton; + mappings[242] = ItemType.SpruceButton; + mappings[243] = ItemType.BirchButton; + mappings[244] = ItemType.JungleButton; + mappings[245] = ItemType.AcaciaButton; + mappings[246] = ItemType.DarkOakButton; + mappings[247] = ItemType.Anvil; + mappings[248] = ItemType.ChippedAnvil; + mappings[249] = ItemType.DamagedAnvil; + mappings[250] = ItemType.TrappedChest; + mappings[251] = ItemType.LightWeightedPressurePlate; + mappings[252] = ItemType.HeavyWeightedPressurePlate; + mappings[253] = ItemType.DaylightDetector; + mappings[254] = ItemType.RedstoneBlock; + mappings[255] = ItemType.NetherQuartzOre; + mappings[256] = ItemType.Hopper; + mappings[257] = ItemType.ChiseledQuartzBlock; + mappings[258] = ItemType.QuartzBlock; + mappings[259] = ItemType.QuartzPillar; + mappings[260] = ItemType.QuartzStairs; + mappings[261] = ItemType.ActivatorRail; + mappings[262] = ItemType.Dropper; + mappings[263] = ItemType.WhiteTerracotta; + mappings[264] = ItemType.OrangeTerracotta; + mappings[265] = ItemType.MagentaTerracotta; + mappings[266] = ItemType.LightBlueTerracotta; + mappings[267] = ItemType.YellowTerracotta; + mappings[268] = ItemType.LimeTerracotta; + mappings[269] = ItemType.PinkTerracotta; + mappings[270] = ItemType.GrayTerracotta; + mappings[271] = ItemType.LightGrayTerracotta; + mappings[272] = ItemType.CyanTerracotta; + mappings[273] = ItemType.PurpleTerracotta; + mappings[274] = ItemType.BlueTerracotta; + mappings[275] = ItemType.BrownTerracotta; + mappings[276] = ItemType.GreenTerracotta; + mappings[277] = ItemType.RedTerracotta; + mappings[278] = ItemType.BlackTerracotta; + mappings[279] = ItemType.Barrier; + mappings[280] = ItemType.IronTrapdoor; + mappings[281] = ItemType.HayBlock; + mappings[282] = ItemType.WhiteCarpet; + mappings[283] = ItemType.OrangeCarpet; + mappings[284] = ItemType.MagentaCarpet; + mappings[285] = ItemType.LightBlueCarpet; + mappings[286] = ItemType.YellowCarpet; + mappings[287] = ItemType.LimeCarpet; + mappings[288] = ItemType.PinkCarpet; + mappings[289] = ItemType.GrayCarpet; + mappings[290] = ItemType.LightGrayCarpet; + mappings[291] = ItemType.CyanCarpet; + mappings[292] = ItemType.PurpleCarpet; + mappings[293] = ItemType.BlueCarpet; + mappings[294] = ItemType.BrownCarpet; + mappings[295] = ItemType.GreenCarpet; + mappings[296] = ItemType.RedCarpet; + mappings[297] = ItemType.BlackCarpet; + mappings[298] = ItemType.Terracotta; + mappings[299] = ItemType.CoalBlock; + mappings[300] = ItemType.PackedIce; + mappings[301] = ItemType.AcaciaStairs; + mappings[302] = ItemType.DarkOakStairs; + mappings[303] = ItemType.SlimeBlock; + mappings[304] = ItemType.DirtPath; + mappings[305] = ItemType.Sunflower; + mappings[306] = ItemType.Lilac; + mappings[307] = ItemType.RoseBush; + mappings[308] = ItemType.Peony; + mappings[309] = ItemType.TallGrass; + mappings[310] = ItemType.LargeFern; + mappings[311] = ItemType.WhiteStainedGlass; + mappings[312] = ItemType.OrangeStainedGlass; + mappings[313] = ItemType.MagentaStainedGlass; + mappings[314] = ItemType.LightBlueStainedGlass; + mappings[315] = ItemType.YellowStainedGlass; + mappings[316] = ItemType.LimeStainedGlass; + mappings[317] = ItemType.PinkStainedGlass; + mappings[318] = ItemType.GrayStainedGlass; + mappings[319] = ItemType.LightGrayStainedGlass; + mappings[320] = ItemType.CyanStainedGlass; + mappings[321] = ItemType.PurpleStainedGlass; + mappings[322] = ItemType.BlueStainedGlass; + mappings[323] = ItemType.BrownStainedGlass; + mappings[324] = ItemType.GreenStainedGlass; + mappings[325] = ItemType.RedStainedGlass; + mappings[326] = ItemType.BlackStainedGlass; + mappings[327] = ItemType.WhiteStainedGlassPane; + mappings[328] = ItemType.OrangeStainedGlassPane; + mappings[329] = ItemType.MagentaStainedGlassPane; + mappings[330] = ItemType.LightBlueStainedGlassPane; + mappings[331] = ItemType.YellowStainedGlassPane; + mappings[332] = ItemType.LimeStainedGlassPane; + mappings[333] = ItemType.PinkStainedGlassPane; + mappings[334] = ItemType.GrayStainedGlassPane; + mappings[335] = ItemType.LightGrayStainedGlassPane; + mappings[336] = ItemType.CyanStainedGlassPane; + mappings[337] = ItemType.PurpleStainedGlassPane; + mappings[338] = ItemType.BlueStainedGlassPane; + mappings[339] = ItemType.BrownStainedGlassPane; + mappings[340] = ItemType.GreenStainedGlassPane; + mappings[341] = ItemType.RedStainedGlassPane; + mappings[342] = ItemType.BlackStainedGlassPane; + mappings[343] = ItemType.Prismarine; + mappings[344] = ItemType.PrismarineBricks; + mappings[345] = ItemType.DarkPrismarine; + mappings[346] = ItemType.PrismarineStairs; + mappings[347] = ItemType.PrismarineBrickStairs; + mappings[348] = ItemType.DarkPrismarineStairs; + mappings[349] = ItemType.SeaLantern; + mappings[350] = ItemType.RedSandstone; + mappings[351] = ItemType.ChiseledRedSandstone; + mappings[352] = ItemType.CutRedSandstone; + mappings[353] = ItemType.RedSandstoneStairs; + mappings[354] = ItemType.RepeatingCommandBlock; + mappings[355] = ItemType.ChainCommandBlock; + mappings[356] = ItemType.MagmaBlock; + mappings[357] = ItemType.NetherWartBlock; + mappings[358] = ItemType.RedNetherBricks; + mappings[359] = ItemType.BoneBlock; + mappings[360] = ItemType.StructureVoid; + mappings[361] = ItemType.Observer; + mappings[362] = ItemType.ShulkerBox; + mappings[363] = ItemType.WhiteShulkerBox; + mappings[364] = ItemType.OrangeShulkerBox; + mappings[365] = ItemType.MagentaShulkerBox; + mappings[366] = ItemType.LightBlueShulkerBox; + mappings[367] = ItemType.YellowShulkerBox; + mappings[368] = ItemType.LimeShulkerBox; + mappings[369] = ItemType.PinkShulkerBox; + mappings[370] = ItemType.GrayShulkerBox; + mappings[371] = ItemType.LightGrayShulkerBox; + mappings[372] = ItemType.CyanShulkerBox; + mappings[373] = ItemType.PurpleShulkerBox; + mappings[374] = ItemType.BlueShulkerBox; + mappings[375] = ItemType.BrownShulkerBox; + mappings[376] = ItemType.GreenShulkerBox; + mappings[377] = ItemType.RedShulkerBox; + mappings[378] = ItemType.BlackShulkerBox; + mappings[379] = ItemType.WhiteGlazedTerracotta; + mappings[380] = ItemType.OrangeGlazedTerracotta; + mappings[381] = ItemType.MagentaGlazedTerracotta; + mappings[382] = ItemType.LightBlueGlazedTerracotta; + mappings[383] = ItemType.YellowGlazedTerracotta; + mappings[384] = ItemType.LimeGlazedTerracotta; + mappings[385] = ItemType.PinkGlazedTerracotta; + mappings[386] = ItemType.GrayGlazedTerracotta; + mappings[387] = ItemType.LightGrayGlazedTerracotta; + mappings[388] = ItemType.CyanGlazedTerracotta; + mappings[389] = ItemType.PurpleGlazedTerracotta; + mappings[390] = ItemType.BlueGlazedTerracotta; + mappings[391] = ItemType.BrownGlazedTerracotta; + mappings[392] = ItemType.GreenGlazedTerracotta; + mappings[393] = ItemType.RedGlazedTerracotta; + mappings[394] = ItemType.BlackGlazedTerracotta; + mappings[395] = ItemType.WhiteConcrete; + mappings[396] = ItemType.OrangeConcrete; + mappings[397] = ItemType.MagentaConcrete; + mappings[398] = ItemType.LightBlueConcrete; + mappings[399] = ItemType.YellowConcrete; + mappings[400] = ItemType.LimeConcrete; + mappings[401] = ItemType.PinkConcrete; + mappings[402] = ItemType.GrayConcrete; + mappings[403] = ItemType.LightGrayConcrete; + mappings[404] = ItemType.CyanConcrete; + mappings[405] = ItemType.PurpleConcrete; + mappings[406] = ItemType.BlueConcrete; + mappings[407] = ItemType.BrownConcrete; + mappings[408] = ItemType.GreenConcrete; + mappings[409] = ItemType.RedConcrete; + mappings[410] = ItemType.BlackConcrete; + mappings[411] = ItemType.WhiteConcretePowder; + mappings[412] = ItemType.OrangeConcretePowder; + mappings[413] = ItemType.MagentaConcretePowder; + mappings[414] = ItemType.LightBlueConcretePowder; + mappings[415] = ItemType.YellowConcretePowder; + mappings[416] = ItemType.LimeConcretePowder; + mappings[417] = ItemType.PinkConcretePowder; + mappings[418] = ItemType.GrayConcretePowder; + mappings[419] = ItemType.LightGrayConcretePowder; + mappings[420] = ItemType.CyanConcretePowder; + mappings[421] = ItemType.PurpleConcretePowder; + mappings[422] = ItemType.BlueConcretePowder; + mappings[423] = ItemType.BrownConcretePowder; + mappings[424] = ItemType.GreenConcretePowder; + mappings[425] = ItemType.RedConcretePowder; + mappings[426] = ItemType.BlackConcretePowder; + mappings[427] = ItemType.TurtleEgg; + mappings[428] = ItemType.DeadTubeCoralBlock; + mappings[429] = ItemType.DeadBrainCoralBlock; + mappings[430] = ItemType.DeadBubbleCoralBlock; + mappings[431] = ItemType.DeadFireCoralBlock; + mappings[432] = ItemType.DeadHornCoralBlock; + mappings[433] = ItemType.TubeCoralBlock; + mappings[434] = ItemType.BrainCoralBlock; + mappings[435] = ItemType.BubbleCoralBlock; + mappings[436] = ItemType.FireCoralBlock; + mappings[437] = ItemType.HornCoralBlock; + mappings[438] = ItemType.TubeCoral; + mappings[439] = ItemType.BrainCoral; + mappings[440] = ItemType.BubbleCoral; + mappings[441] = ItemType.FireCoral; + mappings[442] = ItemType.HornCoral; + mappings[443] = ItemType.DeadBrainCoral; + mappings[444] = ItemType.DeadBubbleCoral; + mappings[445] = ItemType.DeadFireCoral; + mappings[446] = ItemType.DeadHornCoral; + mappings[447] = ItemType.DeadTubeCoral; + mappings[448] = ItemType.TubeCoralFan; + mappings[449] = ItemType.BrainCoralFan; + mappings[450] = ItemType.BubbleCoralFan; + mappings[451] = ItemType.FireCoralFan; + mappings[452] = ItemType.HornCoralFan; + mappings[453] = ItemType.DeadTubeCoralFan; + mappings[454] = ItemType.DeadBrainCoralFan; + mappings[455] = ItemType.DeadBubbleCoralFan; + mappings[456] = ItemType.DeadFireCoralFan; + mappings[457] = ItemType.DeadHornCoralFan; + mappings[458] = ItemType.BlueIce; + mappings[459] = ItemType.Conduit; + mappings[460] = ItemType.IronDoor; + mappings[461] = ItemType.OakDoor; + mappings[462] = ItemType.SpruceDoor; + mappings[463] = ItemType.BirchDoor; + mappings[464] = ItemType.JungleDoor; + mappings[465] = ItemType.AcaciaDoor; + mappings[466] = ItemType.DarkOakDoor; + mappings[467] = ItemType.Repeater; + mappings[468] = ItemType.Comparator; + mappings[469] = ItemType.StructureBlock; + mappings[470] = ItemType.TurtleHelmet; + mappings[471] = ItemType.TurtleScute; + mappings[472] = ItemType.IronShovel; + mappings[473] = ItemType.IronPickaxe; + mappings[474] = ItemType.IronAxe; + mappings[475] = ItemType.FlintAndSteel; + mappings[476] = ItemType.Apple; + mappings[477] = ItemType.Bow; + mappings[478] = ItemType.Arrow; + mappings[479] = ItemType.Coal; + mappings[480] = ItemType.Charcoal; + mappings[481] = ItemType.Diamond; + mappings[482] = ItemType.IronIngot; + mappings[483] = ItemType.GoldIngot; + mappings[484] = ItemType.IronSword; + mappings[485] = ItemType.WoodenSword; + mappings[486] = ItemType.WoodenShovel; + mappings[487] = ItemType.WoodenPickaxe; + mappings[488] = ItemType.WoodenAxe; + mappings[489] = ItemType.StoneSword; + mappings[490] = ItemType.StoneShovel; + mappings[491] = ItemType.StonePickaxe; + mappings[492] = ItemType.StoneAxe; + mappings[493] = ItemType.DiamondSword; + mappings[494] = ItemType.DiamondShovel; + mappings[495] = ItemType.DiamondPickaxe; + mappings[496] = ItemType.DiamondAxe; + mappings[497] = ItemType.Stick; + mappings[498] = ItemType.Bowl; + mappings[499] = ItemType.MushroomStew; + mappings[500] = ItemType.GoldenSword; + mappings[501] = ItemType.GoldenShovel; + mappings[502] = ItemType.GoldenPickaxe; + mappings[503] = ItemType.GoldenAxe; + mappings[504] = ItemType.String; + mappings[505] = ItemType.Feather; + mappings[506] = ItemType.Gunpowder; + mappings[507] = ItemType.WoodenHoe; + mappings[508] = ItemType.StoneHoe; + mappings[509] = ItemType.IronHoe; + mappings[510] = ItemType.DiamondHoe; + mappings[511] = ItemType.GoldenHoe; + mappings[512] = ItemType.WheatSeeds; + mappings[513] = ItemType.Wheat; + mappings[514] = ItemType.Bread; + mappings[515] = ItemType.LeatherHelmet; + mappings[516] = ItemType.LeatherChestplate; + mappings[517] = ItemType.LeatherLeggings; + mappings[518] = ItemType.LeatherBoots; + mappings[519] = ItemType.ChainmailHelmet; + mappings[520] = ItemType.ChainmailChestplate; + mappings[521] = ItemType.ChainmailLeggings; + mappings[522] = ItemType.ChainmailBoots; + mappings[523] = ItemType.IronHelmet; + mappings[524] = ItemType.IronChestplate; + mappings[525] = ItemType.IronLeggings; + mappings[526] = ItemType.IronBoots; + mappings[527] = ItemType.DiamondHelmet; + mappings[528] = ItemType.DiamondChestplate; + mappings[529] = ItemType.DiamondLeggings; + mappings[530] = ItemType.DiamondBoots; + mappings[531] = ItemType.GoldenHelmet; + mappings[532] = ItemType.GoldenChestplate; + mappings[533] = ItemType.GoldenLeggings; + mappings[534] = ItemType.GoldenBoots; + mappings[535] = ItemType.Flint; + mappings[536] = ItemType.Porkchop; + mappings[537] = ItemType.CookedPorkchop; + mappings[538] = ItemType.Painting; + mappings[539] = ItemType.GoldenApple; + mappings[540] = ItemType.EnchantedGoldenApple; + mappings[541] = ItemType.OakSign; + mappings[542] = ItemType.Bucket; + mappings[543] = ItemType.WaterBucket; + mappings[544] = ItemType.LavaBucket; + mappings[545] = ItemType.Minecart; + mappings[546] = ItemType.Saddle; + mappings[547] = ItemType.Redstone; + mappings[548] = ItemType.Snowball; + mappings[549] = ItemType.OakBoat; + mappings[550] = ItemType.Leather; + mappings[551] = ItemType.MilkBucket; + mappings[552] = ItemType.PufferfishBucket; + mappings[553] = ItemType.SalmonBucket; + mappings[554] = ItemType.CodBucket; + mappings[555] = ItemType.TropicalFishBucket; + mappings[556] = ItemType.Brick; + mappings[557] = ItemType.ClayBall; + mappings[558] = ItemType.SugarCane; + mappings[559] = ItemType.Kelp; + mappings[560] = ItemType.DriedKelpBlock; + mappings[561] = ItemType.Paper; + mappings[562] = ItemType.Book; + mappings[563] = ItemType.SlimeBall; + mappings[564] = ItemType.ChestMinecart; + mappings[565] = ItemType.FurnaceMinecart; + mappings[566] = ItemType.Egg; + mappings[567] = ItemType.Compass; + mappings[568] = ItemType.FishingRod; + mappings[569] = ItemType.Clock; + mappings[570] = ItemType.GlowstoneDust; + mappings[571] = ItemType.Cod; + mappings[572] = ItemType.Salmon; + mappings[573] = ItemType.TropicalFish; + mappings[574] = ItemType.Pufferfish; + mappings[575] = ItemType.CookedCod; + mappings[576] = ItemType.CookedSalmon; + mappings[577] = ItemType.InkSac; + mappings[578] = ItemType.RedDye; + mappings[579] = ItemType.GreenDye; + mappings[580] = ItemType.CocoaBeans; + mappings[581] = ItemType.LapisLazuli; + mappings[582] = ItemType.PurpleDye; + mappings[583] = ItemType.CyanDye; + mappings[584] = ItemType.LightGrayDye; + mappings[585] = ItemType.GrayDye; + mappings[586] = ItemType.PinkDye; + mappings[587] = ItemType.LimeDye; + mappings[588] = ItemType.YellowDye; + mappings[589] = ItemType.LightBlueDye; + mappings[590] = ItemType.MagentaDye; + mappings[591] = ItemType.OrangeDye; + mappings[592] = ItemType.BoneMeal; + mappings[593] = ItemType.Bone; + mappings[594] = ItemType.Sugar; + mappings[595] = ItemType.Cake; + mappings[596] = ItemType.WhiteBed; + mappings[597] = ItemType.OrangeBed; + mappings[598] = ItemType.MagentaBed; + mappings[599] = ItemType.LightBlueBed; + mappings[600] = ItemType.YellowBed; + mappings[601] = ItemType.LimeBed; + mappings[602] = ItemType.PinkBed; + mappings[603] = ItemType.GrayBed; + mappings[604] = ItemType.LightGrayBed; + mappings[605] = ItemType.CyanBed; + mappings[606] = ItemType.PurpleBed; + mappings[607] = ItemType.BlueBed; + mappings[608] = ItemType.BrownBed; + mappings[609] = ItemType.GreenBed; + mappings[610] = ItemType.RedBed; + mappings[611] = ItemType.BlackBed; + mappings[612] = ItemType.Cookie; + mappings[613] = ItemType.FilledMap; + mappings[614] = ItemType.Shears; + mappings[615] = ItemType.MelonSlice; + mappings[616] = ItemType.DriedKelp; + mappings[617] = ItemType.PumpkinSeeds; + mappings[618] = ItemType.MelonSeeds; + mappings[619] = ItemType.Beef; + mappings[620] = ItemType.CookedBeef; + mappings[621] = ItemType.Chicken; + mappings[622] = ItemType.CookedChicken; + mappings[623] = ItemType.RottenFlesh; + mappings[624] = ItemType.EnderPearl; + mappings[625] = ItemType.BlazeRod; + mappings[626] = ItemType.GhastTear; + mappings[627] = ItemType.GoldNugget; + mappings[628] = ItemType.NetherWart; + mappings[629] = ItemType.Potion; + mappings[630] = ItemType.GlassBottle; + mappings[631] = ItemType.SpiderEye; + mappings[632] = ItemType.FermentedSpiderEye; + mappings[633] = ItemType.BlazePowder; + mappings[634] = ItemType.MagmaCream; + mappings[635] = ItemType.BrewingStand; + mappings[636] = ItemType.Cauldron; + mappings[637] = ItemType.EnderEye; + mappings[638] = ItemType.GlisteringMelonSlice; + mappings[639] = ItemType.BatSpawnEgg; + mappings[640] = ItemType.BlazeSpawnEgg; + mappings[641] = ItemType.CaveSpiderSpawnEgg; + mappings[642] = ItemType.ChickenSpawnEgg; + mappings[643] = ItemType.CodSpawnEgg; + mappings[644] = ItemType.CowSpawnEgg; + mappings[645] = ItemType.CreeperSpawnEgg; + mappings[646] = ItemType.DolphinSpawnEgg; + mappings[647] = ItemType.DonkeySpawnEgg; + mappings[648] = ItemType.DrownedSpawnEgg; + mappings[649] = ItemType.ElderGuardianSpawnEgg; + mappings[650] = ItemType.EndermanSpawnEgg; + mappings[651] = ItemType.EndermiteSpawnEgg; + mappings[652] = ItemType.EvokerSpawnEgg; + mappings[653] = ItemType.GhastSpawnEgg; + mappings[654] = ItemType.GuardianSpawnEgg; + mappings[655] = ItemType.HorseSpawnEgg; + mappings[656] = ItemType.HuskSpawnEgg; + mappings[657] = ItemType.LlamaSpawnEgg; + mappings[658] = ItemType.MagmaCubeSpawnEgg; + mappings[659] = ItemType.MooshroomSpawnEgg; + mappings[660] = ItemType.MuleSpawnEgg; + mappings[661] = ItemType.OcelotSpawnEgg; + mappings[662] = ItemType.ParrotSpawnEgg; + mappings[663] = ItemType.PhantomSpawnEgg; + mappings[664] = ItemType.PigSpawnEgg; + mappings[665] = ItemType.PolarBearSpawnEgg; + mappings[666] = ItemType.PufferfishSpawnEgg; + mappings[667] = ItemType.RabbitSpawnEgg; + mappings[668] = ItemType.SalmonSpawnEgg; + mappings[669] = ItemType.SheepSpawnEgg; + mappings[670] = ItemType.ShulkerSpawnEgg; + mappings[671] = ItemType.SilverfishSpawnEgg; + mappings[672] = ItemType.SkeletonSpawnEgg; + mappings[673] = ItemType.SkeletonHorseSpawnEgg; + mappings[674] = ItemType.SlimeSpawnEgg; + mappings[675] = ItemType.SpiderSpawnEgg; + mappings[676] = ItemType.SquidSpawnEgg; + mappings[677] = ItemType.StraySpawnEgg; + mappings[678] = ItemType.TropicalFishSpawnEgg; + mappings[679] = ItemType.TurtleSpawnEgg; + mappings[680] = ItemType.VexSpawnEgg; + mappings[681] = ItemType.VillagerSpawnEgg; + mappings[682] = ItemType.VindicatorSpawnEgg; + mappings[683] = ItemType.WitchSpawnEgg; + mappings[684] = ItemType.WitherSkeletonSpawnEgg; + mappings[685] = ItemType.WolfSpawnEgg; + mappings[686] = ItemType.ZombieSpawnEgg; + mappings[687] = ItemType.ZombieHorseSpawnEgg; + mappings[688] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[689] = ItemType.ZombieVillagerSpawnEgg; + mappings[690] = ItemType.ExperienceBottle; + mappings[691] = ItemType.FireCharge; + mappings[692] = ItemType.WritableBook; + mappings[693] = ItemType.WrittenBook; + mappings[694] = ItemType.Emerald; + mappings[695] = ItemType.ItemFrame; + mappings[696] = ItemType.FlowerPot; + mappings[697] = ItemType.Carrot; + mappings[698] = ItemType.Potato; + mappings[699] = ItemType.BakedPotato; + mappings[700] = ItemType.PoisonousPotato; + mappings[701] = ItemType.Map; + mappings[702] = ItemType.GoldenCarrot; + mappings[703] = ItemType.SkeletonSkull; + mappings[704] = ItemType.WitherSkeletonSkull; + mappings[705] = ItemType.PlayerHead; + mappings[706] = ItemType.ZombieHead; + mappings[707] = ItemType.CreeperHead; + mappings[708] = ItemType.DragonHead; + mappings[709] = ItemType.CarrotOnAStick; + mappings[710] = ItemType.NetherStar; + mappings[711] = ItemType.PumpkinPie; + mappings[712] = ItemType.FireworkRocket; + mappings[713] = ItemType.FireworkStar; + mappings[714] = ItemType.EnchantedBook; + mappings[715] = ItemType.NetherBrick; + mappings[716] = ItemType.Quartz; + mappings[717] = ItemType.TntMinecart; + mappings[718] = ItemType.HopperMinecart; + mappings[719] = ItemType.PrismarineShard; + mappings[720] = ItemType.PrismarineCrystals; + mappings[721] = ItemType.Rabbit; + mappings[722] = ItemType.CookedRabbit; + mappings[723] = ItemType.RabbitStew; + mappings[724] = ItemType.RabbitFoot; + mappings[725] = ItemType.RabbitHide; + mappings[726] = ItemType.ArmorStand; + mappings[727] = ItemType.IronHorseArmor; + mappings[728] = ItemType.GoldenHorseArmor; + mappings[729] = ItemType.DiamondHorseArmor; + mappings[730] = ItemType.Lead; + mappings[731] = ItemType.NameTag; + mappings[732] = ItemType.CommandBlockMinecart; + mappings[733] = ItemType.Mutton; + mappings[734] = ItemType.CookedMutton; + mappings[735] = ItemType.WhiteBanner; + mappings[736] = ItemType.OrangeBanner; + mappings[737] = ItemType.MagentaBanner; + mappings[738] = ItemType.LightBlueBanner; + mappings[739] = ItemType.YellowBanner; + mappings[740] = ItemType.LimeBanner; + mappings[741] = ItemType.PinkBanner; + mappings[742] = ItemType.GrayBanner; + mappings[743] = ItemType.LightGrayBanner; + mappings[744] = ItemType.CyanBanner; + mappings[745] = ItemType.PurpleBanner; + mappings[746] = ItemType.BlueBanner; + mappings[747] = ItemType.BrownBanner; + mappings[748] = ItemType.GreenBanner; + mappings[749] = ItemType.RedBanner; + mappings[750] = ItemType.BlackBanner; + mappings[751] = ItemType.EndCrystal; + mappings[752] = ItemType.ChorusFruit; + mappings[753] = ItemType.PoppedChorusFruit; + mappings[754] = ItemType.Beetroot; + mappings[755] = ItemType.BeetrootSeeds; + mappings[756] = ItemType.BeetrootSoup; + mappings[757] = ItemType.DragonBreath; + mappings[758] = ItemType.SplashPotion; + mappings[759] = ItemType.SpectralArrow; + mappings[760] = ItemType.TippedArrow; + mappings[761] = ItemType.LingeringPotion; + mappings[762] = ItemType.Shield; + mappings[763] = ItemType.Elytra; + mappings[764] = ItemType.SpruceBoat; + mappings[765] = ItemType.BirchBoat; + mappings[766] = ItemType.JungleBoat; + mappings[767] = ItemType.AcaciaBoat; + mappings[768] = ItemType.DarkOakBoat; + mappings[769] = ItemType.TotemOfUndying; + mappings[770] = ItemType.ShulkerShell; + mappings[771] = ItemType.IronNugget; + mappings[772] = ItemType.KnowledgeBook; + mappings[773] = ItemType.DebugStick; + mappings[774] = ItemType.MusicDisc13; + mappings[775] = ItemType.MusicDiscCat; + mappings[776] = ItemType.MusicDiscBlocks; + mappings[777] = ItemType.MusicDiscChirp; + mappings[778] = ItemType.MusicDiscFar; + mappings[779] = ItemType.MusicDiscMall; + mappings[780] = ItemType.MusicDiscMellohi; + mappings[781] = ItemType.MusicDiscStal; + mappings[782] = ItemType.MusicDiscStrad; + mappings[783] = ItemType.MusicDiscWard; + mappings[784] = ItemType.MusicDisc11; + mappings[785] = ItemType.MusicDiscWait; + mappings[786] = ItemType.Trident; + mappings[787] = ItemType.PhantomMembrane; + mappings[788] = ItemType.NautilusShell; + mappings[789] = ItemType.HeartOfTheSea; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette114.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette114.cs new file mode 100644 index 00000000..c84079da --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette114.cs @@ -0,0 +1,895 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette114 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette114() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.GrassBlock; + mappings[9] = ItemType.Dirt; + mappings[10] = ItemType.CoarseDirt; + mappings[11] = ItemType.Podzol; + mappings[12] = ItemType.Cobblestone; + mappings[13] = ItemType.OakPlanks; + mappings[14] = ItemType.SprucePlanks; + mappings[15] = ItemType.BirchPlanks; + mappings[16] = ItemType.JunglePlanks; + mappings[17] = ItemType.AcaciaPlanks; + mappings[18] = ItemType.DarkOakPlanks; + mappings[19] = ItemType.OakSapling; + mappings[20] = ItemType.SpruceSapling; + mappings[21] = ItemType.BirchSapling; + mappings[22] = ItemType.JungleSapling; + mappings[23] = ItemType.AcaciaSapling; + mappings[24] = ItemType.DarkOakSapling; + mappings[25] = ItemType.Bedrock; + mappings[26] = ItemType.Sand; + mappings[27] = ItemType.RedSand; + mappings[28] = ItemType.Gravel; + mappings[29] = ItemType.GoldOre; + mappings[30] = ItemType.IronOre; + mappings[31] = ItemType.CoalOre; + mappings[32] = ItemType.OakLog; + mappings[33] = ItemType.SpruceLog; + mappings[34] = ItemType.BirchLog; + mappings[35] = ItemType.JungleLog; + mappings[36] = ItemType.AcaciaLog; + mappings[37] = ItemType.DarkOakLog; + mappings[38] = ItemType.StrippedOakLog; + mappings[39] = ItemType.StrippedSpruceLog; + mappings[40] = ItemType.StrippedBirchLog; + mappings[41] = ItemType.StrippedJungleLog; + mappings[42] = ItemType.StrippedAcaciaLog; + mappings[43] = ItemType.StrippedDarkOakLog; + mappings[44] = ItemType.StrippedOakWood; + mappings[45] = ItemType.StrippedSpruceWood; + mappings[46] = ItemType.StrippedBirchWood; + mappings[47] = ItemType.StrippedJungleWood; + mappings[48] = ItemType.StrippedAcaciaWood; + mappings[49] = ItemType.StrippedDarkOakWood; + mappings[50] = ItemType.OakWood; + mappings[51] = ItemType.SpruceWood; + mappings[52] = ItemType.BirchWood; + mappings[53] = ItemType.JungleWood; + mappings[54] = ItemType.AcaciaWood; + mappings[55] = ItemType.DarkOakWood; + mappings[56] = ItemType.OakLeaves; + mappings[57] = ItemType.SpruceLeaves; + mappings[58] = ItemType.BirchLeaves; + mappings[59] = ItemType.JungleLeaves; + mappings[60] = ItemType.AcaciaLeaves; + mappings[61] = ItemType.DarkOakLeaves; + mappings[62] = ItemType.Sponge; + mappings[63] = ItemType.WetSponge; + mappings[64] = ItemType.Glass; + mappings[65] = ItemType.LapisOre; + mappings[66] = ItemType.LapisBlock; + mappings[67] = ItemType.Dispenser; + mappings[68] = ItemType.Sandstone; + mappings[69] = ItemType.ChiseledSandstone; + mappings[70] = ItemType.CutSandstone; + mappings[71] = ItemType.NoteBlock; + mappings[72] = ItemType.PoweredRail; + mappings[73] = ItemType.DetectorRail; + mappings[74] = ItemType.StickyPiston; + mappings[75] = ItemType.Cobweb; + mappings[76] = ItemType.ShortGrass; + mappings[77] = ItemType.Fern; + mappings[78] = ItemType.DeadBush; + mappings[79] = ItemType.Seagrass; + mappings[80] = ItemType.SeaPickle; + mappings[81] = ItemType.Piston; + mappings[82] = ItemType.WhiteWool; + mappings[83] = ItemType.OrangeWool; + mappings[84] = ItemType.MagentaWool; + mappings[85] = ItemType.LightBlueWool; + mappings[86] = ItemType.YellowWool; + mappings[87] = ItemType.LimeWool; + mappings[88] = ItemType.PinkWool; + mappings[89] = ItemType.GrayWool; + mappings[90] = ItemType.LightGrayWool; + mappings[91] = ItemType.CyanWool; + mappings[92] = ItemType.PurpleWool; + mappings[93] = ItemType.BlueWool; + mappings[94] = ItemType.BrownWool; + mappings[95] = ItemType.GreenWool; + mappings[96] = ItemType.RedWool; + mappings[97] = ItemType.BlackWool; + mappings[98] = ItemType.Dandelion; + mappings[99] = ItemType.Poppy; + mappings[100] = ItemType.BlueOrchid; + mappings[101] = ItemType.Allium; + mappings[102] = ItemType.AzureBluet; + mappings[103] = ItemType.RedTulip; + mappings[104] = ItemType.OrangeTulip; + mappings[105] = ItemType.WhiteTulip; + mappings[106] = ItemType.PinkTulip; + mappings[107] = ItemType.OxeyeDaisy; + mappings[108] = ItemType.Cornflower; + mappings[109] = ItemType.LilyOfTheValley; + mappings[110] = ItemType.WitherRose; + mappings[111] = ItemType.BrownMushroom; + mappings[112] = ItemType.RedMushroom; + mappings[113] = ItemType.GoldBlock; + mappings[114] = ItemType.IronBlock; + mappings[115] = ItemType.OakSlab; + mappings[116] = ItemType.SpruceSlab; + mappings[117] = ItemType.BirchSlab; + mappings[118] = ItemType.JungleSlab; + mappings[119] = ItemType.AcaciaSlab; + mappings[120] = ItemType.DarkOakSlab; + mappings[121] = ItemType.StoneSlab; + mappings[122] = ItemType.SmoothStoneSlab; + mappings[123] = ItemType.SandstoneSlab; + mappings[124] = ItemType.CutSandstoneSlab; + mappings[125] = ItemType.PetrifiedOakSlab; + mappings[126] = ItemType.CobblestoneSlab; + mappings[127] = ItemType.BrickSlab; + mappings[128] = ItemType.StoneBrickSlab; + mappings[129] = ItemType.NetherBrickSlab; + mappings[130] = ItemType.QuartzSlab; + mappings[131] = ItemType.RedSandstoneSlab; + mappings[132] = ItemType.CutRedSandstoneSlab; + mappings[133] = ItemType.PurpurSlab; + mappings[134] = ItemType.PrismarineSlab; + mappings[135] = ItemType.PrismarineBrickSlab; + mappings[136] = ItemType.DarkPrismarineSlab; + mappings[137] = ItemType.SmoothQuartz; + mappings[138] = ItemType.SmoothRedSandstone; + mappings[139] = ItemType.SmoothSandstone; + mappings[140] = ItemType.SmoothStone; + mappings[141] = ItemType.Bricks; + mappings[142] = ItemType.Tnt; + mappings[143] = ItemType.Bookshelf; + mappings[144] = ItemType.MossyCobblestone; + mappings[145] = ItemType.Obsidian; + mappings[146] = ItemType.Torch; + mappings[147] = ItemType.EndRod; + mappings[148] = ItemType.ChorusPlant; + mappings[149] = ItemType.ChorusFlower; + mappings[150] = ItemType.PurpurBlock; + mappings[151] = ItemType.PurpurPillar; + mappings[152] = ItemType.PurpurStairs; + mappings[153] = ItemType.Spawner; + mappings[154] = ItemType.OakStairs; + mappings[155] = ItemType.Chest; + mappings[156] = ItemType.DiamondOre; + mappings[157] = ItemType.DiamondBlock; + mappings[158] = ItemType.CraftingTable; + mappings[159] = ItemType.Farmland; + mappings[160] = ItemType.Furnace; + mappings[161] = ItemType.Ladder; + mappings[162] = ItemType.Rail; + mappings[163] = ItemType.CobblestoneStairs; + mappings[164] = ItemType.Lever; + mappings[165] = ItemType.StonePressurePlate; + mappings[166] = ItemType.OakPressurePlate; + mappings[167] = ItemType.SprucePressurePlate; + mappings[168] = ItemType.BirchPressurePlate; + mappings[169] = ItemType.JunglePressurePlate; + mappings[170] = ItemType.AcaciaPressurePlate; + mappings[171] = ItemType.DarkOakPressurePlate; + mappings[172] = ItemType.RedstoneOre; + mappings[173] = ItemType.RedstoneTorch; + mappings[174] = ItemType.StoneButton; + mappings[175] = ItemType.Snow; + mappings[176] = ItemType.Ice; + mappings[177] = ItemType.SnowBlock; + mappings[178] = ItemType.Cactus; + mappings[179] = ItemType.Clay; + mappings[180] = ItemType.Jukebox; + mappings[181] = ItemType.OakFence; + mappings[182] = ItemType.SpruceFence; + mappings[183] = ItemType.BirchFence; + mappings[184] = ItemType.JungleFence; + mappings[185] = ItemType.AcaciaFence; + mappings[186] = ItemType.DarkOakFence; + mappings[187] = ItemType.Pumpkin; + mappings[188] = ItemType.CarvedPumpkin; + mappings[189] = ItemType.Netherrack; + mappings[190] = ItemType.SoulSand; + mappings[191] = ItemType.Glowstone; + mappings[192] = ItemType.JackOLantern; + mappings[193] = ItemType.OakTrapdoor; + mappings[194] = ItemType.SpruceTrapdoor; + mappings[195] = ItemType.BirchTrapdoor; + mappings[196] = ItemType.JungleTrapdoor; + mappings[197] = ItemType.AcaciaTrapdoor; + mappings[198] = ItemType.DarkOakTrapdoor; + mappings[199] = ItemType.InfestedStone; + mappings[200] = ItemType.InfestedCobblestone; + mappings[201] = ItemType.InfestedStoneBricks; + mappings[202] = ItemType.InfestedMossyStoneBricks; + mappings[203] = ItemType.InfestedCrackedStoneBricks; + mappings[204] = ItemType.InfestedChiseledStoneBricks; + mappings[205] = ItemType.StoneBricks; + mappings[206] = ItemType.MossyStoneBricks; + mappings[207] = ItemType.CrackedStoneBricks; + mappings[208] = ItemType.ChiseledStoneBricks; + mappings[209] = ItemType.BrownMushroomBlock; + mappings[210] = ItemType.RedMushroomBlock; + mappings[211] = ItemType.MushroomStem; + mappings[212] = ItemType.IronBars; + mappings[213] = ItemType.GlassPane; + mappings[214] = ItemType.Melon; + mappings[215] = ItemType.Vine; + mappings[216] = ItemType.OakFenceGate; + mappings[217] = ItemType.SpruceFenceGate; + mappings[218] = ItemType.BirchFenceGate; + mappings[219] = ItemType.JungleFenceGate; + mappings[220] = ItemType.AcaciaFenceGate; + mappings[221] = ItemType.DarkOakFenceGate; + mappings[222] = ItemType.BrickStairs; + mappings[223] = ItemType.StoneBrickStairs; + mappings[224] = ItemType.Mycelium; + mappings[225] = ItemType.LilyPad; + mappings[226] = ItemType.NetherBricks; + mappings[227] = ItemType.NetherBrickFence; + mappings[228] = ItemType.NetherBrickStairs; + mappings[229] = ItemType.EnchantingTable; + mappings[230] = ItemType.EndPortalFrame; + mappings[231] = ItemType.EndStone; + mappings[232] = ItemType.EndStoneBricks; + mappings[233] = ItemType.DragonEgg; + mappings[234] = ItemType.RedstoneLamp; + mappings[235] = ItemType.SandstoneStairs; + mappings[236] = ItemType.EmeraldOre; + mappings[237] = ItemType.EnderChest; + mappings[238] = ItemType.TripwireHook; + mappings[239] = ItemType.EmeraldBlock; + mappings[240] = ItemType.SpruceStairs; + mappings[241] = ItemType.BirchStairs; + mappings[242] = ItemType.JungleStairs; + mappings[243] = ItemType.CommandBlock; + mappings[244] = ItemType.Beacon; + mappings[245] = ItemType.CobblestoneWall; + mappings[246] = ItemType.MossyCobblestoneWall; + mappings[247] = ItemType.BrickWall; + mappings[248] = ItemType.PrismarineWall; + mappings[249] = ItemType.RedSandstoneWall; + mappings[250] = ItemType.MossyStoneBrickWall; + mappings[251] = ItemType.GraniteWall; + mappings[252] = ItemType.StoneBrickWall; + mappings[253] = ItemType.NetherBrickWall; + mappings[254] = ItemType.AndesiteWall; + mappings[255] = ItemType.RedNetherBrickWall; + mappings[256] = ItemType.SandstoneWall; + mappings[257] = ItemType.EndStoneBrickWall; + mappings[258] = ItemType.DioriteWall; + mappings[259] = ItemType.OakButton; + mappings[260] = ItemType.SpruceButton; + mappings[261] = ItemType.BirchButton; + mappings[262] = ItemType.JungleButton; + mappings[263] = ItemType.AcaciaButton; + mappings[264] = ItemType.DarkOakButton; + mappings[265] = ItemType.Anvil; + mappings[266] = ItemType.ChippedAnvil; + mappings[267] = ItemType.DamagedAnvil; + mappings[268] = ItemType.TrappedChest; + mappings[269] = ItemType.LightWeightedPressurePlate; + mappings[270] = ItemType.HeavyWeightedPressurePlate; + mappings[271] = ItemType.DaylightDetector; + mappings[272] = ItemType.RedstoneBlock; + mappings[273] = ItemType.NetherQuartzOre; + mappings[274] = ItemType.Hopper; + mappings[275] = ItemType.ChiseledQuartzBlock; + mappings[276] = ItemType.QuartzBlock; + mappings[277] = ItemType.QuartzPillar; + mappings[278] = ItemType.QuartzStairs; + mappings[279] = ItemType.ActivatorRail; + mappings[280] = ItemType.Dropper; + mappings[281] = ItemType.WhiteTerracotta; + mappings[282] = ItemType.OrangeTerracotta; + mappings[283] = ItemType.MagentaTerracotta; + mappings[284] = ItemType.LightBlueTerracotta; + mappings[285] = ItemType.YellowTerracotta; + mappings[286] = ItemType.LimeTerracotta; + mappings[287] = ItemType.PinkTerracotta; + mappings[288] = ItemType.GrayTerracotta; + mappings[289] = ItemType.LightGrayTerracotta; + mappings[290] = ItemType.CyanTerracotta; + mappings[291] = ItemType.PurpleTerracotta; + mappings[292] = ItemType.BlueTerracotta; + mappings[293] = ItemType.BrownTerracotta; + mappings[294] = ItemType.GreenTerracotta; + mappings[295] = ItemType.RedTerracotta; + mappings[296] = ItemType.BlackTerracotta; + mappings[297] = ItemType.Barrier; + mappings[298] = ItemType.IronTrapdoor; + mappings[299] = ItemType.HayBlock; + mappings[300] = ItemType.WhiteCarpet; + mappings[301] = ItemType.OrangeCarpet; + mappings[302] = ItemType.MagentaCarpet; + mappings[303] = ItemType.LightBlueCarpet; + mappings[304] = ItemType.YellowCarpet; + mappings[305] = ItemType.LimeCarpet; + mappings[306] = ItemType.PinkCarpet; + mappings[307] = ItemType.GrayCarpet; + mappings[308] = ItemType.LightGrayCarpet; + mappings[309] = ItemType.CyanCarpet; + mappings[310] = ItemType.PurpleCarpet; + mappings[311] = ItemType.BlueCarpet; + mappings[312] = ItemType.BrownCarpet; + mappings[313] = ItemType.GreenCarpet; + mappings[314] = ItemType.RedCarpet; + mappings[315] = ItemType.BlackCarpet; + mappings[316] = ItemType.Terracotta; + mappings[317] = ItemType.CoalBlock; + mappings[318] = ItemType.PackedIce; + mappings[319] = ItemType.AcaciaStairs; + mappings[320] = ItemType.DarkOakStairs; + mappings[321] = ItemType.SlimeBlock; + mappings[322] = ItemType.DirtPath; + mappings[323] = ItemType.Sunflower; + mappings[324] = ItemType.Lilac; + mappings[325] = ItemType.RoseBush; + mappings[326] = ItemType.Peony; + mappings[327] = ItemType.TallGrass; + mappings[328] = ItemType.LargeFern; + mappings[329] = ItemType.WhiteStainedGlass; + mappings[330] = ItemType.OrangeStainedGlass; + mappings[331] = ItemType.MagentaStainedGlass; + mappings[332] = ItemType.LightBlueStainedGlass; + mappings[333] = ItemType.YellowStainedGlass; + mappings[334] = ItemType.LimeStainedGlass; + mappings[335] = ItemType.PinkStainedGlass; + mappings[336] = ItemType.GrayStainedGlass; + mappings[337] = ItemType.LightGrayStainedGlass; + mappings[338] = ItemType.CyanStainedGlass; + mappings[339] = ItemType.PurpleStainedGlass; + mappings[340] = ItemType.BlueStainedGlass; + mappings[341] = ItemType.BrownStainedGlass; + mappings[342] = ItemType.GreenStainedGlass; + mappings[343] = ItemType.RedStainedGlass; + mappings[344] = ItemType.BlackStainedGlass; + mappings[345] = ItemType.WhiteStainedGlassPane; + mappings[346] = ItemType.OrangeStainedGlassPane; + mappings[347] = ItemType.MagentaStainedGlassPane; + mappings[348] = ItemType.LightBlueStainedGlassPane; + mappings[349] = ItemType.YellowStainedGlassPane; + mappings[350] = ItemType.LimeStainedGlassPane; + mappings[351] = ItemType.PinkStainedGlassPane; + mappings[352] = ItemType.GrayStainedGlassPane; + mappings[353] = ItemType.LightGrayStainedGlassPane; + mappings[354] = ItemType.CyanStainedGlassPane; + mappings[355] = ItemType.PurpleStainedGlassPane; + mappings[356] = ItemType.BlueStainedGlassPane; + mappings[357] = ItemType.BrownStainedGlassPane; + mappings[358] = ItemType.GreenStainedGlassPane; + mappings[359] = ItemType.RedStainedGlassPane; + mappings[360] = ItemType.BlackStainedGlassPane; + mappings[361] = ItemType.Prismarine; + mappings[362] = ItemType.PrismarineBricks; + mappings[363] = ItemType.DarkPrismarine; + mappings[364] = ItemType.PrismarineStairs; + mappings[365] = ItemType.PrismarineBrickStairs; + mappings[366] = ItemType.DarkPrismarineStairs; + mappings[367] = ItemType.SeaLantern; + mappings[368] = ItemType.RedSandstone; + mappings[369] = ItemType.ChiseledRedSandstone; + mappings[370] = ItemType.CutRedSandstone; + mappings[371] = ItemType.RedSandstoneStairs; + mappings[372] = ItemType.RepeatingCommandBlock; + mappings[373] = ItemType.ChainCommandBlock; + mappings[374] = ItemType.MagmaBlock; + mappings[375] = ItemType.NetherWartBlock; + mappings[376] = ItemType.RedNetherBricks; + mappings[377] = ItemType.BoneBlock; + mappings[378] = ItemType.StructureVoid; + mappings[379] = ItemType.Observer; + mappings[380] = ItemType.ShulkerBox; + mappings[381] = ItemType.WhiteShulkerBox; + mappings[382] = ItemType.OrangeShulkerBox; + mappings[383] = ItemType.MagentaShulkerBox; + mappings[384] = ItemType.LightBlueShulkerBox; + mappings[385] = ItemType.YellowShulkerBox; + mappings[386] = ItemType.LimeShulkerBox; + mappings[387] = ItemType.PinkShulkerBox; + mappings[388] = ItemType.GrayShulkerBox; + mappings[389] = ItemType.LightGrayShulkerBox; + mappings[390] = ItemType.CyanShulkerBox; + mappings[391] = ItemType.PurpleShulkerBox; + mappings[392] = ItemType.BlueShulkerBox; + mappings[393] = ItemType.BrownShulkerBox; + mappings[394] = ItemType.GreenShulkerBox; + mappings[395] = ItemType.RedShulkerBox; + mappings[396] = ItemType.BlackShulkerBox; + mappings[397] = ItemType.WhiteGlazedTerracotta; + mappings[398] = ItemType.OrangeGlazedTerracotta; + mappings[399] = ItemType.MagentaGlazedTerracotta; + mappings[400] = ItemType.LightBlueGlazedTerracotta; + mappings[401] = ItemType.YellowGlazedTerracotta; + mappings[402] = ItemType.LimeGlazedTerracotta; + mappings[403] = ItemType.PinkGlazedTerracotta; + mappings[404] = ItemType.GrayGlazedTerracotta; + mappings[405] = ItemType.LightGrayGlazedTerracotta; + mappings[406] = ItemType.CyanGlazedTerracotta; + mappings[407] = ItemType.PurpleGlazedTerracotta; + mappings[408] = ItemType.BlueGlazedTerracotta; + mappings[409] = ItemType.BrownGlazedTerracotta; + mappings[410] = ItemType.GreenGlazedTerracotta; + mappings[411] = ItemType.RedGlazedTerracotta; + mappings[412] = ItemType.BlackGlazedTerracotta; + mappings[413] = ItemType.WhiteConcrete; + mappings[414] = ItemType.OrangeConcrete; + mappings[415] = ItemType.MagentaConcrete; + mappings[416] = ItemType.LightBlueConcrete; + mappings[417] = ItemType.YellowConcrete; + mappings[418] = ItemType.LimeConcrete; + mappings[419] = ItemType.PinkConcrete; + mappings[420] = ItemType.GrayConcrete; + mappings[421] = ItemType.LightGrayConcrete; + mappings[422] = ItemType.CyanConcrete; + mappings[423] = ItemType.PurpleConcrete; + mappings[424] = ItemType.BlueConcrete; + mappings[425] = ItemType.BrownConcrete; + mappings[426] = ItemType.GreenConcrete; + mappings[427] = ItemType.RedConcrete; + mappings[428] = ItemType.BlackConcrete; + mappings[429] = ItemType.WhiteConcretePowder; + mappings[430] = ItemType.OrangeConcretePowder; + mappings[431] = ItemType.MagentaConcretePowder; + mappings[432] = ItemType.LightBlueConcretePowder; + mappings[433] = ItemType.YellowConcretePowder; + mappings[434] = ItemType.LimeConcretePowder; + mappings[435] = ItemType.PinkConcretePowder; + mappings[436] = ItemType.GrayConcretePowder; + mappings[437] = ItemType.LightGrayConcretePowder; + mappings[438] = ItemType.CyanConcretePowder; + mappings[439] = ItemType.PurpleConcretePowder; + mappings[440] = ItemType.BlueConcretePowder; + mappings[441] = ItemType.BrownConcretePowder; + mappings[442] = ItemType.GreenConcretePowder; + mappings[443] = ItemType.RedConcretePowder; + mappings[444] = ItemType.BlackConcretePowder; + mappings[445] = ItemType.TurtleEgg; + mappings[446] = ItemType.DeadTubeCoralBlock; + mappings[447] = ItemType.DeadBrainCoralBlock; + mappings[448] = ItemType.DeadBubbleCoralBlock; + mappings[449] = ItemType.DeadFireCoralBlock; + mappings[450] = ItemType.DeadHornCoralBlock; + mappings[451] = ItemType.TubeCoralBlock; + mappings[452] = ItemType.BrainCoralBlock; + mappings[453] = ItemType.BubbleCoralBlock; + mappings[454] = ItemType.FireCoralBlock; + mappings[455] = ItemType.HornCoralBlock; + mappings[456] = ItemType.TubeCoral; + mappings[457] = ItemType.BrainCoral; + mappings[458] = ItemType.BubbleCoral; + mappings[459] = ItemType.FireCoral; + mappings[460] = ItemType.HornCoral; + mappings[461] = ItemType.DeadBrainCoral; + mappings[462] = ItemType.DeadBubbleCoral; + mappings[463] = ItemType.DeadFireCoral; + mappings[464] = ItemType.DeadHornCoral; + mappings[465] = ItemType.DeadTubeCoral; + mappings[466] = ItemType.TubeCoralFan; + mappings[467] = ItemType.BrainCoralFan; + mappings[468] = ItemType.BubbleCoralFan; + mappings[469] = ItemType.FireCoralFan; + mappings[470] = ItemType.HornCoralFan; + mappings[471] = ItemType.DeadTubeCoralFan; + mappings[472] = ItemType.DeadBrainCoralFan; + mappings[473] = ItemType.DeadBubbleCoralFan; + mappings[474] = ItemType.DeadFireCoralFan; + mappings[475] = ItemType.DeadHornCoralFan; + mappings[476] = ItemType.BlueIce; + mappings[477] = ItemType.Conduit; + mappings[478] = ItemType.PolishedGraniteStairs; + mappings[479] = ItemType.SmoothRedSandstoneStairs; + mappings[480] = ItemType.MossyStoneBrickStairs; + mappings[481] = ItemType.PolishedDioriteStairs; + mappings[482] = ItemType.MossyCobblestoneStairs; + mappings[483] = ItemType.EndStoneBrickStairs; + mappings[484] = ItemType.StoneStairs; + mappings[485] = ItemType.SmoothSandstoneStairs; + mappings[486] = ItemType.SmoothQuartzStairs; + mappings[487] = ItemType.GraniteStairs; + mappings[488] = ItemType.AndesiteStairs; + mappings[489] = ItemType.RedNetherBrickStairs; + mappings[490] = ItemType.PolishedAndesiteStairs; + mappings[491] = ItemType.DioriteStairs; + mappings[492] = ItemType.PolishedGraniteSlab; + mappings[493] = ItemType.SmoothRedSandstoneSlab; + mappings[494] = ItemType.MossyStoneBrickSlab; + mappings[495] = ItemType.PolishedDioriteSlab; + mappings[496] = ItemType.MossyCobblestoneSlab; + mappings[497] = ItemType.EndStoneBrickSlab; + mappings[498] = ItemType.SmoothSandstoneSlab; + mappings[499] = ItemType.SmoothQuartzSlab; + mappings[500] = ItemType.GraniteSlab; + mappings[501] = ItemType.AndesiteSlab; + mappings[502] = ItemType.RedNetherBrickSlab; + mappings[503] = ItemType.PolishedAndesiteSlab; + mappings[504] = ItemType.DioriteSlab; + mappings[505] = ItemType.Scaffolding; + mappings[506] = ItemType.IronDoor; + mappings[507] = ItemType.OakDoor; + mappings[508] = ItemType.SpruceDoor; + mappings[509] = ItemType.BirchDoor; + mappings[510] = ItemType.JungleDoor; + mappings[511] = ItemType.AcaciaDoor; + mappings[512] = ItemType.DarkOakDoor; + mappings[513] = ItemType.Repeater; + mappings[514] = ItemType.Comparator; + mappings[515] = ItemType.StructureBlock; + mappings[516] = ItemType.Jigsaw; + mappings[517] = ItemType.Composter; + mappings[518] = ItemType.TurtleHelmet; + mappings[519] = ItemType.TurtleScute; + mappings[520] = ItemType.IronShovel; + mappings[521] = ItemType.IronPickaxe; + mappings[522] = ItemType.IronAxe; + mappings[523] = ItemType.FlintAndSteel; + mappings[524] = ItemType.Apple; + mappings[525] = ItemType.Bow; + mappings[526] = ItemType.Arrow; + mappings[527] = ItemType.Coal; + mappings[528] = ItemType.Charcoal; + mappings[529] = ItemType.Diamond; + mappings[530] = ItemType.IronIngot; + mappings[531] = ItemType.GoldIngot; + mappings[532] = ItemType.IronSword; + mappings[533] = ItemType.WoodenSword; + mappings[534] = ItemType.WoodenShovel; + mappings[535] = ItemType.WoodenPickaxe; + mappings[536] = ItemType.WoodenAxe; + mappings[537] = ItemType.StoneSword; + mappings[538] = ItemType.StoneShovel; + mappings[539] = ItemType.StonePickaxe; + mappings[540] = ItemType.StoneAxe; + mappings[541] = ItemType.DiamondSword; + mappings[542] = ItemType.DiamondShovel; + mappings[543] = ItemType.DiamondPickaxe; + mappings[544] = ItemType.DiamondAxe; + mappings[545] = ItemType.Stick; + mappings[546] = ItemType.Bowl; + mappings[547] = ItemType.MushroomStew; + mappings[548] = ItemType.GoldenSword; + mappings[549] = ItemType.GoldenShovel; + mappings[550] = ItemType.GoldenPickaxe; + mappings[551] = ItemType.GoldenAxe; + mappings[552] = ItemType.String; + mappings[553] = ItemType.Feather; + mappings[554] = ItemType.Gunpowder; + mappings[555] = ItemType.WoodenHoe; + mappings[556] = ItemType.StoneHoe; + mappings[557] = ItemType.IronHoe; + mappings[558] = ItemType.DiamondHoe; + mappings[559] = ItemType.GoldenHoe; + mappings[560] = ItemType.WheatSeeds; + mappings[561] = ItemType.Wheat; + mappings[562] = ItemType.Bread; + mappings[563] = ItemType.LeatherHelmet; + mappings[564] = ItemType.LeatherChestplate; + mappings[565] = ItemType.LeatherLeggings; + mappings[566] = ItemType.LeatherBoots; + mappings[567] = ItemType.ChainmailHelmet; + mappings[568] = ItemType.ChainmailChestplate; + mappings[569] = ItemType.ChainmailLeggings; + mappings[570] = ItemType.ChainmailBoots; + mappings[571] = ItemType.IronHelmet; + mappings[572] = ItemType.IronChestplate; + mappings[573] = ItemType.IronLeggings; + mappings[574] = ItemType.IronBoots; + mappings[575] = ItemType.DiamondHelmet; + mappings[576] = ItemType.DiamondChestplate; + mappings[577] = ItemType.DiamondLeggings; + mappings[578] = ItemType.DiamondBoots; + mappings[579] = ItemType.GoldenHelmet; + mappings[580] = ItemType.GoldenChestplate; + mappings[581] = ItemType.GoldenLeggings; + mappings[582] = ItemType.GoldenBoots; + mappings[583] = ItemType.Flint; + mappings[584] = ItemType.Porkchop; + mappings[585] = ItemType.CookedPorkchop; + mappings[586] = ItemType.Painting; + mappings[587] = ItemType.GoldenApple; + mappings[588] = ItemType.EnchantedGoldenApple; + mappings[589] = ItemType.OakSign; + mappings[590] = ItemType.SpruceSign; + mappings[591] = ItemType.BirchSign; + mappings[592] = ItemType.JungleSign; + mappings[593] = ItemType.AcaciaSign; + mappings[594] = ItemType.DarkOakSign; + mappings[595] = ItemType.Bucket; + mappings[596] = ItemType.WaterBucket; + mappings[597] = ItemType.LavaBucket; + mappings[598] = ItemType.Minecart; + mappings[599] = ItemType.Saddle; + mappings[600] = ItemType.Redstone; + mappings[601] = ItemType.Snowball; + mappings[602] = ItemType.OakBoat; + mappings[603] = ItemType.Leather; + mappings[604] = ItemType.MilkBucket; + mappings[605] = ItemType.PufferfishBucket; + mappings[606] = ItemType.SalmonBucket; + mappings[607] = ItemType.CodBucket; + mappings[608] = ItemType.TropicalFishBucket; + mappings[609] = ItemType.Brick; + mappings[610] = ItemType.ClayBall; + mappings[611] = ItemType.SugarCane; + mappings[612] = ItemType.Kelp; + mappings[613] = ItemType.DriedKelpBlock; + mappings[614] = ItemType.Bamboo; + mappings[615] = ItemType.Paper; + mappings[616] = ItemType.Book; + mappings[617] = ItemType.SlimeBall; + mappings[618] = ItemType.ChestMinecart; + mappings[619] = ItemType.FurnaceMinecart; + mappings[620] = ItemType.Egg; + mappings[621] = ItemType.Compass; + mappings[622] = ItemType.FishingRod; + mappings[623] = ItemType.Clock; + mappings[624] = ItemType.GlowstoneDust; + mappings[625] = ItemType.Cod; + mappings[626] = ItemType.Salmon; + mappings[627] = ItemType.TropicalFish; + mappings[628] = ItemType.Pufferfish; + mappings[629] = ItemType.CookedCod; + mappings[630] = ItemType.CookedSalmon; + mappings[631] = ItemType.InkSac; + mappings[632] = ItemType.RedDye; + mappings[633] = ItemType.GreenDye; + mappings[634] = ItemType.CocoaBeans; + mappings[635] = ItemType.LapisLazuli; + mappings[636] = ItemType.PurpleDye; + mappings[637] = ItemType.CyanDye; + mappings[638] = ItemType.LightGrayDye; + mappings[639] = ItemType.GrayDye; + mappings[640] = ItemType.PinkDye; + mappings[641] = ItemType.LimeDye; + mappings[642] = ItemType.YellowDye; + mappings[643] = ItemType.LightBlueDye; + mappings[644] = ItemType.MagentaDye; + mappings[645] = ItemType.OrangeDye; + mappings[646] = ItemType.BoneMeal; + mappings[647] = ItemType.BlueDye; + mappings[648] = ItemType.BrownDye; + mappings[649] = ItemType.BlackDye; + mappings[650] = ItemType.WhiteDye; + mappings[651] = ItemType.Bone; + mappings[652] = ItemType.Sugar; + mappings[653] = ItemType.Cake; + mappings[654] = ItemType.WhiteBed; + mappings[655] = ItemType.OrangeBed; + mappings[656] = ItemType.MagentaBed; + mappings[657] = ItemType.LightBlueBed; + mappings[658] = ItemType.YellowBed; + mappings[659] = ItemType.LimeBed; + mappings[660] = ItemType.PinkBed; + mappings[661] = ItemType.GrayBed; + mappings[662] = ItemType.LightGrayBed; + mappings[663] = ItemType.CyanBed; + mappings[664] = ItemType.PurpleBed; + mappings[665] = ItemType.BlueBed; + mappings[666] = ItemType.BrownBed; + mappings[667] = ItemType.GreenBed; + mappings[668] = ItemType.RedBed; + mappings[669] = ItemType.BlackBed; + mappings[670] = ItemType.Cookie; + mappings[671] = ItemType.FilledMap; + mappings[672] = ItemType.Shears; + mappings[673] = ItemType.MelonSlice; + mappings[674] = ItemType.DriedKelp; + mappings[675] = ItemType.PumpkinSeeds; + mappings[676] = ItemType.MelonSeeds; + mappings[677] = ItemType.Beef; + mappings[678] = ItemType.CookedBeef; + mappings[679] = ItemType.Chicken; + mappings[680] = ItemType.CookedChicken; + mappings[681] = ItemType.RottenFlesh; + mappings[682] = ItemType.EnderPearl; + mappings[683] = ItemType.BlazeRod; + mappings[684] = ItemType.GhastTear; + mappings[685] = ItemType.GoldNugget; + mappings[686] = ItemType.NetherWart; + mappings[687] = ItemType.Potion; + mappings[688] = ItemType.GlassBottle; + mappings[689] = ItemType.SpiderEye; + mappings[690] = ItemType.FermentedSpiderEye; + mappings[691] = ItemType.BlazePowder; + mappings[692] = ItemType.MagmaCream; + mappings[693] = ItemType.BrewingStand; + mappings[694] = ItemType.Cauldron; + mappings[695] = ItemType.EnderEye; + mappings[696] = ItemType.GlisteringMelonSlice; + mappings[697] = ItemType.BatSpawnEgg; + mappings[698] = ItemType.BlazeSpawnEgg; + mappings[699] = ItemType.CatSpawnEgg; + mappings[700] = ItemType.CaveSpiderSpawnEgg; + mappings[701] = ItemType.ChickenSpawnEgg; + mappings[702] = ItemType.CodSpawnEgg; + mappings[703] = ItemType.CowSpawnEgg; + mappings[704] = ItemType.CreeperSpawnEgg; + mappings[705] = ItemType.DolphinSpawnEgg; + mappings[706] = ItemType.DonkeySpawnEgg; + mappings[707] = ItemType.DrownedSpawnEgg; + mappings[708] = ItemType.ElderGuardianSpawnEgg; + mappings[709] = ItemType.EndermanSpawnEgg; + mappings[710] = ItemType.EndermiteSpawnEgg; + mappings[711] = ItemType.EvokerSpawnEgg; + mappings[712] = ItemType.FoxSpawnEgg; + mappings[713] = ItemType.GhastSpawnEgg; + mappings[714] = ItemType.GuardianSpawnEgg; + mappings[715] = ItemType.HorseSpawnEgg; + mappings[716] = ItemType.HuskSpawnEgg; + mappings[717] = ItemType.LlamaSpawnEgg; + mappings[718] = ItemType.MagmaCubeSpawnEgg; + mappings[719] = ItemType.MooshroomSpawnEgg; + mappings[720] = ItemType.MuleSpawnEgg; + mappings[721] = ItemType.OcelotSpawnEgg; + mappings[722] = ItemType.PandaSpawnEgg; + mappings[723] = ItemType.ParrotSpawnEgg; + mappings[724] = ItemType.PhantomSpawnEgg; + mappings[725] = ItemType.PigSpawnEgg; + mappings[726] = ItemType.PillagerSpawnEgg; + mappings[727] = ItemType.PolarBearSpawnEgg; + mappings[728] = ItemType.PufferfishSpawnEgg; + mappings[729] = ItemType.RabbitSpawnEgg; + mappings[730] = ItemType.RavagerSpawnEgg; + mappings[731] = ItemType.SalmonSpawnEgg; + mappings[732] = ItemType.SheepSpawnEgg; + mappings[733] = ItemType.ShulkerSpawnEgg; + mappings[734] = ItemType.SilverfishSpawnEgg; + mappings[735] = ItemType.SkeletonSpawnEgg; + mappings[736] = ItemType.SkeletonHorseSpawnEgg; + mappings[737] = ItemType.SlimeSpawnEgg; + mappings[738] = ItemType.SpiderSpawnEgg; + mappings[739] = ItemType.SquidSpawnEgg; + mappings[740] = ItemType.StraySpawnEgg; + mappings[741] = ItemType.TraderLlamaSpawnEgg; + mappings[742] = ItemType.TropicalFishSpawnEgg; + mappings[743] = ItemType.TurtleSpawnEgg; + mappings[744] = ItemType.VexSpawnEgg; + mappings[745] = ItemType.VillagerSpawnEgg; + mappings[746] = ItemType.VindicatorSpawnEgg; + mappings[747] = ItemType.WanderingTraderSpawnEgg; + mappings[748] = ItemType.WitchSpawnEgg; + mappings[749] = ItemType.WitherSkeletonSpawnEgg; + mappings[750] = ItemType.WolfSpawnEgg; + mappings[751] = ItemType.ZombieSpawnEgg; + mappings[752] = ItemType.ZombieHorseSpawnEgg; + mappings[753] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[754] = ItemType.ZombieVillagerSpawnEgg; + mappings[755] = ItemType.ExperienceBottle; + mappings[756] = ItemType.FireCharge; + mappings[757] = ItemType.WritableBook; + mappings[758] = ItemType.WrittenBook; + mappings[759] = ItemType.Emerald; + mappings[760] = ItemType.ItemFrame; + mappings[761] = ItemType.FlowerPot; + mappings[762] = ItemType.Carrot; + mappings[763] = ItemType.Potato; + mappings[764] = ItemType.BakedPotato; + mappings[765] = ItemType.PoisonousPotato; + mappings[766] = ItemType.Map; + mappings[767] = ItemType.GoldenCarrot; + mappings[768] = ItemType.SkeletonSkull; + mappings[769] = ItemType.WitherSkeletonSkull; + mappings[770] = ItemType.PlayerHead; + mappings[771] = ItemType.ZombieHead; + mappings[772] = ItemType.CreeperHead; + mappings[773] = ItemType.DragonHead; + mappings[774] = ItemType.CarrotOnAStick; + mappings[775] = ItemType.NetherStar; + mappings[776] = ItemType.PumpkinPie; + mappings[777] = ItemType.FireworkRocket; + mappings[778] = ItemType.FireworkStar; + mappings[779] = ItemType.EnchantedBook; + mappings[780] = ItemType.NetherBrick; + mappings[781] = ItemType.Quartz; + mappings[782] = ItemType.TntMinecart; + mappings[783] = ItemType.HopperMinecart; + mappings[784] = ItemType.PrismarineShard; + mappings[785] = ItemType.PrismarineCrystals; + mappings[786] = ItemType.Rabbit; + mappings[787] = ItemType.CookedRabbit; + mappings[788] = ItemType.RabbitStew; + mappings[789] = ItemType.RabbitFoot; + mappings[790] = ItemType.RabbitHide; + mappings[791] = ItemType.ArmorStand; + mappings[792] = ItemType.IronHorseArmor; + mappings[793] = ItemType.GoldenHorseArmor; + mappings[794] = ItemType.DiamondHorseArmor; + mappings[795] = ItemType.LeatherHorseArmor; + mappings[796] = ItemType.Lead; + mappings[797] = ItemType.NameTag; + mappings[798] = ItemType.CommandBlockMinecart; + mappings[799] = ItemType.Mutton; + mappings[800] = ItemType.CookedMutton; + mappings[801] = ItemType.WhiteBanner; + mappings[802] = ItemType.OrangeBanner; + mappings[803] = ItemType.MagentaBanner; + mappings[804] = ItemType.LightBlueBanner; + mappings[805] = ItemType.YellowBanner; + mappings[806] = ItemType.LimeBanner; + mappings[807] = ItemType.PinkBanner; + mappings[808] = ItemType.GrayBanner; + mappings[809] = ItemType.LightGrayBanner; + mappings[810] = ItemType.CyanBanner; + mappings[811] = ItemType.PurpleBanner; + mappings[812] = ItemType.BlueBanner; + mappings[813] = ItemType.BrownBanner; + mappings[814] = ItemType.GreenBanner; + mappings[815] = ItemType.RedBanner; + mappings[816] = ItemType.BlackBanner; + mappings[817] = ItemType.EndCrystal; + mappings[818] = ItemType.ChorusFruit; + mappings[819] = ItemType.PoppedChorusFruit; + mappings[820] = ItemType.Beetroot; + mappings[821] = ItemType.BeetrootSeeds; + mappings[822] = ItemType.BeetrootSoup; + mappings[823] = ItemType.DragonBreath; + mappings[824] = ItemType.SplashPotion; + mappings[825] = ItemType.SpectralArrow; + mappings[826] = ItemType.TippedArrow; + mappings[827] = ItemType.LingeringPotion; + mappings[828] = ItemType.Shield; + mappings[829] = ItemType.Elytra; + mappings[830] = ItemType.SpruceBoat; + mappings[831] = ItemType.BirchBoat; + mappings[832] = ItemType.JungleBoat; + mappings[833] = ItemType.AcaciaBoat; + mappings[834] = ItemType.DarkOakBoat; + mappings[835] = ItemType.TotemOfUndying; + mappings[836] = ItemType.ShulkerShell; + mappings[837] = ItemType.IronNugget; + mappings[838] = ItemType.KnowledgeBook; + mappings[839] = ItemType.DebugStick; + mappings[840] = ItemType.MusicDisc13; + mappings[841] = ItemType.MusicDiscCat; + mappings[842] = ItemType.MusicDiscBlocks; + mappings[843] = ItemType.MusicDiscChirp; + mappings[844] = ItemType.MusicDiscFar; + mappings[845] = ItemType.MusicDiscMall; + mappings[846] = ItemType.MusicDiscMellohi; + mappings[847] = ItemType.MusicDiscStal; + mappings[848] = ItemType.MusicDiscStrad; + mappings[849] = ItemType.MusicDiscWard; + mappings[850] = ItemType.MusicDisc11; + mappings[851] = ItemType.MusicDiscWait; + mappings[852] = ItemType.Trident; + mappings[853] = ItemType.PhantomMembrane; + mappings[854] = ItemType.NautilusShell; + mappings[855] = ItemType.HeartOfTheSea; + mappings[856] = ItemType.Crossbow; + mappings[857] = ItemType.SuspiciousStew; + mappings[858] = ItemType.Loom; + mappings[859] = ItemType.FlowerBannerPattern; + mappings[860] = ItemType.CreeperBannerPattern; + mappings[861] = ItemType.SkullBannerPattern; + mappings[862] = ItemType.MojangBannerPattern; + mappings[863] = ItemType.GlobeBannerPattern; + mappings[864] = ItemType.Barrel; + mappings[865] = ItemType.Smoker; + mappings[866] = ItemType.BlastFurnace; + mappings[867] = ItemType.CartographyTable; + mappings[868] = ItemType.FletchingTable; + mappings[869] = ItemType.Grindstone; + mappings[870] = ItemType.Lectern; + mappings[871] = ItemType.SmithingTable; + mappings[872] = ItemType.Stonecutter; + mappings[873] = ItemType.Bell; + mappings[874] = ItemType.Lantern; + mappings[875] = ItemType.SweetBerries; + mappings[876] = ItemType.Campfire; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette115.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette115.cs index 8c67fff8..35593662 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette115.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette115.cs @@ -88,7 +88,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[73] = ItemType.DetectorRail; mappings[74] = ItemType.StickyPiston; mappings[75] = ItemType.Cobweb; - mappings[76] = ItemType.Grass; + mappings[76] = ItemType.ShortGrass; mappings[77] = ItemType.Fern; mappings[78] = ItemType.DeadBush; mappings[79] = ItemType.Seagrass; @@ -531,7 +531,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[516] = ItemType.Jigsaw; mappings[517] = ItemType.Composter; mappings[518] = ItemType.TurtleHelmet; - mappings[519] = ItemType.Scute; + mappings[519] = ItemType.TurtleScute; mappings[520] = ItemType.IronShovel; mappings[521] = ItemType.IronPickaxe; mappings[522] = ItemType.IronAxe; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1161.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1161.cs index 20dae2ad..4bdb9006 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1161.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1161.cs @@ -101,7 +101,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[86] = ItemType.DetectorRail; mappings[87] = ItemType.StickyPiston; mappings[88] = ItemType.Cobweb; - mappings[89] = ItemType.Grass; + mappings[89] = ItemType.ShortGrass; mappings[90] = ItemType.Fern; mappings[91] = ItemType.DeadBush; mappings[92] = ItemType.Seagrass; @@ -583,7 +583,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[568] = ItemType.StructureBlock; mappings[569] = ItemType.Jigsaw; mappings[570] = ItemType.TurtleHelmet; - mappings[571] = ItemType.Scute; + mappings[571] = ItemType.TurtleScute; mappings[572] = ItemType.IronShovel; mappings[573] = ItemType.IronPickaxe; mappings[574] = ItemType.IronAxe; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1162.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1162.cs index bdacf33a..5a82eef4 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1162.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1162.cs @@ -101,7 +101,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[86] = ItemType.DetectorRail; mappings[87] = ItemType.StickyPiston; mappings[88] = ItemType.Cobweb; - mappings[89] = ItemType.Grass; + mappings[89] = ItemType.ShortGrass; mappings[90] = ItemType.Fern; mappings[91] = ItemType.DeadBush; mappings[92] = ItemType.Seagrass; @@ -583,7 +583,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[568] = ItemType.StructureBlock; mappings[569] = ItemType.Jigsaw; mappings[570] = ItemType.TurtleHelmet; - mappings[571] = ItemType.Scute; + mappings[571] = ItemType.TurtleScute; mappings[572] = ItemType.FlintAndSteel; mappings[573] = ItemType.Apple; mappings[574] = ItemType.Bow; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette117.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette117.cs index f21c9465..be1bd517 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette117.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette117.cs @@ -158,7 +158,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[147] = ItemType.ChiseledSandstone; mappings[148] = ItemType.CutSandstone; mappings[149] = ItemType.Cobweb; - mappings[150] = ItemType.Grass; + mappings[150] = ItemType.ShortGrass; mappings[151] = ItemType.Fern; mappings[152] = ItemType.Azalea; mappings[153] = ItemType.FloweringAzalea; @@ -687,7 +687,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[676] = ItemType.StructureBlock; mappings[677] = ItemType.Jigsaw; mappings[678] = ItemType.TurtleHelmet; - mappings[679] = ItemType.Scute; + mappings[679] = ItemType.TurtleScute; mappings[680] = ItemType.FlintAndSteel; mappings[681] = ItemType.Apple; mappings[682] = ItemType.Bow; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette118.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette118.cs index 8db51826..bc3f25ad 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette118.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette118.cs @@ -158,7 +158,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[147] = ItemType.ChiseledSandstone; mappings[148] = ItemType.CutSandstone; mappings[149] = ItemType.Cobweb; - mappings[150] = ItemType.Grass; + mappings[150] = ItemType.ShortGrass; mappings[151] = ItemType.Fern; mappings[152] = ItemType.Azalea; mappings[153] = ItemType.FloweringAzalea; @@ -687,7 +687,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[676] = ItemType.StructureBlock; mappings[677] = ItemType.Jigsaw; mappings[678] = ItemType.TurtleHelmet; - mappings[679] = ItemType.Scute; + mappings[679] = ItemType.TurtleScute; mappings[680] = ItemType.FlintAndSteel; mappings[681] = ItemType.Apple; mappings[682] = ItemType.Bow; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette119.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette119.cs index a47104e7..a1ab48d3 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette119.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette119.cs @@ -449,7 +449,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[598] = ItemType.GraniteSlab; mappings[581] = ItemType.GraniteStairs; mappings[355] = ItemType.GraniteWall; - mappings[160] = ItemType.Grass; + mappings[160] = ItemType.ShortGrass; mappings[14] = ItemType.GrassBlock; mappings[42] = ItemType.Gravel; mappings[1032] = ItemType.GrayBanner; @@ -927,7 +927,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[626] = ItemType.SculkSensor; mappings[329] = ItemType.SculkShrieker; mappings[327] = ItemType.SculkVein; - mappings[715] = ItemType.Scute; + mappings[715] = ItemType.TurtleScute; mappings[461] = ItemType.SeaLantern; mappings[166] = ItemType.SeaPickle; mappings[165] = ItemType.Seagrass; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs index 563b16a5..6677721e 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs @@ -473,7 +473,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[608] = ItemType.GraniteSlab; mappings[591] = ItemType.GraniteStairs; mappings[365] = ItemType.GraniteWall; - mappings[164] = ItemType.Grass; + mappings[164] = ItemType.ShortGrass; mappings[14] = ItemType.GrassBlock; mappings[44] = ItemType.Gravel; mappings[1066] = ItemType.GrayBanner; @@ -956,7 +956,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[636] = ItemType.SculkSensor; mappings[337] = ItemType.SculkShrieker; mappings[335] = ItemType.SculkVein; - mappings[732] = ItemType.Scute; + mappings[732] = ItemType.TurtleScute; mappings[471] = ItemType.SeaLantern; mappings[170] = ItemType.SeaPickle; mappings[169] = ItemType.Seagrass; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1194.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1194.cs index 5306eac3..ef04e7dc 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1194.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1194.cs @@ -495,7 +495,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[622] = ItemType.GraniteSlab; mappings[605] = ItemType.GraniteStairs; mappings[379] = ItemType.GraniteWall; - mappings[172] = ItemType.Grass; + mappings[172] = ItemType.ShortGrass; mappings[14] = ItemType.GrassBlock; mappings[47] = ItemType.Gravel; mappings[1090] = ItemType.GrayBanner; @@ -985,7 +985,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[650] = ItemType.SculkSensor; mappings[350] = ItemType.SculkShrieker; mappings[348] = ItemType.SculkVein; - mappings[753] = ItemType.Scute; + mappings[753] = ItemType.TurtleScute; mappings[485] = ItemType.SeaLantern; mappings[178] = ItemType.SeaPickle; mappings[177] = ItemType.Seagrass; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette120.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette120.cs index fdce6150..2ca03bed 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette120.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette120.cs @@ -505,7 +505,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[625] = ItemType.GraniteSlab; mappings[608] = ItemType.GraniteStairs; mappings[381] = ItemType.GraniteWall; - mappings[173] = ItemType.Grass; + mappings[173] = ItemType.ShortGrass; mappings[14] = ItemType.GrassBlock; mappings[48] = ItemType.Gravel; mappings[1094] = ItemType.GrayBanner; @@ -1003,7 +1003,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[653] = ItemType.SculkSensor; mappings[352] = ItemType.SculkShrieker; mappings[350] = ItemType.SculkVein; - mappings[757] = ItemType.Scute; + mappings[757] = ItemType.TurtleScute; mappings[487] = ItemType.SeaLantern; mappings[179] = ItemType.SeaPickle; mappings[178] = ItemType.Seagrass; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1204.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1204.cs index 13f7676e..3796e4cb 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1204.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1204.cs @@ -1025,7 +1025,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[674] = ItemType.SculkSensor; mappings[373] = ItemType.SculkShrieker; mappings[371] = ItemType.SculkVein; - mappings[794] = ItemType.Scute; + mappings[794] = ItemType.TurtleScute; mappings[508] = ItemType.SeaLantern; mappings[200] = ItemType.SeaPickle; mappings[199] = ItemType.Seagrass; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1206.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1206.cs new file mode 100644 index 00000000..d0dceeeb --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1206.cs @@ -0,0 +1,1348 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1206 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1206() + { + mappings[782] = ItemType.AcaciaBoat; + mappings[688] = ItemType.AcaciaButton; + mappings[783] = ItemType.AcaciaChestBoat; + mappings[715] = ItemType.AcaciaDoor; + mappings[315] = ItemType.AcaciaFence; + mappings[754] = ItemType.AcaciaFenceGate; + mappings[901] = ItemType.AcaciaHangingSign; + mappings[180] = ItemType.AcaciaLeaves; + mappings[136] = ItemType.AcaciaLog; + mappings[40] = ItemType.AcaciaPlanks; + mappings[703] = ItemType.AcaciaPressurePlate; + mappings[52] = ItemType.AcaciaSapling; + mappings[890] = ItemType.AcaciaSign; + mappings[256] = ItemType.AcaciaSlab; + mappings[387] = ItemType.AcaciaStairs; + mappings[735] = ItemType.AcaciaTrapdoor; + mappings[170] = ItemType.AcaciaWood; + mappings[764] = ItemType.ActivatorRail; + mappings[0] = ItemType.Air; + mappings[1009] = ItemType.AllaySpawnEgg; + mappings[221] = ItemType.Allium; + mappings[86] = ItemType.AmethystBlock; + mappings[1258] = ItemType.AmethystCluster; + mappings[808] = ItemType.AmethystShard; + mappings[80] = ItemType.AncientDebris; + mappings[6] = ItemType.Andesite; + mappings[648] = ItemType.AndesiteSlab; + mappings[631] = ItemType.AndesiteStairs; + mappings[407] = ItemType.AndesiteWall; + mappings[1285] = ItemType.AnglerPotterySherd; + mappings[419] = ItemType.Anvil; + mappings[799] = ItemType.Apple; + mappings[1286] = ItemType.ArcherPotterySherd; + mappings[796] = ItemType.ArmadilloScute; + mappings[1008] = ItemType.ArmadilloSpawnEgg; + mappings[1123] = ItemType.ArmorStand; + mappings[1287] = ItemType.ArmsUpPotterySherd; + mappings[801] = ItemType.Arrow; + mappings[919] = ItemType.AxolotlBucket; + mappings[1010] = ItemType.AxolotlSpawnEgg; + mappings[197] = ItemType.Azalea; + mappings[184] = ItemType.AzaleaLeaves; + mappings[222] = ItemType.AzureBluet; + mappings[1099] = ItemType.BakedPotato; + mappings[251] = ItemType.Bamboo; + mappings[144] = ItemType.BambooBlock; + mappings[692] = ItemType.BambooButton; + mappings[791] = ItemType.BambooChestRaft; + mappings[719] = ItemType.BambooDoor; + mappings[319] = ItemType.BambooFence; + mappings[758] = ItemType.BambooFenceGate; + mappings[905] = ItemType.BambooHangingSign; + mappings[47] = ItemType.BambooMosaic; + mappings[261] = ItemType.BambooMosaicSlab; + mappings[392] = ItemType.BambooMosaicStairs; + mappings[44] = ItemType.BambooPlanks; + mappings[707] = ItemType.BambooPressurePlate; + mappings[790] = ItemType.BambooRaft; + mappings[894] = ItemType.BambooSign; + mappings[260] = ItemType.BambooSlab; + mappings[391] = ItemType.BambooStairs; + mappings[739] = ItemType.BambooTrapdoor; + mappings[1202] = ItemType.Barrel; + mappings[443] = ItemType.Barrier; + mappings[328] = ItemType.Basalt; + mappings[1011] = ItemType.BatSpawnEgg; + mappings[396] = ItemType.Beacon; + mappings[56] = ItemType.Bedrock; + mappings[1219] = ItemType.BeeNest; + mappings[1012] = ItemType.BeeSpawnEgg; + mappings[988] = ItemType.Beef; + mappings[1220] = ItemType.Beehive; + mappings[1154] = ItemType.Beetroot; + mappings[1155] = ItemType.BeetrootSeeds; + mappings[1156] = ItemType.BeetrootSoup; + mappings[1210] = ItemType.Bell; + mappings[249] = ItemType.BigDripleaf; + mappings[778] = ItemType.BirchBoat; + mappings[686] = ItemType.BirchButton; + mappings[779] = ItemType.BirchChestBoat; + mappings[713] = ItemType.BirchDoor; + mappings[313] = ItemType.BirchFence; + mappings[752] = ItemType.BirchFenceGate; + mappings[899] = ItemType.BirchHangingSign; + mappings[178] = ItemType.BirchLeaves; + mappings[134] = ItemType.BirchLog; + mappings[38] = ItemType.BirchPlanks; + mappings[701] = ItemType.BirchPressurePlate; + mappings[50] = ItemType.BirchSapling; + mappings[888] = ItemType.BirchSign; + mappings[254] = ItemType.BirchSlab; + mappings[385] = ItemType.BirchStairs; + mappings[733] = ItemType.BirchTrapdoor; + mappings[168] = ItemType.BirchWood; + mappings[1148] = ItemType.BlackBanner; + mappings[979] = ItemType.BlackBed; + mappings[1254] = ItemType.BlackCandle; + mappings[461] = ItemType.BlackCarpet; + mappings[570] = ItemType.BlackConcrete; + mappings[586] = ItemType.BlackConcretePowder; + mappings[959] = ItemType.BlackDye; + mappings[554] = ItemType.BlackGlazedTerracotta; + mappings[538] = ItemType.BlackShulkerBox; + mappings[486] = ItemType.BlackStainedGlass; + mappings[502] = ItemType.BlackStainedGlassPane; + mappings[442] = ItemType.BlackTerracotta; + mappings[217] = ItemType.BlackWool; + mappings[1225] = ItemType.Blackstone; + mappings[1226] = ItemType.BlackstoneSlab; + mappings[1227] = ItemType.BlackstoneStairs; + mappings[412] = ItemType.BlackstoneWall; + mappings[1288] = ItemType.BladePotterySherd; + mappings[1204] = ItemType.BlastFurnace; + mappings[1002] = ItemType.BlazePowder; + mappings[994] = ItemType.BlazeRod; + mappings[1013] = ItemType.BlazeSpawnEgg; + mappings[1144] = ItemType.BlueBanner; + mappings[975] = ItemType.BlueBed; + mappings[1250] = ItemType.BlueCandle; + mappings[457] = ItemType.BlueCarpet; + mappings[566] = ItemType.BlueConcrete; + mappings[582] = ItemType.BlueConcretePowder; + mappings[955] = ItemType.BlueDye; + mappings[550] = ItemType.BlueGlazedTerracotta; + mappings[619] = ItemType.BlueIce; + mappings[220] = ItemType.BlueOrchid; + mappings[534] = ItemType.BlueShulkerBox; + mappings[482] = ItemType.BlueStainedGlass; + mappings[498] = ItemType.BlueStainedGlassPane; + mappings[438] = ItemType.BlueTerracotta; + mappings[213] = ItemType.BlueWool; + mappings[1014] = ItemType.BoggedSpawnEgg; + mappings[1284] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[961] = ItemType.Bone; + mappings[520] = ItemType.BoneBlock; + mappings[960] = ItemType.BoneMeal; + mappings[925] = ItemType.Book; + mappings[286] = ItemType.Bookshelf; + mappings[800] = ItemType.Bow; + mappings[848] = ItemType.Bowl; + mappings[600] = ItemType.BrainCoral; + mappings[595] = ItemType.BrainCoralBlock; + mappings[610] = ItemType.BrainCoralFan; + mappings[855] = ItemType.Bread; + mappings[1329] = ItemType.BreezeRod; + mappings[1015] = ItemType.BreezeSpawnEgg; + mappings[1289] = ItemType.BrewerPotterySherd; + mappings[1004] = ItemType.BrewingStand; + mappings[921] = ItemType.Brick; + mappings[270] = ItemType.BrickSlab; + mappings[361] = ItemType.BrickStairs; + mappings[399] = ItemType.BrickWall; + mappings[285] = ItemType.Bricks; + mappings[1145] = ItemType.BrownBanner; + mappings[976] = ItemType.BrownBed; + mappings[1251] = ItemType.BrownCandle; + mappings[458] = ItemType.BrownCarpet; + mappings[567] = ItemType.BrownConcrete; + mappings[583] = ItemType.BrownConcretePowder; + mappings[956] = ItemType.BrownDye; + mappings[551] = ItemType.BrownGlazedTerracotta; + mappings[234] = ItemType.BrownMushroom; + mappings[352] = ItemType.BrownMushroomBlock; + mappings[535] = ItemType.BrownShulkerBox; + mappings[483] = ItemType.BrownStainedGlass; + mappings[499] = ItemType.BrownStainedGlassPane; + mappings[439] = ItemType.BrownTerracotta; + mappings[214] = ItemType.BrownWool; + mappings[1265] = ItemType.Brush; + mappings[601] = ItemType.BubbleCoral; + mappings[596] = ItemType.BubbleCoralBlock; + mappings[611] = ItemType.BubbleCoralFan; + mappings[908] = ItemType.Bucket; + mappings[87] = ItemType.BuddingAmethyst; + mappings[930] = ItemType.Bundle; + mappings[1290] = ItemType.BurnPotterySherd; + mappings[308] = ItemType.Cactus; + mappings[963] = ItemType.Cake; + mappings[11] = ItemType.Calcite; + mappings[676] = ItemType.CalibratedSculkSensor; + mappings[1017] = ItemType.CamelSpawnEgg; + mappings[1215] = ItemType.Campfire; + mappings[1238] = ItemType.Candle; + mappings[1097] = ItemType.Carrot; + mappings[771] = ItemType.CarrotOnAStick; + mappings[1205] = ItemType.CartographyTable; + mappings[323] = ItemType.CarvedPumpkin; + mappings[1016] = ItemType.CatSpawnEgg; + mappings[1005] = ItemType.Cauldron; + mappings[1018] = ItemType.CaveSpiderSpawnEgg; + mappings[356] = ItemType.Chain; + mappings[515] = ItemType.ChainCommandBlock; + mappings[863] = ItemType.ChainmailBoots; + mappings[861] = ItemType.ChainmailChestplate; + mappings[860] = ItemType.ChainmailHelmet; + mappings[862] = ItemType.ChainmailLeggings; + mappings[803] = ItemType.Charcoal; + mappings[784] = ItemType.CherryBoat; + mappings[689] = ItemType.CherryButton; + mappings[785] = ItemType.CherryChestBoat; + mappings[716] = ItemType.CherryDoor; + mappings[316] = ItemType.CherryFence; + mappings[755] = ItemType.CherryFenceGate; + mappings[902] = ItemType.CherryHangingSign; + mappings[181] = ItemType.CherryLeaves; + mappings[137] = ItemType.CherryLog; + mappings[41] = ItemType.CherryPlanks; + mappings[704] = ItemType.CherryPressurePlate; + mappings[53] = ItemType.CherrySapling; + mappings[891] = ItemType.CherrySign; + mappings[257] = ItemType.CherrySlab; + mappings[388] = ItemType.CherryStairs; + mappings[736] = ItemType.CherryTrapdoor; + mappings[171] = ItemType.CherryWood; + mappings[299] = ItemType.Chest; + mappings[767] = ItemType.ChestMinecart; + mappings[990] = ItemType.Chicken; + mappings[1019] = ItemType.ChickenSpawnEgg; + mappings[420] = ItemType.ChippedAnvil; + mappings[287] = ItemType.ChiseledBookshelf; + mappings[96] = ItemType.ChiseledCopper; + mappings[350] = ItemType.ChiseledDeepslate; + mappings[368] = ItemType.ChiseledNetherBricks; + mappings[1232] = ItemType.ChiseledPolishedBlackstone; + mappings[422] = ItemType.ChiseledQuartzBlock; + mappings[511] = ItemType.ChiseledRedSandstone; + mappings[192] = ItemType.ChiseledSandstone; + mappings[343] = ItemType.ChiseledStoneBricks; + mappings[16] = ItemType.ChiseledTuff; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[294] = ItemType.ChorusFlower; + mappings[1150] = ItemType.ChorusFruit; + mappings[293] = ItemType.ChorusPlant; + mappings[309] = ItemType.Clay; + mappings[922] = ItemType.ClayBall; + mappings[932] = ItemType.Clock; + mappings[802] = ItemType.Coal; + mappings[81] = ItemType.CoalBlock; + mappings[62] = ItemType.CoalOre; + mappings[29] = ItemType.CoarseDirt; + mappings[1269] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[652] = ItemType.CobbledDeepslateSlab; + mappings[635] = ItemType.CobbledDeepslateStairs; + mappings[415] = ItemType.CobbledDeepslateWall; + mappings[35] = ItemType.Cobblestone; + mappings[269] = ItemType.CobblestoneSlab; + mappings[304] = ItemType.CobblestoneStairs; + mappings[397] = ItemType.CobblestoneWall; + mappings[194] = ItemType.Cobweb; + mappings[943] = ItemType.CocoaBeans; + mappings[935] = ItemType.Cod; + mappings[917] = ItemType.CodBucket; + mappings[1020] = ItemType.CodSpawnEgg; + mappings[395] = ItemType.CommandBlock; + mappings[1130] = ItemType.CommandBlockMinecart; + mappings[661] = ItemType.Comparator; + mappings[928] = ItemType.Compass; + mappings[1201] = ItemType.Composter; + mappings[620] = ItemType.Conduit; + mappings[989] = ItemType.CookedBeef; + mappings[991] = ItemType.CookedChicken; + mappings[939] = ItemType.CookedCod; + mappings[1132] = ItemType.CookedMutton; + mappings[882] = ItemType.CookedPorkchop; + mappings[1119] = ItemType.CookedRabbit; + mappings[940] = ItemType.CookedSalmon; + mappings[980] = ItemType.Cookie; + mappings[89] = ItemType.CopperBlock; + mappings[1316] = ItemType.CopperBulb; + mappings[722] = ItemType.CopperDoor; + mappings[1308] = ItemType.CopperGrate; + mappings[812] = ItemType.CopperIngot; + mappings[66] = ItemType.CopperOre; + mappings[742] = ItemType.CopperTrapdoor; + mappings[228] = ItemType.Cornflower; + mappings[1021] = ItemType.CowSpawnEgg; + mappings[347] = ItemType.CrackedDeepslateBricks; + mappings[349] = ItemType.CrackedDeepslateTiles; + mappings[367] = ItemType.CrackedNetherBricks; + mappings[1236] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[342] = ItemType.CrackedStoneBricks; + mappings[981] = ItemType.Crafter; + mappings[300] = ItemType.CraftingTable; + mappings[1193] = ItemType.CreeperBannerPattern; + mappings[1107] = ItemType.CreeperHead; + mappings[1022] = ItemType.CreeperSpawnEgg; + mappings[693] = ItemType.CrimsonButton; + mappings[720] = ItemType.CrimsonDoor; + mappings[320] = ItemType.CrimsonFence; + mappings[759] = ItemType.CrimsonFenceGate; + mappings[236] = ItemType.CrimsonFungus; + mappings[906] = ItemType.CrimsonHangingSign; + mappings[174] = ItemType.CrimsonHyphae; + mappings[33] = ItemType.CrimsonNylium; + mappings[45] = ItemType.CrimsonPlanks; + mappings[708] = ItemType.CrimsonPressurePlate; + mappings[238] = ItemType.CrimsonRoots; + mappings[895] = ItemType.CrimsonSign; + mappings[262] = ItemType.CrimsonSlab; + mappings[393] = ItemType.CrimsonStairs; + mappings[142] = ItemType.CrimsonStem; + mappings[740] = ItemType.CrimsonTrapdoor; + mappings[1189] = ItemType.Crossbow; + mappings[1224] = ItemType.CryingObsidian; + mappings[100] = ItemType.CutCopper; + mappings[108] = ItemType.CutCopperSlab; + mappings[104] = ItemType.CutCopperStairs; + mappings[512] = ItemType.CutRedSandstone; + mappings[276] = ItemType.CutRedSandstoneSlab; + mappings[193] = ItemType.CutSandstone; + mappings[267] = ItemType.CutSandstoneSlab; + mappings[1142] = ItemType.CyanBanner; + mappings[973] = ItemType.CyanBed; + mappings[1248] = ItemType.CyanCandle; + mappings[455] = ItemType.CyanCarpet; + mappings[564] = ItemType.CyanConcrete; + mappings[580] = ItemType.CyanConcretePowder; + mappings[953] = ItemType.CyanDye; + mappings[548] = ItemType.CyanGlazedTerracotta; + mappings[532] = ItemType.CyanShulkerBox; + mappings[480] = ItemType.CyanStainedGlass; + mappings[496] = ItemType.CyanStainedGlassPane; + mappings[436] = ItemType.CyanTerracotta; + mappings[211] = ItemType.CyanWool; + mappings[421] = ItemType.DamagedAnvil; + mappings[218] = ItemType.Dandelion; + mappings[1291] = ItemType.DangerPotterySherd; + mappings[786] = ItemType.DarkOakBoat; + mappings[690] = ItemType.DarkOakButton; + mappings[787] = ItemType.DarkOakChestBoat; + mappings[717] = ItemType.DarkOakDoor; + mappings[317] = ItemType.DarkOakFence; + mappings[756] = ItemType.DarkOakFenceGate; + mappings[903] = ItemType.DarkOakHangingSign; + mappings[182] = ItemType.DarkOakLeaves; + mappings[138] = ItemType.DarkOakLog; + mappings[42] = ItemType.DarkOakPlanks; + mappings[705] = ItemType.DarkOakPressurePlate; + mappings[54] = ItemType.DarkOakSapling; + mappings[892] = ItemType.DarkOakSign; + mappings[258] = ItemType.DarkOakSlab; + mappings[389] = ItemType.DarkOakStairs; + mappings[737] = ItemType.DarkOakTrapdoor; + mappings[172] = ItemType.DarkOakWood; + mappings[505] = ItemType.DarkPrismarine; + mappings[280] = ItemType.DarkPrismarineSlab; + mappings[508] = ItemType.DarkPrismarineStairs; + mappings[674] = ItemType.DaylightDetector; + mappings[604] = ItemType.DeadBrainCoral; + mappings[590] = ItemType.DeadBrainCoralBlock; + mappings[615] = ItemType.DeadBrainCoralFan; + mappings[605] = ItemType.DeadBubbleCoral; + mappings[591] = ItemType.DeadBubbleCoralBlock; + mappings[616] = ItemType.DeadBubbleCoralFan; + mappings[199] = ItemType.DeadBush; + mappings[606] = ItemType.DeadFireCoral; + mappings[592] = ItemType.DeadFireCoralBlock; + mappings[617] = ItemType.DeadFireCoralFan; + mappings[607] = ItemType.DeadHornCoral; + mappings[593] = ItemType.DeadHornCoralBlock; + mappings[618] = ItemType.DeadHornCoralFan; + mappings[608] = ItemType.DeadTubeCoral; + mappings[589] = ItemType.DeadTubeCoralBlock; + mappings[614] = ItemType.DeadTubeCoralFan; + mappings[1167] = ItemType.DebugStick; + mappings[288] = ItemType.DecoratedPot; + mappings[8] = ItemType.Deepslate; + mappings[654] = ItemType.DeepslateBrickSlab; + mappings[637] = ItemType.DeepslateBrickStairs; + mappings[417] = ItemType.DeepslateBrickWall; + mappings[346] = ItemType.DeepslateBricks; + mappings[63] = ItemType.DeepslateCoalOre; + mappings[67] = ItemType.DeepslateCopperOre; + mappings[77] = ItemType.DeepslateDiamondOre; + mappings[73] = ItemType.DeepslateEmeraldOre; + mappings[69] = ItemType.DeepslateGoldOre; + mappings[65] = ItemType.DeepslateIronOre; + mappings[75] = ItemType.DeepslateLapisOre; + mappings[71] = ItemType.DeepslateRedstoneOre; + mappings[655] = ItemType.DeepslateTileSlab; + mappings[638] = ItemType.DeepslateTileStairs; + mappings[418] = ItemType.DeepslateTileWall; + mappings[348] = ItemType.DeepslateTiles; + mappings[762] = ItemType.DetectorRail; + mappings[804] = ItemType.Diamond; + mappings[840] = ItemType.DiamondAxe; + mappings[91] = ItemType.DiamondBlock; + mappings[871] = ItemType.DiamondBoots; + mappings[869] = ItemType.DiamondChestplate; + mappings[868] = ItemType.DiamondHelmet; + mappings[841] = ItemType.DiamondHoe; + mappings[1126] = ItemType.DiamondHorseArmor; + mappings[870] = ItemType.DiamondLeggings; + mappings[76] = ItemType.DiamondOre; + mappings[839] = ItemType.DiamondPickaxe; + mappings[838] = ItemType.DiamondShovel; + mappings[837] = ItemType.DiamondSword; + mappings[4] = ItemType.Diorite; + mappings[651] = ItemType.DioriteSlab; + mappings[634] = ItemType.DioriteStairs; + mappings[411] = ItemType.DioriteWall; + mappings[28] = ItemType.Dirt; + mappings[464] = ItemType.DirtPath; + mappings[1184] = ItemType.DiscFragment5; + mappings[668] = ItemType.Dispenser; + mappings[1023] = ItemType.DolphinSpawnEgg; + mappings[1024] = ItemType.DonkeySpawnEgg; + mappings[1157] = ItemType.DragonBreath; + mappings[379] = ItemType.DragonEgg; + mappings[1108] = ItemType.DragonHead; + mappings[985] = ItemType.DriedKelp; + mappings[923] = ItemType.DriedKelpBlock; + mappings[26] = ItemType.DripstoneBlock; + mappings[669] = ItemType.Dropper; + mappings[1025] = ItemType.DrownedSpawnEgg; + mappings[1268] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1264] = ItemType.EchoShard; + mappings[927] = ItemType.Egg; + mappings[1026] = ItemType.ElderGuardianSpawnEgg; + mappings[773] = ItemType.Elytra; + mappings[805] = ItemType.Emerald; + mappings[382] = ItemType.EmeraldBlock; + mappings[72] = ItemType.EmeraldOre; + mappings[1114] = ItemType.EnchantedBook; + mappings[885] = ItemType.EnchantedGoldenApple; + mappings[375] = ItemType.EnchantingTable; + mappings[1149] = ItemType.EndCrystal; + mappings[376] = ItemType.EndPortalFrame; + mappings[292] = ItemType.EndRod; + mappings[377] = ItemType.EndStone; + mappings[644] = ItemType.EndStoneBrickSlab; + mappings[626] = ItemType.EndStoneBrickStairs; + mappings[410] = ItemType.EndStoneBrickWall; + mappings[378] = ItemType.EndStoneBricks; + mappings[381] = ItemType.EnderChest; + mappings[1027] = ItemType.EnderDragonSpawnEgg; + mappings[1006] = ItemType.EnderEye; + mappings[993] = ItemType.EnderPearl; + mappings[1028] = ItemType.EndermanSpawnEgg; + mappings[1029] = ItemType.EndermiteSpawnEgg; + mappings[1030] = ItemType.EvokerSpawnEgg; + mappings[1088] = ItemType.ExperienceBottle; + mappings[1292] = ItemType.ExplorerPotterySherd; + mappings[97] = ItemType.ExposedChiseledCopper; + mappings[93] = ItemType.ExposedCopper; + mappings[1317] = ItemType.ExposedCopperBulb; + mappings[723] = ItemType.ExposedCopperDoor; + mappings[1309] = ItemType.ExposedCopperGrate; + mappings[743] = ItemType.ExposedCopperTrapdoor; + mappings[101] = ItemType.ExposedCutCopper; + mappings[109] = ItemType.ExposedCutCopperSlab; + mappings[105] = ItemType.ExposedCutCopperStairs; + mappings[1272] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[301] = ItemType.Farmland; + mappings[851] = ItemType.Feather; + mappings[1001] = ItemType.FermentedSpiderEye; + mappings[196] = ItemType.Fern; + mappings[982] = ItemType.FilledMap; + mappings[1089] = ItemType.FireCharge; + mappings[602] = ItemType.FireCoral; + mappings[597] = ItemType.FireCoralBlock; + mappings[612] = ItemType.FireCoralFan; + mappings[1112] = ItemType.FireworkRocket; + mappings[1113] = ItemType.FireworkStar; + mappings[931] = ItemType.FishingRod; + mappings[1206] = ItemType.FletchingTable; + mappings[880] = ItemType.Flint; + mappings[798] = ItemType.FlintAndSteel; + mappings[1283] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1198] = ItemType.FlowBannerPattern; + mappings[1293] = ItemType.FlowPotterySherd; + mappings[1192] = ItemType.FlowerBannerPattern; + mappings[1096] = ItemType.FlowerPot; + mappings[198] = ItemType.FloweringAzalea; + mappings[185] = ItemType.FloweringAzaleaLeaves; + mappings[1031] = ItemType.FoxSpawnEgg; + mappings[1294] = ItemType.FriendPotterySherd; + mappings[1032] = ItemType.FrogSpawnEgg; + mappings[1263] = ItemType.Frogspawn; + mappings[302] = ItemType.Furnace; + mappings[768] = ItemType.FurnaceMinecart; + mappings[1033] = ItemType.GhastSpawnEgg; + mappings[995] = ItemType.GhastTear; + mappings[1228] = ItemType.GildedBlackstone; + mappings[188] = ItemType.Glass; + mappings[999] = ItemType.GlassBottle; + mappings[357] = ItemType.GlassPane; + mappings[1007] = ItemType.GlisteringMelonSlice; + mappings[1196] = ItemType.GlobeBannerPattern; + mappings[1214] = ItemType.GlowBerries; + mappings[942] = ItemType.GlowInkSac; + mappings[1095] = ItemType.GlowItemFrame; + mappings[360] = ItemType.GlowLichen; + mappings[1034] = ItemType.GlowSquidSpawnEgg; + mappings[332] = ItemType.Glowstone; + mappings[934] = ItemType.GlowstoneDust; + mappings[1200] = ItemType.GoatHorn; + mappings[1035] = ItemType.GoatSpawnEgg; + mappings[90] = ItemType.GoldBlock; + mappings[814] = ItemType.GoldIngot; + mappings[996] = ItemType.GoldNugget; + mappings[68] = ItemType.GoldOre; + mappings[884] = ItemType.GoldenApple; + mappings[830] = ItemType.GoldenAxe; + mappings[875] = ItemType.GoldenBoots; + mappings[1102] = ItemType.GoldenCarrot; + mappings[873] = ItemType.GoldenChestplate; + mappings[872] = ItemType.GoldenHelmet; + mappings[831] = ItemType.GoldenHoe; + mappings[1125] = ItemType.GoldenHorseArmor; + mappings[874] = ItemType.GoldenLeggings; + mappings[829] = ItemType.GoldenPickaxe; + mappings[828] = ItemType.GoldenShovel; + mappings[827] = ItemType.GoldenSword; + mappings[2] = ItemType.Granite; + mappings[647] = ItemType.GraniteSlab; + mappings[630] = ItemType.GraniteStairs; + mappings[403] = ItemType.GraniteWall; + mappings[27] = ItemType.GrassBlock; + mappings[61] = ItemType.Gravel; + mappings[1140] = ItemType.GrayBanner; + mappings[971] = ItemType.GrayBed; + mappings[1246] = ItemType.GrayCandle; + mappings[453] = ItemType.GrayCarpet; + mappings[562] = ItemType.GrayConcrete; + mappings[578] = ItemType.GrayConcretePowder; + mappings[951] = ItemType.GrayDye; + mappings[546] = ItemType.GrayGlazedTerracotta; + mappings[530] = ItemType.GrayShulkerBox; + mappings[478] = ItemType.GrayStainedGlass; + mappings[494] = ItemType.GrayStainedGlassPane; + mappings[434] = ItemType.GrayTerracotta; + mappings[209] = ItemType.GrayWool; + mappings[1146] = ItemType.GreenBanner; + mappings[977] = ItemType.GreenBed; + mappings[1252] = ItemType.GreenCandle; + mappings[459] = ItemType.GreenCarpet; + mappings[568] = ItemType.GreenConcrete; + mappings[584] = ItemType.GreenConcretePowder; + mappings[957] = ItemType.GreenDye; + mappings[552] = ItemType.GreenGlazedTerracotta; + mappings[536] = ItemType.GreenShulkerBox; + mappings[484] = ItemType.GreenStainedGlass; + mappings[500] = ItemType.GreenStainedGlassPane; + mappings[440] = ItemType.GreenTerracotta; + mappings[215] = ItemType.GreenWool; + mappings[1207] = ItemType.Grindstone; + mappings[1036] = ItemType.GuardianSpawnEgg; + mappings[852] = ItemType.Gunpowder; + mappings[1199] = ItemType.GusterBannerPattern; + mappings[1295] = ItemType.GusterPotterySherd; + mappings[248] = ItemType.HangingRoots; + mappings[445] = ItemType.HayBlock; + mappings[1188] = ItemType.HeartOfTheSea; + mappings[1296] = ItemType.HeartPotterySherd; + mappings[1297] = ItemType.HeartbreakPotterySherd; + mappings[85] = ItemType.HeavyCore; + mappings[698] = ItemType.HeavyWeightedPressurePlate; + mappings[1037] = ItemType.HoglinSpawnEgg; + mappings[665] = ItemType.HoneyBlock; + mappings[1221] = ItemType.HoneyBottle; + mappings[1218] = ItemType.Honeycomb; + mappings[1222] = ItemType.HoneycombBlock; + mappings[667] = ItemType.Hopper; + mappings[770] = ItemType.HopperMinecart; + mappings[603] = ItemType.HornCoral; + mappings[598] = ItemType.HornCoralBlock; + mappings[613] = ItemType.HornCoralFan; + mappings[1038] = ItemType.HorseSpawnEgg; + mappings[1282] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1298] = ItemType.HowlPotterySherd; + mappings[1039] = ItemType.HuskSpawnEgg; + mappings[306] = ItemType.Ice; + mappings[338] = ItemType.InfestedChiseledStoneBricks; + mappings[334] = ItemType.InfestedCobblestone; + mappings[337] = ItemType.InfestedCrackedStoneBricks; + mappings[339] = ItemType.InfestedDeepslate; + mappings[336] = ItemType.InfestedMossyStoneBricks; + mappings[333] = ItemType.InfestedStone; + mappings[335] = ItemType.InfestedStoneBricks; + mappings[941] = ItemType.InkSac; + mappings[835] = ItemType.IronAxe; + mappings[355] = ItemType.IronBars; + mappings[88] = ItemType.IronBlock; + mappings[867] = ItemType.IronBoots; + mappings[865] = ItemType.IronChestplate; + mappings[710] = ItemType.IronDoor; + mappings[1040] = ItemType.IronGolemSpawnEgg; + mappings[864] = ItemType.IronHelmet; + mappings[836] = ItemType.IronHoe; + mappings[1124] = ItemType.IronHorseArmor; + mappings[810] = ItemType.IronIngot; + mappings[866] = ItemType.IronLeggings; + mappings[1165] = ItemType.IronNugget; + mappings[64] = ItemType.IronOre; + mappings[834] = ItemType.IronPickaxe; + mappings[833] = ItemType.IronShovel; + mappings[832] = ItemType.IronSword; + mappings[730] = ItemType.IronTrapdoor; + mappings[1094] = ItemType.ItemFrame; + mappings[324] = ItemType.JackOLantern; + mappings[793] = ItemType.Jigsaw; + mappings[310] = ItemType.Jukebox; + mappings[780] = ItemType.JungleBoat; + mappings[687] = ItemType.JungleButton; + mappings[781] = ItemType.JungleChestBoat; + mappings[714] = ItemType.JungleDoor; + mappings[314] = ItemType.JungleFence; + mappings[753] = ItemType.JungleFenceGate; + mappings[900] = ItemType.JungleHangingSign; + mappings[179] = ItemType.JungleLeaves; + mappings[135] = ItemType.JungleLog; + mappings[39] = ItemType.JunglePlanks; + mappings[702] = ItemType.JunglePressurePlate; + mappings[51] = ItemType.JungleSapling; + mappings[889] = ItemType.JungleSign; + mappings[255] = ItemType.JungleSlab; + mappings[386] = ItemType.JungleStairs; + mappings[734] = ItemType.JungleTrapdoor; + mappings[169] = ItemType.JungleWood; + mappings[244] = ItemType.Kelp; + mappings[1166] = ItemType.KnowledgeBook; + mappings[303] = ItemType.Ladder; + mappings[1211] = ItemType.Lantern; + mappings[190] = ItemType.LapisBlock; + mappings[806] = ItemType.LapisLazuli; + mappings[74] = ItemType.LapisOre; + mappings[1257] = ItemType.LargeAmethystBud; + mappings[470] = ItemType.LargeFern; + mappings[910] = ItemType.LavaBucket; + mappings[1128] = ItemType.Lead; + mappings[913] = ItemType.Leather; + mappings[859] = ItemType.LeatherBoots; + mappings[857] = ItemType.LeatherChestplate; + mappings[856] = ItemType.LeatherHelmet; + mappings[1127] = ItemType.LeatherHorseArmor; + mappings[858] = ItemType.LeatherLeggings; + mappings[670] = ItemType.Lectern; + mappings[672] = ItemType.Lever; + mappings[444] = ItemType.Light; + mappings[1136] = ItemType.LightBlueBanner; + mappings[967] = ItemType.LightBlueBed; + mappings[1242] = ItemType.LightBlueCandle; + mappings[449] = ItemType.LightBlueCarpet; + mappings[558] = ItemType.LightBlueConcrete; + mappings[574] = ItemType.LightBlueConcretePowder; + mappings[947] = ItemType.LightBlueDye; + mappings[542] = ItemType.LightBlueGlazedTerracotta; + mappings[526] = ItemType.LightBlueShulkerBox; + mappings[474] = ItemType.LightBlueStainedGlass; + mappings[490] = ItemType.LightBlueStainedGlassPane; + mappings[430] = ItemType.LightBlueTerracotta; + mappings[205] = ItemType.LightBlueWool; + mappings[1141] = ItemType.LightGrayBanner; + mappings[972] = ItemType.LightGrayBed; + mappings[1247] = ItemType.LightGrayCandle; + mappings[454] = ItemType.LightGrayCarpet; + mappings[563] = ItemType.LightGrayConcrete; + mappings[579] = ItemType.LightGrayConcretePowder; + mappings[952] = ItemType.LightGrayDye; + mappings[547] = ItemType.LightGrayGlazedTerracotta; + mappings[531] = ItemType.LightGrayShulkerBox; + mappings[479] = ItemType.LightGrayStainedGlass; + mappings[495] = ItemType.LightGrayStainedGlassPane; + mappings[435] = ItemType.LightGrayTerracotta; + mappings[210] = ItemType.LightGrayWool; + mappings[697] = ItemType.LightWeightedPressurePlate; + mappings[673] = ItemType.LightningRod; + mappings[466] = ItemType.Lilac; + mappings[229] = ItemType.LilyOfTheValley; + mappings[365] = ItemType.LilyPad; + mappings[1138] = ItemType.LimeBanner; + mappings[969] = ItemType.LimeBed; + mappings[1244] = ItemType.LimeCandle; + mappings[451] = ItemType.LimeCarpet; + mappings[560] = ItemType.LimeConcrete; + mappings[576] = ItemType.LimeConcretePowder; + mappings[949] = ItemType.LimeDye; + mappings[544] = ItemType.LimeGlazedTerracotta; + mappings[528] = ItemType.LimeShulkerBox; + mappings[476] = ItemType.LimeStainedGlass; + mappings[492] = ItemType.LimeStainedGlassPane; + mappings[432] = ItemType.LimeTerracotta; + mappings[207] = ItemType.LimeWool; + mappings[1161] = ItemType.LingeringPotion; + mappings[1041] = ItemType.LlamaSpawnEgg; + mappings[1223] = ItemType.Lodestone; + mappings[1191] = ItemType.Loom; + mappings[1093] = ItemType.Mace; + mappings[1135] = ItemType.MagentaBanner; + mappings[966] = ItemType.MagentaBed; + mappings[1241] = ItemType.MagentaCandle; + mappings[448] = ItemType.MagentaCarpet; + mappings[557] = ItemType.MagentaConcrete; + mappings[573] = ItemType.MagentaConcretePowder; + mappings[946] = ItemType.MagentaDye; + mappings[541] = ItemType.MagentaGlazedTerracotta; + mappings[525] = ItemType.MagentaShulkerBox; + mappings[473] = ItemType.MagentaStainedGlass; + mappings[489] = ItemType.MagentaStainedGlassPane; + mappings[429] = ItemType.MagentaTerracotta; + mappings[204] = ItemType.MagentaWool; + mappings[516] = ItemType.MagmaBlock; + mappings[1003] = ItemType.MagmaCream; + mappings[1042] = ItemType.MagmaCubeSpawnEgg; + mappings[788] = ItemType.MangroveBoat; + mappings[691] = ItemType.MangroveButton; + mappings[789] = ItemType.MangroveChestBoat; + mappings[718] = ItemType.MangroveDoor; + mappings[318] = ItemType.MangroveFence; + mappings[757] = ItemType.MangroveFenceGate; + mappings[904] = ItemType.MangroveHangingSign; + mappings[183] = ItemType.MangroveLeaves; + mappings[139] = ItemType.MangroveLog; + mappings[43] = ItemType.MangrovePlanks; + mappings[706] = ItemType.MangrovePressurePlate; + mappings[55] = ItemType.MangrovePropagule; + mappings[140] = ItemType.MangroveRoots; + mappings[893] = ItemType.MangroveSign; + mappings[259] = ItemType.MangroveSlab; + mappings[390] = ItemType.MangroveStairs; + mappings[738] = ItemType.MangroveTrapdoor; + mappings[173] = ItemType.MangroveWood; + mappings[1101] = ItemType.Map; + mappings[1256] = ItemType.MediumAmethystBud; + mappings[358] = ItemType.Melon; + mappings[987] = ItemType.MelonSeeds; + mappings[984] = ItemType.MelonSlice; + mappings[914] = ItemType.MilkBucket; + mappings[766] = ItemType.Minecart; + mappings[1299] = ItemType.MinerPotterySherd; + mappings[1195] = ItemType.MojangBannerPattern; + mappings[1043] = ItemType.MooshroomSpawnEgg; + mappings[247] = ItemType.MossBlock; + mappings[245] = ItemType.MossCarpet; + mappings[289] = ItemType.MossyCobblestone; + mappings[643] = ItemType.MossyCobblestoneSlab; + mappings[625] = ItemType.MossyCobblestoneStairs; + mappings[398] = ItemType.MossyCobblestoneWall; + mappings[641] = ItemType.MossyStoneBrickSlab; + mappings[623] = ItemType.MossyStoneBrickStairs; + mappings[402] = ItemType.MossyStoneBrickWall; + mappings[341] = ItemType.MossyStoneBricks; + mappings[1300] = ItemType.MournerPotterySherd; + mappings[32] = ItemType.Mud; + mappings[272] = ItemType.MudBrickSlab; + mappings[363] = ItemType.MudBrickStairs; + mappings[405] = ItemType.MudBrickWall; + mappings[345] = ItemType.MudBricks; + mappings[141] = ItemType.MuddyMangroveRoots; + mappings[1044] = ItemType.MuleSpawnEgg; + mappings[354] = ItemType.MushroomStem; + mappings[849] = ItemType.MushroomStew; + mappings[1178] = ItemType.MusicDisc11; + mappings[1168] = ItemType.MusicDisc13; + mappings[1182] = ItemType.MusicDisc5; + mappings[1170] = ItemType.MusicDiscBlocks; + mappings[1169] = ItemType.MusicDiscCat; + mappings[1171] = ItemType.MusicDiscChirp; + mappings[1172] = ItemType.MusicDiscFar; + mappings[1173] = ItemType.MusicDiscMall; + mappings[1174] = ItemType.MusicDiscMellohi; + mappings[1180] = ItemType.MusicDiscOtherside; + mappings[1183] = ItemType.MusicDiscPigstep; + mappings[1181] = ItemType.MusicDiscRelic; + mappings[1175] = ItemType.MusicDiscStal; + mappings[1176] = ItemType.MusicDiscStrad; + mappings[1179] = ItemType.MusicDiscWait; + mappings[1177] = ItemType.MusicDiscWard; + mappings[1131] = ItemType.Mutton; + mappings[364] = ItemType.Mycelium; + mappings[1129] = ItemType.NameTag; + mappings[1187] = ItemType.NautilusShell; + mappings[1115] = ItemType.NetherBrick; + mappings[369] = ItemType.NetherBrickFence; + mappings[273] = ItemType.NetherBrickSlab; + mappings[370] = ItemType.NetherBrickStairs; + mappings[406] = ItemType.NetherBrickWall; + mappings[366] = ItemType.NetherBricks; + mappings[78] = ItemType.NetherGoldOre; + mappings[79] = ItemType.NetherQuartzOre; + mappings[240] = ItemType.NetherSprouts; + mappings[1110] = ItemType.NetherStar; + mappings[997] = ItemType.NetherWart; + mappings[517] = ItemType.NetherWartBlock; + mappings[845] = ItemType.NetheriteAxe; + mappings[92] = ItemType.NetheriteBlock; + mappings[879] = ItemType.NetheriteBoots; + mappings[877] = ItemType.NetheriteChestplate; + mappings[876] = ItemType.NetheriteHelmet; + mappings[846] = ItemType.NetheriteHoe; + mappings[815] = ItemType.NetheriteIngot; + mappings[878] = ItemType.NetheriteLeggings; + mappings[844] = ItemType.NetheritePickaxe; + mappings[816] = ItemType.NetheriteScrap; + mappings[843] = ItemType.NetheriteShovel; + mappings[842] = ItemType.NetheriteSword; + mappings[1266] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[325] = ItemType.Netherrack; + mappings[681] = ItemType.NoteBlock; + mappings[774] = ItemType.OakBoat; + mappings[684] = ItemType.OakButton; + mappings[775] = ItemType.OakChestBoat; + mappings[711] = ItemType.OakDoor; + mappings[311] = ItemType.OakFence; + mappings[750] = ItemType.OakFenceGate; + mappings[897] = ItemType.OakHangingSign; + mappings[176] = ItemType.OakLeaves; + mappings[132] = ItemType.OakLog; + mappings[36] = ItemType.OakPlanks; + mappings[699] = ItemType.OakPressurePlate; + mappings[48] = ItemType.OakSapling; + mappings[886] = ItemType.OakSign; + mappings[252] = ItemType.OakSlab; + mappings[383] = ItemType.OakStairs; + mappings[731] = ItemType.OakTrapdoor; + mappings[166] = ItemType.OakWood; + mappings[666] = ItemType.Observer; + mappings[290] = ItemType.Obsidian; + mappings[1045] = ItemType.OcelotSpawnEgg; + mappings[1260] = ItemType.OchreFroglight; + mappings[1328] = ItemType.OminousBottle; + mappings[1326] = ItemType.OminousTrialKey; + mappings[1134] = ItemType.OrangeBanner; + mappings[965] = ItemType.OrangeBed; + mappings[1240] = ItemType.OrangeCandle; + mappings[447] = ItemType.OrangeCarpet; + mappings[556] = ItemType.OrangeConcrete; + mappings[572] = ItemType.OrangeConcretePowder; + mappings[945] = ItemType.OrangeDye; + mappings[540] = ItemType.OrangeGlazedTerracotta; + mappings[524] = ItemType.OrangeShulkerBox; + mappings[472] = ItemType.OrangeStainedGlass; + mappings[488] = ItemType.OrangeStainedGlassPane; + mappings[428] = ItemType.OrangeTerracotta; + mappings[224] = ItemType.OrangeTulip; + mappings[203] = ItemType.OrangeWool; + mappings[227] = ItemType.OxeyeDaisy; + mappings[99] = ItemType.OxidizedChiseledCopper; + mappings[95] = ItemType.OxidizedCopper; + mappings[1319] = ItemType.OxidizedCopperBulb; + mappings[725] = ItemType.OxidizedCopperDoor; + mappings[1311] = ItemType.OxidizedCopperGrate; + mappings[745] = ItemType.OxidizedCopperTrapdoor; + mappings[103] = ItemType.OxidizedCutCopper; + mappings[111] = ItemType.OxidizedCutCopperSlab; + mappings[107] = ItemType.OxidizedCutCopperStairs; + mappings[463] = ItemType.PackedIce; + mappings[344] = ItemType.PackedMud; + mappings[883] = ItemType.Painting; + mappings[1046] = ItemType.PandaSpawnEgg; + mappings[924] = ItemType.Paper; + mappings[1047] = ItemType.ParrotSpawnEgg; + mappings[1262] = ItemType.PearlescentFroglight; + mappings[468] = ItemType.Peony; + mappings[268] = ItemType.PetrifiedOakSlab; + mappings[1186] = ItemType.PhantomMembrane; + mappings[1048] = ItemType.PhantomSpawnEgg; + mappings[1049] = ItemType.PigSpawnEgg; + mappings[1197] = ItemType.PiglinBannerPattern; + mappings[1051] = ItemType.PiglinBruteSpawnEgg; + mappings[1109] = ItemType.PiglinHead; + mappings[1050] = ItemType.PiglinSpawnEgg; + mappings[1052] = ItemType.PillagerSpawnEgg; + mappings[1139] = ItemType.PinkBanner; + mappings[970] = ItemType.PinkBed; + mappings[1245] = ItemType.PinkCandle; + mappings[452] = ItemType.PinkCarpet; + mappings[561] = ItemType.PinkConcrete; + mappings[577] = ItemType.PinkConcretePowder; + mappings[950] = ItemType.PinkDye; + mappings[545] = ItemType.PinkGlazedTerracotta; + mappings[246] = ItemType.PinkPetals; + mappings[529] = ItemType.PinkShulkerBox; + mappings[477] = ItemType.PinkStainedGlass; + mappings[493] = ItemType.PinkStainedGlassPane; + mappings[433] = ItemType.PinkTerracotta; + mappings[226] = ItemType.PinkTulip; + mappings[208] = ItemType.PinkWool; + mappings[662] = ItemType.Piston; + mappings[232] = ItemType.PitcherPlant; + mappings[1153] = ItemType.PitcherPod; + mappings[1105] = ItemType.PlayerHead; + mappings[1301] = ItemType.PlentyPotterySherd; + mappings[30] = ItemType.Podzol; + mappings[1259] = ItemType.PointedDripstone; + mappings[1100] = ItemType.PoisonousPotato; + mappings[1053] = ItemType.PolarBearSpawnEgg; + mappings[7] = ItemType.PolishedAndesite; + mappings[650] = ItemType.PolishedAndesiteSlab; + mappings[633] = ItemType.PolishedAndesiteStairs; + mappings[329] = ItemType.PolishedBasalt; + mappings[1229] = ItemType.PolishedBlackstone; + mappings[1234] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1235] = ItemType.PolishedBlackstoneBrickStairs; + mappings[414] = ItemType.PolishedBlackstoneBrickWall; + mappings[1233] = ItemType.PolishedBlackstoneBricks; + mappings[683] = ItemType.PolishedBlackstoneButton; + mappings[696] = ItemType.PolishedBlackstonePressurePlate; + mappings[1230] = ItemType.PolishedBlackstoneSlab; + mappings[1231] = ItemType.PolishedBlackstoneStairs; + mappings[413] = ItemType.PolishedBlackstoneWall; + mappings[10] = ItemType.PolishedDeepslate; + mappings[653] = ItemType.PolishedDeepslateSlab; + mappings[636] = ItemType.PolishedDeepslateStairs; + mappings[416] = ItemType.PolishedDeepslateWall; + mappings[5] = ItemType.PolishedDiorite; + mappings[642] = ItemType.PolishedDioriteSlab; + mappings[624] = ItemType.PolishedDioriteStairs; + mappings[3] = ItemType.PolishedGranite; + mappings[639] = ItemType.PolishedGraniteSlab; + mappings[621] = ItemType.PolishedGraniteStairs; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[1151] = ItemType.PoppedChorusFruit; + mappings[219] = ItemType.Poppy; + mappings[881] = ItemType.Porkchop; + mappings[1098] = ItemType.Potato; + mappings[998] = ItemType.Potion; + mappings[911] = ItemType.PowderSnowBucket; + mappings[761] = ItemType.PoweredRail; + mappings[503] = ItemType.Prismarine; + mappings[279] = ItemType.PrismarineBrickSlab; + mappings[507] = ItemType.PrismarineBrickStairs; + mappings[504] = ItemType.PrismarineBricks; + mappings[1117] = ItemType.PrismarineCrystals; + mappings[1116] = ItemType.PrismarineShard; + mappings[278] = ItemType.PrismarineSlab; + mappings[506] = ItemType.PrismarineStairs; + mappings[400] = ItemType.PrismarineWall; + mappings[1302] = ItemType.PrizePotterySherd; + mappings[938] = ItemType.Pufferfish; + mappings[915] = ItemType.PufferfishBucket; + mappings[1054] = ItemType.PufferfishSpawnEgg; + mappings[322] = ItemType.Pumpkin; + mappings[1111] = ItemType.PumpkinPie; + mappings[986] = ItemType.PumpkinSeeds; + mappings[1143] = ItemType.PurpleBanner; + mappings[974] = ItemType.PurpleBed; + mappings[1249] = ItemType.PurpleCandle; + mappings[456] = ItemType.PurpleCarpet; + mappings[565] = ItemType.PurpleConcrete; + mappings[581] = ItemType.PurpleConcretePowder; + mappings[954] = ItemType.PurpleDye; + mappings[549] = ItemType.PurpleGlazedTerracotta; + mappings[533] = ItemType.PurpleShulkerBox; + mappings[481] = ItemType.PurpleStainedGlass; + mappings[497] = ItemType.PurpleStainedGlassPane; + mappings[437] = ItemType.PurpleTerracotta; + mappings[212] = ItemType.PurpleWool; + mappings[295] = ItemType.PurpurBlock; + mappings[296] = ItemType.PurpurPillar; + mappings[277] = ItemType.PurpurSlab; + mappings[297] = ItemType.PurpurStairs; + mappings[807] = ItemType.Quartz; + mappings[423] = ItemType.QuartzBlock; + mappings[424] = ItemType.QuartzBricks; + mappings[425] = ItemType.QuartzPillar; + mappings[274] = ItemType.QuartzSlab; + mappings[426] = ItemType.QuartzStairs; + mappings[1118] = ItemType.Rabbit; + mappings[1121] = ItemType.RabbitFoot; + mappings[1122] = ItemType.RabbitHide; + mappings[1055] = ItemType.RabbitSpawnEgg; + mappings[1120] = ItemType.RabbitStew; + mappings[763] = ItemType.Rail; + mappings[1281] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1056] = ItemType.RavagerSpawnEgg; + mappings[811] = ItemType.RawCopper; + mappings[83] = ItemType.RawCopperBlock; + mappings[813] = ItemType.RawGold; + mappings[84] = ItemType.RawGoldBlock; + mappings[809] = ItemType.RawIron; + mappings[82] = ItemType.RawIronBlock; + mappings[929] = ItemType.RecoveryCompass; + mappings[1147] = ItemType.RedBanner; + mappings[978] = ItemType.RedBed; + mappings[1253] = ItemType.RedCandle; + mappings[460] = ItemType.RedCarpet; + mappings[569] = ItemType.RedConcrete; + mappings[585] = ItemType.RedConcretePowder; + mappings[958] = ItemType.RedDye; + mappings[553] = ItemType.RedGlazedTerracotta; + mappings[235] = ItemType.RedMushroom; + mappings[353] = ItemType.RedMushroomBlock; + mappings[649] = ItemType.RedNetherBrickSlab; + mappings[632] = ItemType.RedNetherBrickStairs; + mappings[408] = ItemType.RedNetherBrickWall; + mappings[519] = ItemType.RedNetherBricks; + mappings[60] = ItemType.RedSand; + mappings[510] = ItemType.RedSandstone; + mappings[275] = ItemType.RedSandstoneSlab; + mappings[513] = ItemType.RedSandstoneStairs; + mappings[401] = ItemType.RedSandstoneWall; + mappings[537] = ItemType.RedShulkerBox; + mappings[485] = ItemType.RedStainedGlass; + mappings[501] = ItemType.RedStainedGlassPane; + mappings[441] = ItemType.RedTerracotta; + mappings[223] = ItemType.RedTulip; + mappings[216] = ItemType.RedWool; + mappings[657] = ItemType.Redstone; + mappings[659] = ItemType.RedstoneBlock; + mappings[680] = ItemType.RedstoneLamp; + mappings[70] = ItemType.RedstoneOre; + mappings[658] = ItemType.RedstoneTorch; + mappings[351] = ItemType.ReinforcedDeepslate; + mappings[660] = ItemType.Repeater; + mappings[514] = ItemType.RepeatingCommandBlock; + mappings[1237] = ItemType.RespawnAnchor; + mappings[1276] = ItemType.RibArmorTrimSmithingTemplate; + mappings[31] = ItemType.RootedDirt; + mappings[467] = ItemType.RoseBush; + mappings[992] = ItemType.RottenFlesh; + mappings[765] = ItemType.Saddle; + mappings[936] = ItemType.Salmon; + mappings[916] = ItemType.SalmonBucket; + mappings[1057] = ItemType.SalmonSpawnEgg; + mappings[57] = ItemType.Sand; + mappings[191] = ItemType.Sandstone; + mappings[266] = ItemType.SandstoneSlab; + mappings[380] = ItemType.SandstoneStairs; + mappings[409] = ItemType.SandstoneWall; + mappings[656] = ItemType.Scaffolding; + mappings[1303] = ItemType.ScrapePotterySherd; + mappings[371] = ItemType.Sculk; + mappings[373] = ItemType.SculkCatalyst; + mappings[675] = ItemType.SculkSensor; + mappings[374] = ItemType.SculkShrieker; + mappings[372] = ItemType.SculkVein; + mappings[509] = ItemType.SeaLantern; + mappings[201] = ItemType.SeaPickle; + mappings[200] = ItemType.Seagrass; + mappings[1267] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1279] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1304] = ItemType.SheafPotterySherd; + mappings[983] = ItemType.Shears; + mappings[1058] = ItemType.SheepSpawnEgg; + mappings[1305] = ItemType.ShelterPotterySherd; + mappings[1162] = ItemType.Shield; + mappings[195] = ItemType.ShortGrass; + mappings[1217] = ItemType.Shroomlight; + mappings[522] = ItemType.ShulkerBox; + mappings[1164] = ItemType.ShulkerShell; + mappings[1059] = ItemType.ShulkerSpawnEgg; + mappings[1280] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1060] = ItemType.SilverfishSpawnEgg; + mappings[1062] = ItemType.SkeletonHorseSpawnEgg; + mappings[1103] = ItemType.SkeletonSkull; + mappings[1061] = ItemType.SkeletonSpawnEgg; + mappings[1194] = ItemType.SkullBannerPattern; + mappings[1306] = ItemType.SkullPotterySherd; + mappings[926] = ItemType.SlimeBall; + mappings[664] = ItemType.SlimeBlock; + mappings[1063] = ItemType.SlimeSpawnEgg; + mappings[1255] = ItemType.SmallAmethystBud; + mappings[250] = ItemType.SmallDripleaf; + mappings[1208] = ItemType.SmithingTable; + mappings[1203] = ItemType.Smoker; + mappings[330] = ItemType.SmoothBasalt; + mappings[281] = ItemType.SmoothQuartz; + mappings[646] = ItemType.SmoothQuartzSlab; + mappings[629] = ItemType.SmoothQuartzStairs; + mappings[282] = ItemType.SmoothRedSandstone; + mappings[640] = ItemType.SmoothRedSandstoneSlab; + mappings[622] = ItemType.SmoothRedSandstoneStairs; + mappings[283] = ItemType.SmoothSandstone; + mappings[645] = ItemType.SmoothSandstoneSlab; + mappings[628] = ItemType.SmoothSandstoneStairs; + mappings[284] = ItemType.SmoothStone; + mappings[265] = ItemType.SmoothStoneSlab; + mappings[588] = ItemType.SnifferEgg; + mappings[1064] = ItemType.SnifferSpawnEgg; + mappings[1307] = ItemType.SnortPotterySherd; + mappings[1275] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[305] = ItemType.Snow; + mappings[307] = ItemType.SnowBlock; + mappings[1065] = ItemType.SnowGolemSpawnEgg; + mappings[912] = ItemType.Snowball; + mappings[1216] = ItemType.SoulCampfire; + mappings[1212] = ItemType.SoulLantern; + mappings[326] = ItemType.SoulSand; + mappings[327] = ItemType.SoulSoil; + mappings[331] = ItemType.SoulTorch; + mappings[298] = ItemType.Spawner; + mappings[1159] = ItemType.SpectralArrow; + mappings[1000] = ItemType.SpiderEye; + mappings[1066] = ItemType.SpiderSpawnEgg; + mappings[1277] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1158] = ItemType.SplashPotion; + mappings[186] = ItemType.Sponge; + mappings[233] = ItemType.SporeBlossom; + mappings[776] = ItemType.SpruceBoat; + mappings[685] = ItemType.SpruceButton; + mappings[777] = ItemType.SpruceChestBoat; + mappings[712] = ItemType.SpruceDoor; + mappings[312] = ItemType.SpruceFence; + mappings[751] = ItemType.SpruceFenceGate; + mappings[898] = ItemType.SpruceHangingSign; + mappings[177] = ItemType.SpruceLeaves; + mappings[133] = ItemType.SpruceLog; + mappings[37] = ItemType.SprucePlanks; + mappings[700] = ItemType.SprucePressurePlate; + mappings[49] = ItemType.SpruceSapling; + mappings[887] = ItemType.SpruceSign; + mappings[253] = ItemType.SpruceSlab; + mappings[384] = ItemType.SpruceStairs; + mappings[732] = ItemType.SpruceTrapdoor; + mappings[167] = ItemType.SpruceWood; + mappings[933] = ItemType.Spyglass; + mappings[1067] = ItemType.SquidSpawnEgg; + mappings[847] = ItemType.Stick; + mappings[663] = ItemType.StickyPiston; + mappings[1] = ItemType.Stone; + mappings[825] = ItemType.StoneAxe; + mappings[271] = ItemType.StoneBrickSlab; + mappings[362] = ItemType.StoneBrickStairs; + mappings[404] = ItemType.StoneBrickWall; + mappings[340] = ItemType.StoneBricks; + mappings[682] = ItemType.StoneButton; + mappings[826] = ItemType.StoneHoe; + mappings[824] = ItemType.StonePickaxe; + mappings[695] = ItemType.StonePressurePlate; + mappings[823] = ItemType.StoneShovel; + mappings[264] = ItemType.StoneSlab; + mappings[627] = ItemType.StoneStairs; + mappings[822] = ItemType.StoneSword; + mappings[1209] = ItemType.Stonecutter; + mappings[1068] = ItemType.StraySpawnEgg; + mappings[1069] = ItemType.StriderSpawnEgg; + mappings[850] = ItemType.String; + mappings[149] = ItemType.StrippedAcaciaLog; + mappings[159] = ItemType.StrippedAcaciaWood; + mappings[165] = ItemType.StrippedBambooBlock; + mappings[147] = ItemType.StrippedBirchLog; + mappings[157] = ItemType.StrippedBirchWood; + mappings[150] = ItemType.StrippedCherryLog; + mappings[160] = ItemType.StrippedCherryWood; + mappings[163] = ItemType.StrippedCrimsonHyphae; + mappings[153] = ItemType.StrippedCrimsonStem; + mappings[151] = ItemType.StrippedDarkOakLog; + mappings[161] = ItemType.StrippedDarkOakWood; + mappings[148] = ItemType.StrippedJungleLog; + mappings[158] = ItemType.StrippedJungleWood; + mappings[152] = ItemType.StrippedMangroveLog; + mappings[162] = ItemType.StrippedMangroveWood; + mappings[145] = ItemType.StrippedOakLog; + mappings[155] = ItemType.StrippedOakWood; + mappings[146] = ItemType.StrippedSpruceLog; + mappings[156] = ItemType.StrippedSpruceWood; + mappings[164] = ItemType.StrippedWarpedHyphae; + mappings[154] = ItemType.StrippedWarpedStem; + mappings[792] = ItemType.StructureBlock; + mappings[521] = ItemType.StructureVoid; + mappings[962] = ItemType.Sugar; + mappings[243] = ItemType.SugarCane; + mappings[465] = ItemType.Sunflower; + mappings[59] = ItemType.SuspiciousGravel; + mappings[58] = ItemType.SuspiciousSand; + mappings[1190] = ItemType.SuspiciousStew; + mappings[1213] = ItemType.SweetBerries; + mappings[920] = ItemType.TadpoleBucket; + mappings[1070] = ItemType.TadpoleSpawnEgg; + mappings[469] = ItemType.TallGrass; + mappings[671] = ItemType.Target; + mappings[462] = ItemType.Terracotta; + mappings[1274] = ItemType.TideArmorTrimSmithingTemplate; + mappings[189] = ItemType.TintedGlass; + mappings[1160] = ItemType.TippedArrow; + mappings[679] = ItemType.Tnt; + mappings[769] = ItemType.TntMinecart; + mappings[291] = ItemType.Torch; + mappings[231] = ItemType.Torchflower; + mappings[1152] = ItemType.TorchflowerSeeds; + mappings[1163] = ItemType.TotemOfUndying; + mappings[1071] = ItemType.TraderLlamaSpawnEgg; + mappings[678] = ItemType.TrappedChest; + mappings[1325] = ItemType.TrialKey; + mappings[1324] = ItemType.TrialSpawner; + mappings[1185] = ItemType.Trident; + mappings[677] = ItemType.TripwireHook; + mappings[937] = ItemType.TropicalFish; + mappings[918] = ItemType.TropicalFishBucket; + mappings[1072] = ItemType.TropicalFishSpawnEgg; + mappings[599] = ItemType.TubeCoral; + mappings[594] = ItemType.TubeCoralBlock; + mappings[609] = ItemType.TubeCoralFan; + mappings[12] = ItemType.Tuff; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[21] = ItemType.TuffBricks; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[587] = ItemType.TurtleEgg; + mappings[794] = ItemType.TurtleHelmet; + mappings[795] = ItemType.TurtleScute; + mappings[1073] = ItemType.TurtleSpawnEgg; + mappings[242] = ItemType.TwistingVines; + mappings[1327] = ItemType.Vault; + mappings[1261] = ItemType.VerdantFroglight; + mappings[1273] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1074] = ItemType.VexSpawnEgg; + mappings[1075] = ItemType.VillagerSpawnEgg; + mappings[1076] = ItemType.VindicatorSpawnEgg; + mappings[359] = ItemType.Vine; + mappings[1077] = ItemType.WanderingTraderSpawnEgg; + mappings[1271] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1078] = ItemType.WardenSpawnEgg; + mappings[694] = ItemType.WarpedButton; + mappings[721] = ItemType.WarpedDoor; + mappings[321] = ItemType.WarpedFence; + mappings[760] = ItemType.WarpedFenceGate; + mappings[237] = ItemType.WarpedFungus; + mappings[772] = ItemType.WarpedFungusOnAStick; + mappings[907] = ItemType.WarpedHangingSign; + mappings[175] = ItemType.WarpedHyphae; + mappings[34] = ItemType.WarpedNylium; + mappings[46] = ItemType.WarpedPlanks; + mappings[709] = ItemType.WarpedPressurePlate; + mappings[239] = ItemType.WarpedRoots; + mappings[896] = ItemType.WarpedSign; + mappings[263] = ItemType.WarpedSlab; + mappings[394] = ItemType.WarpedStairs; + mappings[143] = ItemType.WarpedStem; + mappings[741] = ItemType.WarpedTrapdoor; + mappings[518] = ItemType.WarpedWartBlock; + mappings[909] = ItemType.WaterBucket; + mappings[116] = ItemType.WaxedChiseledCopper; + mappings[112] = ItemType.WaxedCopperBlock; + mappings[1320] = ItemType.WaxedCopperBulb; + mappings[726] = ItemType.WaxedCopperDoor; + mappings[1312] = ItemType.WaxedCopperGrate; + mappings[746] = ItemType.WaxedCopperTrapdoor; + mappings[120] = ItemType.WaxedCutCopper; + mappings[128] = ItemType.WaxedCutCopperSlab; + mappings[124] = ItemType.WaxedCutCopperStairs; + mappings[117] = ItemType.WaxedExposedChiseledCopper; + mappings[113] = ItemType.WaxedExposedCopper; + mappings[1321] = ItemType.WaxedExposedCopperBulb; + mappings[727] = ItemType.WaxedExposedCopperDoor; + mappings[1313] = ItemType.WaxedExposedCopperGrate; + mappings[747] = ItemType.WaxedExposedCopperTrapdoor; + mappings[121] = ItemType.WaxedExposedCutCopper; + mappings[129] = ItemType.WaxedExposedCutCopperSlab; + mappings[125] = ItemType.WaxedExposedCutCopperStairs; + mappings[119] = ItemType.WaxedOxidizedChiseledCopper; + mappings[115] = ItemType.WaxedOxidizedCopper; + mappings[1323] = ItemType.WaxedOxidizedCopperBulb; + mappings[729] = ItemType.WaxedOxidizedCopperDoor; + mappings[1315] = ItemType.WaxedOxidizedCopperGrate; + mappings[749] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[123] = ItemType.WaxedOxidizedCutCopper; + mappings[131] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[127] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[118] = ItemType.WaxedWeatheredChiseledCopper; + mappings[114] = ItemType.WaxedWeatheredCopper; + mappings[1322] = ItemType.WaxedWeatheredCopperBulb; + mappings[728] = ItemType.WaxedWeatheredCopperDoor; + mappings[1314] = ItemType.WaxedWeatheredCopperGrate; + mappings[748] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[122] = ItemType.WaxedWeatheredCutCopper; + mappings[130] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[126] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[1278] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[98] = ItemType.WeatheredChiseledCopper; + mappings[94] = ItemType.WeatheredCopper; + mappings[1318] = ItemType.WeatheredCopperBulb; + mappings[724] = ItemType.WeatheredCopperDoor; + mappings[1310] = ItemType.WeatheredCopperGrate; + mappings[744] = ItemType.WeatheredCopperTrapdoor; + mappings[102] = ItemType.WeatheredCutCopper; + mappings[110] = ItemType.WeatheredCutCopperSlab; + mappings[106] = ItemType.WeatheredCutCopperStairs; + mappings[241] = ItemType.WeepingVines; + mappings[187] = ItemType.WetSponge; + mappings[854] = ItemType.Wheat; + mappings[853] = ItemType.WheatSeeds; + mappings[1133] = ItemType.WhiteBanner; + mappings[964] = ItemType.WhiteBed; + mappings[1239] = ItemType.WhiteCandle; + mappings[446] = ItemType.WhiteCarpet; + mappings[555] = ItemType.WhiteConcrete; + mappings[571] = ItemType.WhiteConcretePowder; + mappings[944] = ItemType.WhiteDye; + mappings[539] = ItemType.WhiteGlazedTerracotta; + mappings[523] = ItemType.WhiteShulkerBox; + mappings[471] = ItemType.WhiteStainedGlass; + mappings[487] = ItemType.WhiteStainedGlassPane; + mappings[427] = ItemType.WhiteTerracotta; + mappings[225] = ItemType.WhiteTulip; + mappings[202] = ItemType.WhiteWool; + mappings[1270] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1090] = ItemType.WindCharge; + mappings[1079] = ItemType.WitchSpawnEgg; + mappings[230] = ItemType.WitherRose; + mappings[1104] = ItemType.WitherSkeletonSkull; + mappings[1081] = ItemType.WitherSkeletonSpawnEgg; + mappings[1080] = ItemType.WitherSpawnEgg; + mappings[797] = ItemType.WolfArmor; + mappings[1082] = ItemType.WolfSpawnEgg; + mappings[820] = ItemType.WoodenAxe; + mappings[821] = ItemType.WoodenHoe; + mappings[819] = ItemType.WoodenPickaxe; + mappings[818] = ItemType.WoodenShovel; + mappings[817] = ItemType.WoodenSword; + mappings[1091] = ItemType.WritableBook; + mappings[1092] = ItemType.WrittenBook; + mappings[1137] = ItemType.YellowBanner; + mappings[968] = ItemType.YellowBed; + mappings[1243] = ItemType.YellowCandle; + mappings[450] = ItemType.YellowCarpet; + mappings[559] = ItemType.YellowConcrete; + mappings[575] = ItemType.YellowConcretePowder; + mappings[948] = ItemType.YellowDye; + mappings[543] = ItemType.YellowGlazedTerracotta; + mappings[527] = ItemType.YellowShulkerBox; + mappings[475] = ItemType.YellowStainedGlass; + mappings[491] = ItemType.YellowStainedGlassPane; + mappings[431] = ItemType.YellowTerracotta; + mappings[206] = ItemType.YellowWool; + mappings[1083] = ItemType.ZoglinSpawnEgg; + mappings[1106] = ItemType.ZombieHead; + mappings[1085] = ItemType.ZombieHorseSpawnEgg; + mappings[1084] = ItemType.ZombieSpawnEgg; + mappings[1086] = ItemType.ZombieVillagerSpawnEgg; + mappings[1087] = ItemType.ZombifiedPiglinSpawnEgg; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette121.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette121.cs new file mode 100644 index 00000000..6759d899 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette121.cs @@ -0,0 +1,1351 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette121 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette121() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.MangrovePlanks; + mappings[44] = ItemType.BambooPlanks; + mappings[45] = ItemType.CrimsonPlanks; + mappings[46] = ItemType.WarpedPlanks; + mappings[47] = ItemType.BambooMosaic; + mappings[48] = ItemType.OakSapling; + mappings[49] = ItemType.SpruceSapling; + mappings[50] = ItemType.BirchSapling; + mappings[51] = ItemType.JungleSapling; + mappings[52] = ItemType.AcaciaSapling; + mappings[53] = ItemType.CherrySapling; + mappings[54] = ItemType.DarkOakSapling; + mappings[55] = ItemType.MangrovePropagule; + mappings[56] = ItemType.Bedrock; + mappings[57] = ItemType.Sand; + mappings[58] = ItemType.SuspiciousSand; + mappings[59] = ItemType.SuspiciousGravel; + mappings[60] = ItemType.RedSand; + mappings[61] = ItemType.Gravel; + mappings[62] = ItemType.CoalOre; + mappings[63] = ItemType.DeepslateCoalOre; + mappings[64] = ItemType.IronOre; + mappings[65] = ItemType.DeepslateIronOre; + mappings[66] = ItemType.CopperOre; + mappings[67] = ItemType.DeepslateCopperOre; + mappings[68] = ItemType.GoldOre; + mappings[69] = ItemType.DeepslateGoldOre; + mappings[70] = ItemType.RedstoneOre; + mappings[71] = ItemType.DeepslateRedstoneOre; + mappings[72] = ItemType.EmeraldOre; + mappings[73] = ItemType.DeepslateEmeraldOre; + mappings[74] = ItemType.LapisOre; + mappings[75] = ItemType.DeepslateLapisOre; + mappings[76] = ItemType.DiamondOre; + mappings[77] = ItemType.DeepslateDiamondOre; + mappings[78] = ItemType.NetherGoldOre; + mappings[79] = ItemType.NetherQuartzOre; + mappings[80] = ItemType.AncientDebris; + mappings[81] = ItemType.CoalBlock; + mappings[82] = ItemType.RawIronBlock; + mappings[83] = ItemType.RawCopperBlock; + mappings[84] = ItemType.RawGoldBlock; + mappings[85] = ItemType.HeavyCore; + mappings[86] = ItemType.AmethystBlock; + mappings[87] = ItemType.BuddingAmethyst; + mappings[88] = ItemType.IronBlock; + mappings[89] = ItemType.CopperBlock; + mappings[90] = ItemType.GoldBlock; + mappings[91] = ItemType.DiamondBlock; + mappings[92] = ItemType.NetheriteBlock; + mappings[93] = ItemType.ExposedCopper; + mappings[94] = ItemType.WeatheredCopper; + mappings[95] = ItemType.OxidizedCopper; + mappings[96] = ItemType.ChiseledCopper; + mappings[97] = ItemType.ExposedChiseledCopper; + mappings[98] = ItemType.WeatheredChiseledCopper; + mappings[99] = ItemType.OxidizedChiseledCopper; + mappings[100] = ItemType.CutCopper; + mappings[101] = ItemType.ExposedCutCopper; + mappings[102] = ItemType.WeatheredCutCopper; + mappings[103] = ItemType.OxidizedCutCopper; + mappings[104] = ItemType.CutCopperStairs; + mappings[105] = ItemType.ExposedCutCopperStairs; + mappings[106] = ItemType.WeatheredCutCopperStairs; + mappings[107] = ItemType.OxidizedCutCopperStairs; + mappings[108] = ItemType.CutCopperSlab; + mappings[109] = ItemType.ExposedCutCopperSlab; + mappings[110] = ItemType.WeatheredCutCopperSlab; + mappings[111] = ItemType.OxidizedCutCopperSlab; + mappings[112] = ItemType.WaxedCopperBlock; + mappings[113] = ItemType.WaxedExposedCopper; + mappings[114] = ItemType.WaxedWeatheredCopper; + mappings[115] = ItemType.WaxedOxidizedCopper; + mappings[116] = ItemType.WaxedChiseledCopper; + mappings[117] = ItemType.WaxedExposedChiseledCopper; + mappings[118] = ItemType.WaxedWeatheredChiseledCopper; + mappings[119] = ItemType.WaxedOxidizedChiseledCopper; + mappings[120] = ItemType.WaxedCutCopper; + mappings[121] = ItemType.WaxedExposedCutCopper; + mappings[122] = ItemType.WaxedWeatheredCutCopper; + mappings[123] = ItemType.WaxedOxidizedCutCopper; + mappings[124] = ItemType.WaxedCutCopperStairs; + mappings[125] = ItemType.WaxedExposedCutCopperStairs; + mappings[126] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[127] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[128] = ItemType.WaxedCutCopperSlab; + mappings[129] = ItemType.WaxedExposedCutCopperSlab; + mappings[130] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[131] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[132] = ItemType.OakLog; + mappings[133] = ItemType.SpruceLog; + mappings[134] = ItemType.BirchLog; + mappings[135] = ItemType.JungleLog; + mappings[136] = ItemType.AcaciaLog; + mappings[137] = ItemType.CherryLog; + mappings[138] = ItemType.DarkOakLog; + mappings[139] = ItemType.MangroveLog; + mappings[140] = ItemType.MangroveRoots; + mappings[141] = ItemType.MuddyMangroveRoots; + mappings[142] = ItemType.CrimsonStem; + mappings[143] = ItemType.WarpedStem; + mappings[144] = ItemType.BambooBlock; + mappings[145] = ItemType.StrippedOakLog; + mappings[146] = ItemType.StrippedSpruceLog; + mappings[147] = ItemType.StrippedBirchLog; + mappings[148] = ItemType.StrippedJungleLog; + mappings[149] = ItemType.StrippedAcaciaLog; + mappings[150] = ItemType.StrippedCherryLog; + mappings[151] = ItemType.StrippedDarkOakLog; + mappings[152] = ItemType.StrippedMangroveLog; + mappings[153] = ItemType.StrippedCrimsonStem; + mappings[154] = ItemType.StrippedWarpedStem; + mappings[155] = ItemType.StrippedOakWood; + mappings[156] = ItemType.StrippedSpruceWood; + mappings[157] = ItemType.StrippedBirchWood; + mappings[158] = ItemType.StrippedJungleWood; + mappings[159] = ItemType.StrippedAcaciaWood; + mappings[160] = ItemType.StrippedCherryWood; + mappings[161] = ItemType.StrippedDarkOakWood; + mappings[162] = ItemType.StrippedMangroveWood; + mappings[163] = ItemType.StrippedCrimsonHyphae; + mappings[164] = ItemType.StrippedWarpedHyphae; + mappings[165] = ItemType.StrippedBambooBlock; + mappings[166] = ItemType.OakWood; + mappings[167] = ItemType.SpruceWood; + mappings[168] = ItemType.BirchWood; + mappings[169] = ItemType.JungleWood; + mappings[170] = ItemType.AcaciaWood; + mappings[171] = ItemType.CherryWood; + mappings[172] = ItemType.DarkOakWood; + mappings[173] = ItemType.MangroveWood; + mappings[174] = ItemType.CrimsonHyphae; + mappings[175] = ItemType.WarpedHyphae; + mappings[176] = ItemType.OakLeaves; + mappings[177] = ItemType.SpruceLeaves; + mappings[178] = ItemType.BirchLeaves; + mappings[179] = ItemType.JungleLeaves; + mappings[180] = ItemType.AcaciaLeaves; + mappings[181] = ItemType.CherryLeaves; + mappings[182] = ItemType.DarkOakLeaves; + mappings[183] = ItemType.MangroveLeaves; + mappings[184] = ItemType.AzaleaLeaves; + mappings[185] = ItemType.FloweringAzaleaLeaves; + mappings[186] = ItemType.Sponge; + mappings[187] = ItemType.WetSponge; + mappings[188] = ItemType.Glass; + mappings[189] = ItemType.TintedGlass; + mappings[190] = ItemType.LapisBlock; + mappings[191] = ItemType.Sandstone; + mappings[192] = ItemType.ChiseledSandstone; + mappings[193] = ItemType.CutSandstone; + mappings[194] = ItemType.Cobweb; + mappings[195] = ItemType.ShortGrass; + mappings[196] = ItemType.Fern; + mappings[197] = ItemType.Azalea; + mappings[198] = ItemType.FloweringAzalea; + mappings[199] = ItemType.DeadBush; + mappings[200] = ItemType.Seagrass; + mappings[201] = ItemType.SeaPickle; + mappings[202] = ItemType.WhiteWool; + mappings[203] = ItemType.OrangeWool; + mappings[204] = ItemType.MagentaWool; + mappings[205] = ItemType.LightBlueWool; + mappings[206] = ItemType.YellowWool; + mappings[207] = ItemType.LimeWool; + mappings[208] = ItemType.PinkWool; + mappings[209] = ItemType.GrayWool; + mappings[210] = ItemType.LightGrayWool; + mappings[211] = ItemType.CyanWool; + mappings[212] = ItemType.PurpleWool; + mappings[213] = ItemType.BlueWool; + mappings[214] = ItemType.BrownWool; + mappings[215] = ItemType.GreenWool; + mappings[216] = ItemType.RedWool; + mappings[217] = ItemType.BlackWool; + mappings[218] = ItemType.Dandelion; + mappings[219] = ItemType.Poppy; + mappings[220] = ItemType.BlueOrchid; + mappings[221] = ItemType.Allium; + mappings[222] = ItemType.AzureBluet; + mappings[223] = ItemType.RedTulip; + mappings[224] = ItemType.OrangeTulip; + mappings[225] = ItemType.WhiteTulip; + mappings[226] = ItemType.PinkTulip; + mappings[227] = ItemType.OxeyeDaisy; + mappings[228] = ItemType.Cornflower; + mappings[229] = ItemType.LilyOfTheValley; + mappings[230] = ItemType.WitherRose; + mappings[231] = ItemType.Torchflower; + mappings[232] = ItemType.PitcherPlant; + mappings[233] = ItemType.SporeBlossom; + mappings[234] = ItemType.BrownMushroom; + mappings[235] = ItemType.RedMushroom; + mappings[236] = ItemType.CrimsonFungus; + mappings[237] = ItemType.WarpedFungus; + mappings[238] = ItemType.CrimsonRoots; + mappings[239] = ItemType.WarpedRoots; + mappings[240] = ItemType.NetherSprouts; + mappings[241] = ItemType.WeepingVines; + mappings[242] = ItemType.TwistingVines; + mappings[243] = ItemType.SugarCane; + mappings[244] = ItemType.Kelp; + mappings[245] = ItemType.MossCarpet; + mappings[246] = ItemType.PinkPetals; + mappings[247] = ItemType.MossBlock; + mappings[248] = ItemType.HangingRoots; + mappings[249] = ItemType.BigDripleaf; + mappings[250] = ItemType.SmallDripleaf; + mappings[251] = ItemType.Bamboo; + mappings[252] = ItemType.OakSlab; + mappings[253] = ItemType.SpruceSlab; + mappings[254] = ItemType.BirchSlab; + mappings[255] = ItemType.JungleSlab; + mappings[256] = ItemType.AcaciaSlab; + mappings[257] = ItemType.CherrySlab; + mappings[258] = ItemType.DarkOakSlab; + mappings[259] = ItemType.MangroveSlab; + mappings[260] = ItemType.BambooSlab; + mappings[261] = ItemType.BambooMosaicSlab; + mappings[262] = ItemType.CrimsonSlab; + mappings[263] = ItemType.WarpedSlab; + mappings[264] = ItemType.StoneSlab; + mappings[265] = ItemType.SmoothStoneSlab; + mappings[266] = ItemType.SandstoneSlab; + mappings[267] = ItemType.CutSandstoneSlab; + mappings[268] = ItemType.PetrifiedOakSlab; + mappings[269] = ItemType.CobblestoneSlab; + mappings[270] = ItemType.BrickSlab; + mappings[271] = ItemType.StoneBrickSlab; + mappings[272] = ItemType.MudBrickSlab; + mappings[273] = ItemType.NetherBrickSlab; + mappings[274] = ItemType.QuartzSlab; + mappings[275] = ItemType.RedSandstoneSlab; + mappings[276] = ItemType.CutRedSandstoneSlab; + mappings[277] = ItemType.PurpurSlab; + mappings[278] = ItemType.PrismarineSlab; + mappings[279] = ItemType.PrismarineBrickSlab; + mappings[280] = ItemType.DarkPrismarineSlab; + mappings[281] = ItemType.SmoothQuartz; + mappings[282] = ItemType.SmoothRedSandstone; + mappings[283] = ItemType.SmoothSandstone; + mappings[284] = ItemType.SmoothStone; + mappings[285] = ItemType.Bricks; + mappings[286] = ItemType.Bookshelf; + mappings[287] = ItemType.ChiseledBookshelf; + mappings[288] = ItemType.DecoratedPot; + mappings[289] = ItemType.MossyCobblestone; + mappings[290] = ItemType.Obsidian; + mappings[291] = ItemType.Torch; + mappings[292] = ItemType.EndRod; + mappings[293] = ItemType.ChorusPlant; + mappings[294] = ItemType.ChorusFlower; + mappings[295] = ItemType.PurpurBlock; + mappings[296] = ItemType.PurpurPillar; + mappings[297] = ItemType.PurpurStairs; + mappings[298] = ItemType.Spawner; + mappings[299] = ItemType.Chest; + mappings[300] = ItemType.CraftingTable; + mappings[301] = ItemType.Farmland; + mappings[302] = ItemType.Furnace; + mappings[303] = ItemType.Ladder; + mappings[304] = ItemType.CobblestoneStairs; + mappings[305] = ItemType.Snow; + mappings[306] = ItemType.Ice; + mappings[307] = ItemType.SnowBlock; + mappings[308] = ItemType.Cactus; + mappings[309] = ItemType.Clay; + mappings[310] = ItemType.Jukebox; + mappings[311] = ItemType.OakFence; + mappings[312] = ItemType.SpruceFence; + mappings[313] = ItemType.BirchFence; + mappings[314] = ItemType.JungleFence; + mappings[315] = ItemType.AcaciaFence; + mappings[316] = ItemType.CherryFence; + mappings[317] = ItemType.DarkOakFence; + mappings[318] = ItemType.MangroveFence; + mappings[319] = ItemType.BambooFence; + mappings[320] = ItemType.CrimsonFence; + mappings[321] = ItemType.WarpedFence; + mappings[322] = ItemType.Pumpkin; + mappings[323] = ItemType.CarvedPumpkin; + mappings[324] = ItemType.JackOLantern; + mappings[325] = ItemType.Netherrack; + mappings[326] = ItemType.SoulSand; + mappings[327] = ItemType.SoulSoil; + mappings[328] = ItemType.Basalt; + mappings[329] = ItemType.PolishedBasalt; + mappings[330] = ItemType.SmoothBasalt; + mappings[331] = ItemType.SoulTorch; + mappings[332] = ItemType.Glowstone; + mappings[333] = ItemType.InfestedStone; + mappings[334] = ItemType.InfestedCobblestone; + mappings[335] = ItemType.InfestedStoneBricks; + mappings[336] = ItemType.InfestedMossyStoneBricks; + mappings[337] = ItemType.InfestedCrackedStoneBricks; + mappings[338] = ItemType.InfestedChiseledStoneBricks; + mappings[339] = ItemType.InfestedDeepslate; + mappings[340] = ItemType.StoneBricks; + mappings[341] = ItemType.MossyStoneBricks; + mappings[342] = ItemType.CrackedStoneBricks; + mappings[343] = ItemType.ChiseledStoneBricks; + mappings[344] = ItemType.PackedMud; + mappings[345] = ItemType.MudBricks; + mappings[346] = ItemType.DeepslateBricks; + mappings[347] = ItemType.CrackedDeepslateBricks; + mappings[348] = ItemType.DeepslateTiles; + mappings[349] = ItemType.CrackedDeepslateTiles; + mappings[350] = ItemType.ChiseledDeepslate; + mappings[351] = ItemType.ReinforcedDeepslate; + mappings[352] = ItemType.BrownMushroomBlock; + mappings[353] = ItemType.RedMushroomBlock; + mappings[354] = ItemType.MushroomStem; + mappings[355] = ItemType.IronBars; + mappings[356] = ItemType.Chain; + mappings[357] = ItemType.GlassPane; + mappings[358] = ItemType.Melon; + mappings[359] = ItemType.Vine; + mappings[360] = ItemType.GlowLichen; + mappings[361] = ItemType.BrickStairs; + mappings[362] = ItemType.StoneBrickStairs; + mappings[363] = ItemType.MudBrickStairs; + mappings[364] = ItemType.Mycelium; + mappings[365] = ItemType.LilyPad; + mappings[366] = ItemType.NetherBricks; + mappings[367] = ItemType.CrackedNetherBricks; + mappings[368] = ItemType.ChiseledNetherBricks; + mappings[369] = ItemType.NetherBrickFence; + mappings[370] = ItemType.NetherBrickStairs; + mappings[371] = ItemType.Sculk; + mappings[372] = ItemType.SculkVein; + mappings[373] = ItemType.SculkCatalyst; + mappings[374] = ItemType.SculkShrieker; + mappings[375] = ItemType.EnchantingTable; + mappings[376] = ItemType.EndPortalFrame; + mappings[377] = ItemType.EndStone; + mappings[378] = ItemType.EndStoneBricks; + mappings[379] = ItemType.DragonEgg; + mappings[380] = ItemType.SandstoneStairs; + mappings[381] = ItemType.EnderChest; + mappings[382] = ItemType.EmeraldBlock; + mappings[383] = ItemType.OakStairs; + mappings[384] = ItemType.SpruceStairs; + mappings[385] = ItemType.BirchStairs; + mappings[386] = ItemType.JungleStairs; + mappings[387] = ItemType.AcaciaStairs; + mappings[388] = ItemType.CherryStairs; + mappings[389] = ItemType.DarkOakStairs; + mappings[390] = ItemType.MangroveStairs; + mappings[391] = ItemType.BambooStairs; + mappings[392] = ItemType.BambooMosaicStairs; + mappings[393] = ItemType.CrimsonStairs; + mappings[394] = ItemType.WarpedStairs; + mappings[395] = ItemType.CommandBlock; + mappings[396] = ItemType.Beacon; + mappings[397] = ItemType.CobblestoneWall; + mappings[398] = ItemType.MossyCobblestoneWall; + mappings[399] = ItemType.BrickWall; + mappings[400] = ItemType.PrismarineWall; + mappings[401] = ItemType.RedSandstoneWall; + mappings[402] = ItemType.MossyStoneBrickWall; + mappings[403] = ItemType.GraniteWall; + mappings[404] = ItemType.StoneBrickWall; + mappings[405] = ItemType.MudBrickWall; + mappings[406] = ItemType.NetherBrickWall; + mappings[407] = ItemType.AndesiteWall; + mappings[408] = ItemType.RedNetherBrickWall; + mappings[409] = ItemType.SandstoneWall; + mappings[410] = ItemType.EndStoneBrickWall; + mappings[411] = ItemType.DioriteWall; + mappings[412] = ItemType.BlackstoneWall; + mappings[413] = ItemType.PolishedBlackstoneWall; + mappings[414] = ItemType.PolishedBlackstoneBrickWall; + mappings[415] = ItemType.CobbledDeepslateWall; + mappings[416] = ItemType.PolishedDeepslateWall; + mappings[417] = ItemType.DeepslateBrickWall; + mappings[418] = ItemType.DeepslateTileWall; + mappings[419] = ItemType.Anvil; + mappings[420] = ItemType.ChippedAnvil; + mappings[421] = ItemType.DamagedAnvil; + mappings[422] = ItemType.ChiseledQuartzBlock; + mappings[423] = ItemType.QuartzBlock; + mappings[424] = ItemType.QuartzBricks; + mappings[425] = ItemType.QuartzPillar; + mappings[426] = ItemType.QuartzStairs; + mappings[427] = ItemType.WhiteTerracotta; + mappings[428] = ItemType.OrangeTerracotta; + mappings[429] = ItemType.MagentaTerracotta; + mappings[430] = ItemType.LightBlueTerracotta; + mappings[431] = ItemType.YellowTerracotta; + mappings[432] = ItemType.LimeTerracotta; + mappings[433] = ItemType.PinkTerracotta; + mappings[434] = ItemType.GrayTerracotta; + mappings[435] = ItemType.LightGrayTerracotta; + mappings[436] = ItemType.CyanTerracotta; + mappings[437] = ItemType.PurpleTerracotta; + mappings[438] = ItemType.BlueTerracotta; + mappings[439] = ItemType.BrownTerracotta; + mappings[440] = ItemType.GreenTerracotta; + mappings[441] = ItemType.RedTerracotta; + mappings[442] = ItemType.BlackTerracotta; + mappings[443] = ItemType.Barrier; + mappings[444] = ItemType.Light; + mappings[445] = ItemType.HayBlock; + mappings[446] = ItemType.WhiteCarpet; + mappings[447] = ItemType.OrangeCarpet; + mappings[448] = ItemType.MagentaCarpet; + mappings[449] = ItemType.LightBlueCarpet; + mappings[450] = ItemType.YellowCarpet; + mappings[451] = ItemType.LimeCarpet; + mappings[452] = ItemType.PinkCarpet; + mappings[453] = ItemType.GrayCarpet; + mappings[454] = ItemType.LightGrayCarpet; + mappings[455] = ItemType.CyanCarpet; + mappings[456] = ItemType.PurpleCarpet; + mappings[457] = ItemType.BlueCarpet; + mappings[458] = ItemType.BrownCarpet; + mappings[459] = ItemType.GreenCarpet; + mappings[460] = ItemType.RedCarpet; + mappings[461] = ItemType.BlackCarpet; + mappings[462] = ItemType.Terracotta; + mappings[463] = ItemType.PackedIce; + mappings[464] = ItemType.DirtPath; + mappings[465] = ItemType.Sunflower; + mappings[466] = ItemType.Lilac; + mappings[467] = ItemType.RoseBush; + mappings[468] = ItemType.Peony; + mappings[469] = ItemType.TallGrass; + mappings[470] = ItemType.LargeFern; + mappings[471] = ItemType.WhiteStainedGlass; + mappings[472] = ItemType.OrangeStainedGlass; + mappings[473] = ItemType.MagentaStainedGlass; + mappings[474] = ItemType.LightBlueStainedGlass; + mappings[475] = ItemType.YellowStainedGlass; + mappings[476] = ItemType.LimeStainedGlass; + mappings[477] = ItemType.PinkStainedGlass; + mappings[478] = ItemType.GrayStainedGlass; + mappings[479] = ItemType.LightGrayStainedGlass; + mappings[480] = ItemType.CyanStainedGlass; + mappings[481] = ItemType.PurpleStainedGlass; + mappings[482] = ItemType.BlueStainedGlass; + mappings[483] = ItemType.BrownStainedGlass; + mappings[484] = ItemType.GreenStainedGlass; + mappings[485] = ItemType.RedStainedGlass; + mappings[486] = ItemType.BlackStainedGlass; + mappings[487] = ItemType.WhiteStainedGlassPane; + mappings[488] = ItemType.OrangeStainedGlassPane; + mappings[489] = ItemType.MagentaStainedGlassPane; + mappings[490] = ItemType.LightBlueStainedGlassPane; + mappings[491] = ItemType.YellowStainedGlassPane; + mappings[492] = ItemType.LimeStainedGlassPane; + mappings[493] = ItemType.PinkStainedGlassPane; + mappings[494] = ItemType.GrayStainedGlassPane; + mappings[495] = ItemType.LightGrayStainedGlassPane; + mappings[496] = ItemType.CyanStainedGlassPane; + mappings[497] = ItemType.PurpleStainedGlassPane; + mappings[498] = ItemType.BlueStainedGlassPane; + mappings[499] = ItemType.BrownStainedGlassPane; + mappings[500] = ItemType.GreenStainedGlassPane; + mappings[501] = ItemType.RedStainedGlassPane; + mappings[502] = ItemType.BlackStainedGlassPane; + mappings[503] = ItemType.Prismarine; + mappings[504] = ItemType.PrismarineBricks; + mappings[505] = ItemType.DarkPrismarine; + mappings[506] = ItemType.PrismarineStairs; + mappings[507] = ItemType.PrismarineBrickStairs; + mappings[508] = ItemType.DarkPrismarineStairs; + mappings[509] = ItemType.SeaLantern; + mappings[510] = ItemType.RedSandstone; + mappings[511] = ItemType.ChiseledRedSandstone; + mappings[512] = ItemType.CutRedSandstone; + mappings[513] = ItemType.RedSandstoneStairs; + mappings[514] = ItemType.RepeatingCommandBlock; + mappings[515] = ItemType.ChainCommandBlock; + mappings[516] = ItemType.MagmaBlock; + mappings[517] = ItemType.NetherWartBlock; + mappings[518] = ItemType.WarpedWartBlock; + mappings[519] = ItemType.RedNetherBricks; + mappings[520] = ItemType.BoneBlock; + mappings[521] = ItemType.StructureVoid; + mappings[522] = ItemType.ShulkerBox; + mappings[523] = ItemType.WhiteShulkerBox; + mappings[524] = ItemType.OrangeShulkerBox; + mappings[525] = ItemType.MagentaShulkerBox; + mappings[526] = ItemType.LightBlueShulkerBox; + mappings[527] = ItemType.YellowShulkerBox; + mappings[528] = ItemType.LimeShulkerBox; + mappings[529] = ItemType.PinkShulkerBox; + mappings[530] = ItemType.GrayShulkerBox; + mappings[531] = ItemType.LightGrayShulkerBox; + mappings[532] = ItemType.CyanShulkerBox; + mappings[533] = ItemType.PurpleShulkerBox; + mappings[534] = ItemType.BlueShulkerBox; + mappings[535] = ItemType.BrownShulkerBox; + mappings[536] = ItemType.GreenShulkerBox; + mappings[537] = ItemType.RedShulkerBox; + mappings[538] = ItemType.BlackShulkerBox; + mappings[539] = ItemType.WhiteGlazedTerracotta; + mappings[540] = ItemType.OrangeGlazedTerracotta; + mappings[541] = ItemType.MagentaGlazedTerracotta; + mappings[542] = ItemType.LightBlueGlazedTerracotta; + mappings[543] = ItemType.YellowGlazedTerracotta; + mappings[544] = ItemType.LimeGlazedTerracotta; + mappings[545] = ItemType.PinkGlazedTerracotta; + mappings[546] = ItemType.GrayGlazedTerracotta; + mappings[547] = ItemType.LightGrayGlazedTerracotta; + mappings[548] = ItemType.CyanGlazedTerracotta; + mappings[549] = ItemType.PurpleGlazedTerracotta; + mappings[550] = ItemType.BlueGlazedTerracotta; + mappings[551] = ItemType.BrownGlazedTerracotta; + mappings[552] = ItemType.GreenGlazedTerracotta; + mappings[553] = ItemType.RedGlazedTerracotta; + mappings[554] = ItemType.BlackGlazedTerracotta; + mappings[555] = ItemType.WhiteConcrete; + mappings[556] = ItemType.OrangeConcrete; + mappings[557] = ItemType.MagentaConcrete; + mappings[558] = ItemType.LightBlueConcrete; + mappings[559] = ItemType.YellowConcrete; + mappings[560] = ItemType.LimeConcrete; + mappings[561] = ItemType.PinkConcrete; + mappings[562] = ItemType.GrayConcrete; + mappings[563] = ItemType.LightGrayConcrete; + mappings[564] = ItemType.CyanConcrete; + mappings[565] = ItemType.PurpleConcrete; + mappings[566] = ItemType.BlueConcrete; + mappings[567] = ItemType.BrownConcrete; + mappings[568] = ItemType.GreenConcrete; + mappings[569] = ItemType.RedConcrete; + mappings[570] = ItemType.BlackConcrete; + mappings[571] = ItemType.WhiteConcretePowder; + mappings[572] = ItemType.OrangeConcretePowder; + mappings[573] = ItemType.MagentaConcretePowder; + mappings[574] = ItemType.LightBlueConcretePowder; + mappings[575] = ItemType.YellowConcretePowder; + mappings[576] = ItemType.LimeConcretePowder; + mappings[577] = ItemType.PinkConcretePowder; + mappings[578] = ItemType.GrayConcretePowder; + mappings[579] = ItemType.LightGrayConcretePowder; + mappings[580] = ItemType.CyanConcretePowder; + mappings[581] = ItemType.PurpleConcretePowder; + mappings[582] = ItemType.BlueConcretePowder; + mappings[583] = ItemType.BrownConcretePowder; + mappings[584] = ItemType.GreenConcretePowder; + mappings[585] = ItemType.RedConcretePowder; + mappings[586] = ItemType.BlackConcretePowder; + mappings[587] = ItemType.TurtleEgg; + mappings[588] = ItemType.SnifferEgg; + mappings[589] = ItemType.DeadTubeCoralBlock; + mappings[590] = ItemType.DeadBrainCoralBlock; + mappings[591] = ItemType.DeadBubbleCoralBlock; + mappings[592] = ItemType.DeadFireCoralBlock; + mappings[593] = ItemType.DeadHornCoralBlock; + mappings[594] = ItemType.TubeCoralBlock; + mappings[595] = ItemType.BrainCoralBlock; + mappings[596] = ItemType.BubbleCoralBlock; + mappings[597] = ItemType.FireCoralBlock; + mappings[598] = ItemType.HornCoralBlock; + mappings[599] = ItemType.TubeCoral; + mappings[600] = ItemType.BrainCoral; + mappings[601] = ItemType.BubbleCoral; + mappings[602] = ItemType.FireCoral; + mappings[603] = ItemType.HornCoral; + mappings[604] = ItemType.DeadBrainCoral; + mappings[605] = ItemType.DeadBubbleCoral; + mappings[606] = ItemType.DeadFireCoral; + mappings[607] = ItemType.DeadHornCoral; + mappings[608] = ItemType.DeadTubeCoral; + mappings[609] = ItemType.TubeCoralFan; + mappings[610] = ItemType.BrainCoralFan; + mappings[611] = ItemType.BubbleCoralFan; + mappings[612] = ItemType.FireCoralFan; + mappings[613] = ItemType.HornCoralFan; + mappings[614] = ItemType.DeadTubeCoralFan; + mappings[615] = ItemType.DeadBrainCoralFan; + mappings[616] = ItemType.DeadBubbleCoralFan; + mappings[617] = ItemType.DeadFireCoralFan; + mappings[618] = ItemType.DeadHornCoralFan; + mappings[619] = ItemType.BlueIce; + mappings[620] = ItemType.Conduit; + mappings[621] = ItemType.PolishedGraniteStairs; + mappings[622] = ItemType.SmoothRedSandstoneStairs; + mappings[623] = ItemType.MossyStoneBrickStairs; + mappings[624] = ItemType.PolishedDioriteStairs; + mappings[625] = ItemType.MossyCobblestoneStairs; + mappings[626] = ItemType.EndStoneBrickStairs; + mappings[627] = ItemType.StoneStairs; + mappings[628] = ItemType.SmoothSandstoneStairs; + mappings[629] = ItemType.SmoothQuartzStairs; + mappings[630] = ItemType.GraniteStairs; + mappings[631] = ItemType.AndesiteStairs; + mappings[632] = ItemType.RedNetherBrickStairs; + mappings[633] = ItemType.PolishedAndesiteStairs; + mappings[634] = ItemType.DioriteStairs; + mappings[635] = ItemType.CobbledDeepslateStairs; + mappings[636] = ItemType.PolishedDeepslateStairs; + mappings[637] = ItemType.DeepslateBrickStairs; + mappings[638] = ItemType.DeepslateTileStairs; + mappings[639] = ItemType.PolishedGraniteSlab; + mappings[640] = ItemType.SmoothRedSandstoneSlab; + mappings[641] = ItemType.MossyStoneBrickSlab; + mappings[642] = ItemType.PolishedDioriteSlab; + mappings[643] = ItemType.MossyCobblestoneSlab; + mappings[644] = ItemType.EndStoneBrickSlab; + mappings[645] = ItemType.SmoothSandstoneSlab; + mappings[646] = ItemType.SmoothQuartzSlab; + mappings[647] = ItemType.GraniteSlab; + mappings[648] = ItemType.AndesiteSlab; + mappings[649] = ItemType.RedNetherBrickSlab; + mappings[650] = ItemType.PolishedAndesiteSlab; + mappings[651] = ItemType.DioriteSlab; + mappings[652] = ItemType.CobbledDeepslateSlab; + mappings[653] = ItemType.PolishedDeepslateSlab; + mappings[654] = ItemType.DeepslateBrickSlab; + mappings[655] = ItemType.DeepslateTileSlab; + mappings[656] = ItemType.Scaffolding; + mappings[657] = ItemType.Redstone; + mappings[658] = ItemType.RedstoneTorch; + mappings[659] = ItemType.RedstoneBlock; + mappings[660] = ItemType.Repeater; + mappings[661] = ItemType.Comparator; + mappings[662] = ItemType.Piston; + mappings[663] = ItemType.StickyPiston; + mappings[664] = ItemType.SlimeBlock; + mappings[665] = ItemType.HoneyBlock; + mappings[666] = ItemType.Observer; + mappings[667] = ItemType.Hopper; + mappings[668] = ItemType.Dispenser; + mappings[669] = ItemType.Dropper; + mappings[670] = ItemType.Lectern; + mappings[671] = ItemType.Target; + mappings[672] = ItemType.Lever; + mappings[673] = ItemType.LightningRod; + mappings[674] = ItemType.DaylightDetector; + mappings[675] = ItemType.SculkSensor; + mappings[676] = ItemType.CalibratedSculkSensor; + mappings[677] = ItemType.TripwireHook; + mappings[678] = ItemType.TrappedChest; + mappings[679] = ItemType.Tnt; + mappings[680] = ItemType.RedstoneLamp; + mappings[681] = ItemType.NoteBlock; + mappings[682] = ItemType.StoneButton; + mappings[683] = ItemType.PolishedBlackstoneButton; + mappings[684] = ItemType.OakButton; + mappings[685] = ItemType.SpruceButton; + mappings[686] = ItemType.BirchButton; + mappings[687] = ItemType.JungleButton; + mappings[688] = ItemType.AcaciaButton; + mappings[689] = ItemType.CherryButton; + mappings[690] = ItemType.DarkOakButton; + mappings[691] = ItemType.MangroveButton; + mappings[692] = ItemType.BambooButton; + mappings[693] = ItemType.CrimsonButton; + mappings[694] = ItemType.WarpedButton; + mappings[695] = ItemType.StonePressurePlate; + mappings[696] = ItemType.PolishedBlackstonePressurePlate; + mappings[697] = ItemType.LightWeightedPressurePlate; + mappings[698] = ItemType.HeavyWeightedPressurePlate; + mappings[699] = ItemType.OakPressurePlate; + mappings[700] = ItemType.SprucePressurePlate; + mappings[701] = ItemType.BirchPressurePlate; + mappings[702] = ItemType.JunglePressurePlate; + mappings[703] = ItemType.AcaciaPressurePlate; + mappings[704] = ItemType.CherryPressurePlate; + mappings[705] = ItemType.DarkOakPressurePlate; + mappings[706] = ItemType.MangrovePressurePlate; + mappings[707] = ItemType.BambooPressurePlate; + mappings[708] = ItemType.CrimsonPressurePlate; + mappings[709] = ItemType.WarpedPressurePlate; + mappings[710] = ItemType.IronDoor; + mappings[711] = ItemType.OakDoor; + mappings[712] = ItemType.SpruceDoor; + mappings[713] = ItemType.BirchDoor; + mappings[714] = ItemType.JungleDoor; + mappings[715] = ItemType.AcaciaDoor; + mappings[716] = ItemType.CherryDoor; + mappings[717] = ItemType.DarkOakDoor; + mappings[718] = ItemType.MangroveDoor; + mappings[719] = ItemType.BambooDoor; + mappings[720] = ItemType.CrimsonDoor; + mappings[721] = ItemType.WarpedDoor; + mappings[722] = ItemType.CopperDoor; + mappings[723] = ItemType.ExposedCopperDoor; + mappings[724] = ItemType.WeatheredCopperDoor; + mappings[725] = ItemType.OxidizedCopperDoor; + mappings[726] = ItemType.WaxedCopperDoor; + mappings[727] = ItemType.WaxedExposedCopperDoor; + mappings[728] = ItemType.WaxedWeatheredCopperDoor; + mappings[729] = ItemType.WaxedOxidizedCopperDoor; + mappings[730] = ItemType.IronTrapdoor; + mappings[731] = ItemType.OakTrapdoor; + mappings[732] = ItemType.SpruceTrapdoor; + mappings[733] = ItemType.BirchTrapdoor; + mappings[734] = ItemType.JungleTrapdoor; + mappings[735] = ItemType.AcaciaTrapdoor; + mappings[736] = ItemType.CherryTrapdoor; + mappings[737] = ItemType.DarkOakTrapdoor; + mappings[738] = ItemType.MangroveTrapdoor; + mappings[739] = ItemType.BambooTrapdoor; + mappings[740] = ItemType.CrimsonTrapdoor; + mappings[741] = ItemType.WarpedTrapdoor; + mappings[742] = ItemType.CopperTrapdoor; + mappings[743] = ItemType.ExposedCopperTrapdoor; + mappings[744] = ItemType.WeatheredCopperTrapdoor; + mappings[745] = ItemType.OxidizedCopperTrapdoor; + mappings[746] = ItemType.WaxedCopperTrapdoor; + mappings[747] = ItemType.WaxedExposedCopperTrapdoor; + mappings[748] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[749] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[750] = ItemType.OakFenceGate; + mappings[751] = ItemType.SpruceFenceGate; + mappings[752] = ItemType.BirchFenceGate; + mappings[753] = ItemType.JungleFenceGate; + mappings[754] = ItemType.AcaciaFenceGate; + mappings[755] = ItemType.CherryFenceGate; + mappings[756] = ItemType.DarkOakFenceGate; + mappings[757] = ItemType.MangroveFenceGate; + mappings[758] = ItemType.BambooFenceGate; + mappings[759] = ItemType.CrimsonFenceGate; + mappings[760] = ItemType.WarpedFenceGate; + mappings[761] = ItemType.PoweredRail; + mappings[762] = ItemType.DetectorRail; + mappings[763] = ItemType.Rail; + mappings[764] = ItemType.ActivatorRail; + mappings[765] = ItemType.Saddle; + mappings[766] = ItemType.Minecart; + mappings[767] = ItemType.ChestMinecart; + mappings[768] = ItemType.FurnaceMinecart; + mappings[769] = ItemType.TntMinecart; + mappings[770] = ItemType.HopperMinecart; + mappings[771] = ItemType.CarrotOnAStick; + mappings[772] = ItemType.WarpedFungusOnAStick; + mappings[773] = ItemType.Elytra; + mappings[774] = ItemType.OakBoat; + mappings[775] = ItemType.OakChestBoat; + mappings[776] = ItemType.SpruceBoat; + mappings[777] = ItemType.SpruceChestBoat; + mappings[778] = ItemType.BirchBoat; + mappings[779] = ItemType.BirchChestBoat; + mappings[780] = ItemType.JungleBoat; + mappings[781] = ItemType.JungleChestBoat; + mappings[782] = ItemType.AcaciaBoat; + mappings[783] = ItemType.AcaciaChestBoat; + mappings[784] = ItemType.CherryBoat; + mappings[785] = ItemType.CherryChestBoat; + mappings[786] = ItemType.DarkOakBoat; + mappings[787] = ItemType.DarkOakChestBoat; + mappings[788] = ItemType.MangroveBoat; + mappings[789] = ItemType.MangroveChestBoat; + mappings[790] = ItemType.BambooRaft; + mappings[791] = ItemType.BambooChestRaft; + mappings[792] = ItemType.StructureBlock; + mappings[793] = ItemType.Jigsaw; + mappings[794] = ItemType.TurtleHelmet; + mappings[795] = ItemType.TurtleScute; + mappings[796] = ItemType.ArmadilloScute; + mappings[797] = ItemType.WolfArmor; + mappings[798] = ItemType.FlintAndSteel; + mappings[799] = ItemType.Bowl; + mappings[800] = ItemType.Apple; + mappings[801] = ItemType.Bow; + mappings[802] = ItemType.Arrow; + mappings[803] = ItemType.Coal; + mappings[804] = ItemType.Charcoal; + mappings[805] = ItemType.Diamond; + mappings[806] = ItemType.Emerald; + mappings[807] = ItemType.LapisLazuli; + mappings[808] = ItemType.Quartz; + mappings[809] = ItemType.AmethystShard; + mappings[810] = ItemType.RawIron; + mappings[811] = ItemType.IronIngot; + mappings[812] = ItemType.RawCopper; + mappings[813] = ItemType.CopperIngot; + mappings[814] = ItemType.RawGold; + mappings[815] = ItemType.GoldIngot; + mappings[816] = ItemType.NetheriteIngot; + mappings[817] = ItemType.NetheriteScrap; + mappings[818] = ItemType.WoodenSword; + mappings[819] = ItemType.WoodenShovel; + mappings[820] = ItemType.WoodenPickaxe; + mappings[821] = ItemType.WoodenAxe; + mappings[822] = ItemType.WoodenHoe; + mappings[823] = ItemType.StoneSword; + mappings[824] = ItemType.StoneShovel; + mappings[825] = ItemType.StonePickaxe; + mappings[826] = ItemType.StoneAxe; + mappings[827] = ItemType.StoneHoe; + mappings[828] = ItemType.GoldenSword; + mappings[829] = ItemType.GoldenShovel; + mappings[830] = ItemType.GoldenPickaxe; + mappings[831] = ItemType.GoldenAxe; + mappings[832] = ItemType.GoldenHoe; + mappings[833] = ItemType.IronSword; + mappings[834] = ItemType.IronShovel; + mappings[835] = ItemType.IronPickaxe; + mappings[836] = ItemType.IronAxe; + mappings[837] = ItemType.IronHoe; + mappings[838] = ItemType.DiamondSword; + mappings[839] = ItemType.DiamondShovel; + mappings[840] = ItemType.DiamondPickaxe; + mappings[841] = ItemType.DiamondAxe; + mappings[842] = ItemType.DiamondHoe; + mappings[843] = ItemType.NetheriteSword; + mappings[844] = ItemType.NetheriteShovel; + mappings[845] = ItemType.NetheritePickaxe; + mappings[846] = ItemType.NetheriteAxe; + mappings[847] = ItemType.NetheriteHoe; + mappings[848] = ItemType.Stick; + mappings[849] = ItemType.MushroomStew; + mappings[850] = ItemType.String; + mappings[851] = ItemType.Feather; + mappings[852] = ItemType.Gunpowder; + mappings[853] = ItemType.WheatSeeds; + mappings[854] = ItemType.Wheat; + mappings[855] = ItemType.Bread; + mappings[856] = ItemType.LeatherHelmet; + mappings[857] = ItemType.LeatherChestplate; + mappings[858] = ItemType.LeatherLeggings; + mappings[859] = ItemType.LeatherBoots; + mappings[860] = ItemType.ChainmailHelmet; + mappings[861] = ItemType.ChainmailChestplate; + mappings[862] = ItemType.ChainmailLeggings; + mappings[863] = ItemType.ChainmailBoots; + mappings[864] = ItemType.IronHelmet; + mappings[865] = ItemType.IronChestplate; + mappings[866] = ItemType.IronLeggings; + mappings[867] = ItemType.IronBoots; + mappings[868] = ItemType.DiamondHelmet; + mappings[869] = ItemType.DiamondChestplate; + mappings[870] = ItemType.DiamondLeggings; + mappings[871] = ItemType.DiamondBoots; + mappings[872] = ItemType.GoldenHelmet; + mappings[873] = ItemType.GoldenChestplate; + mappings[874] = ItemType.GoldenLeggings; + mappings[875] = ItemType.GoldenBoots; + mappings[876] = ItemType.NetheriteHelmet; + mappings[877] = ItemType.NetheriteChestplate; + mappings[878] = ItemType.NetheriteLeggings; + mappings[879] = ItemType.NetheriteBoots; + mappings[880] = ItemType.Flint; + mappings[881] = ItemType.Porkchop; + mappings[882] = ItemType.CookedPorkchop; + mappings[883] = ItemType.Painting; + mappings[884] = ItemType.GoldenApple; + mappings[885] = ItemType.EnchantedGoldenApple; + mappings[886] = ItemType.OakSign; + mappings[887] = ItemType.SpruceSign; + mappings[888] = ItemType.BirchSign; + mappings[889] = ItemType.JungleSign; + mappings[890] = ItemType.AcaciaSign; + mappings[891] = ItemType.CherrySign; + mappings[892] = ItemType.DarkOakSign; + mappings[893] = ItemType.MangroveSign; + mappings[894] = ItemType.BambooSign; + mappings[895] = ItemType.CrimsonSign; + mappings[896] = ItemType.WarpedSign; + mappings[897] = ItemType.OakHangingSign; + mappings[898] = ItemType.SpruceHangingSign; + mappings[899] = ItemType.BirchHangingSign; + mappings[900] = ItemType.JungleHangingSign; + mappings[901] = ItemType.AcaciaHangingSign; + mappings[902] = ItemType.CherryHangingSign; + mappings[903] = ItemType.DarkOakHangingSign; + mappings[904] = ItemType.MangroveHangingSign; + mappings[905] = ItemType.BambooHangingSign; + mappings[906] = ItemType.CrimsonHangingSign; + mappings[907] = ItemType.WarpedHangingSign; + mappings[908] = ItemType.Bucket; + mappings[909] = ItemType.WaterBucket; + mappings[910] = ItemType.LavaBucket; + mappings[911] = ItemType.PowderSnowBucket; + mappings[912] = ItemType.Snowball; + mappings[913] = ItemType.Leather; + mappings[914] = ItemType.MilkBucket; + mappings[915] = ItemType.PufferfishBucket; + mappings[916] = ItemType.SalmonBucket; + mappings[917] = ItemType.CodBucket; + mappings[918] = ItemType.TropicalFishBucket; + mappings[919] = ItemType.AxolotlBucket; + mappings[920] = ItemType.TadpoleBucket; + mappings[921] = ItemType.Brick; + mappings[922] = ItemType.ClayBall; + mappings[923] = ItemType.DriedKelpBlock; + mappings[924] = ItemType.Paper; + mappings[925] = ItemType.Book; + mappings[926] = ItemType.SlimeBall; + mappings[927] = ItemType.Egg; + mappings[928] = ItemType.Compass; + mappings[929] = ItemType.RecoveryCompass; + mappings[930] = ItemType.Bundle; + mappings[931] = ItemType.FishingRod; + mappings[932] = ItemType.Clock; + mappings[933] = ItemType.Spyglass; + mappings[934] = ItemType.GlowstoneDust; + mappings[935] = ItemType.Cod; + mappings[936] = ItemType.Salmon; + mappings[937] = ItemType.TropicalFish; + mappings[938] = ItemType.Pufferfish; + mappings[939] = ItemType.CookedCod; + mappings[940] = ItemType.CookedSalmon; + mappings[941] = ItemType.InkSac; + mappings[942] = ItemType.GlowInkSac; + mappings[943] = ItemType.CocoaBeans; + mappings[944] = ItemType.WhiteDye; + mappings[945] = ItemType.OrangeDye; + mappings[946] = ItemType.MagentaDye; + mappings[947] = ItemType.LightBlueDye; + mappings[948] = ItemType.YellowDye; + mappings[949] = ItemType.LimeDye; + mappings[950] = ItemType.PinkDye; + mappings[951] = ItemType.GrayDye; + mappings[952] = ItemType.LightGrayDye; + mappings[953] = ItemType.CyanDye; + mappings[954] = ItemType.PurpleDye; + mappings[955] = ItemType.BlueDye; + mappings[956] = ItemType.BrownDye; + mappings[957] = ItemType.GreenDye; + mappings[958] = ItemType.RedDye; + mappings[959] = ItemType.BlackDye; + mappings[960] = ItemType.BoneMeal; + mappings[961] = ItemType.Bone; + mappings[962] = ItemType.Sugar; + mappings[963] = ItemType.Cake; + mappings[964] = ItemType.WhiteBed; + mappings[965] = ItemType.OrangeBed; + mappings[966] = ItemType.MagentaBed; + mappings[967] = ItemType.LightBlueBed; + mappings[968] = ItemType.YellowBed; + mappings[969] = ItemType.LimeBed; + mappings[970] = ItemType.PinkBed; + mappings[971] = ItemType.GrayBed; + mappings[972] = ItemType.LightGrayBed; + mappings[973] = ItemType.CyanBed; + mappings[974] = ItemType.PurpleBed; + mappings[975] = ItemType.BlueBed; + mappings[976] = ItemType.BrownBed; + mappings[977] = ItemType.GreenBed; + mappings[978] = ItemType.RedBed; + mappings[979] = ItemType.BlackBed; + mappings[980] = ItemType.Cookie; + mappings[981] = ItemType.Crafter; + mappings[982] = ItemType.FilledMap; + mappings[983] = ItemType.Shears; + mappings[984] = ItemType.MelonSlice; + mappings[985] = ItemType.DriedKelp; + mappings[986] = ItemType.PumpkinSeeds; + mappings[987] = ItemType.MelonSeeds; + mappings[988] = ItemType.Beef; + mappings[989] = ItemType.CookedBeef; + mappings[990] = ItemType.Chicken; + mappings[991] = ItemType.CookedChicken; + mappings[992] = ItemType.RottenFlesh; + mappings[993] = ItemType.EnderPearl; + mappings[994] = ItemType.BlazeRod; + mappings[995] = ItemType.GhastTear; + mappings[996] = ItemType.GoldNugget; + mappings[997] = ItemType.NetherWart; + mappings[998] = ItemType.Potion; + mappings[999] = ItemType.GlassBottle; + mappings[1000] = ItemType.SpiderEye; + mappings[1001] = ItemType.FermentedSpiderEye; + mappings[1002] = ItemType.BlazePowder; + mappings[1003] = ItemType.MagmaCream; + mappings[1004] = ItemType.BrewingStand; + mappings[1005] = ItemType.Cauldron; + mappings[1006] = ItemType.EnderEye; + mappings[1007] = ItemType.GlisteringMelonSlice; + mappings[1008] = ItemType.ArmadilloSpawnEgg; + mappings[1009] = ItemType.AllaySpawnEgg; + mappings[1010] = ItemType.AxolotlSpawnEgg; + mappings[1011] = ItemType.BatSpawnEgg; + mappings[1012] = ItemType.BeeSpawnEgg; + mappings[1013] = ItemType.BlazeSpawnEgg; + mappings[1014] = ItemType.BoggedSpawnEgg; + mappings[1015] = ItemType.BreezeSpawnEgg; + mappings[1016] = ItemType.CatSpawnEgg; + mappings[1017] = ItemType.CamelSpawnEgg; + mappings[1018] = ItemType.CaveSpiderSpawnEgg; + mappings[1019] = ItemType.ChickenSpawnEgg; + mappings[1020] = ItemType.CodSpawnEgg; + mappings[1021] = ItemType.CowSpawnEgg; + mappings[1022] = ItemType.CreeperSpawnEgg; + mappings[1023] = ItemType.DolphinSpawnEgg; + mappings[1024] = ItemType.DonkeySpawnEgg; + mappings[1025] = ItemType.DrownedSpawnEgg; + mappings[1026] = ItemType.ElderGuardianSpawnEgg; + mappings[1027] = ItemType.EnderDragonSpawnEgg; + mappings[1028] = ItemType.EndermanSpawnEgg; + mappings[1029] = ItemType.EndermiteSpawnEgg; + mappings[1030] = ItemType.EvokerSpawnEgg; + mappings[1031] = ItemType.FoxSpawnEgg; + mappings[1032] = ItemType.FrogSpawnEgg; + mappings[1033] = ItemType.GhastSpawnEgg; + mappings[1034] = ItemType.GlowSquidSpawnEgg; + mappings[1035] = ItemType.GoatSpawnEgg; + mappings[1036] = ItemType.GuardianSpawnEgg; + mappings[1037] = ItemType.HoglinSpawnEgg; + mappings[1038] = ItemType.HorseSpawnEgg; + mappings[1039] = ItemType.HuskSpawnEgg; + mappings[1040] = ItemType.IronGolemSpawnEgg; + mappings[1041] = ItemType.LlamaSpawnEgg; + mappings[1042] = ItemType.MagmaCubeSpawnEgg; + mappings[1043] = ItemType.MooshroomSpawnEgg; + mappings[1044] = ItemType.MuleSpawnEgg; + mappings[1045] = ItemType.OcelotSpawnEgg; + mappings[1046] = ItemType.PandaSpawnEgg; + mappings[1047] = ItemType.ParrotSpawnEgg; + mappings[1048] = ItemType.PhantomSpawnEgg; + mappings[1049] = ItemType.PigSpawnEgg; + mappings[1050] = ItemType.PiglinSpawnEgg; + mappings[1051] = ItemType.PiglinBruteSpawnEgg; + mappings[1052] = ItemType.PillagerSpawnEgg; + mappings[1053] = ItemType.PolarBearSpawnEgg; + mappings[1054] = ItemType.PufferfishSpawnEgg; + mappings[1055] = ItemType.RabbitSpawnEgg; + mappings[1056] = ItemType.RavagerSpawnEgg; + mappings[1057] = ItemType.SalmonSpawnEgg; + mappings[1058] = ItemType.SheepSpawnEgg; + mappings[1059] = ItemType.ShulkerSpawnEgg; + mappings[1060] = ItemType.SilverfishSpawnEgg; + mappings[1061] = ItemType.SkeletonSpawnEgg; + mappings[1062] = ItemType.SkeletonHorseSpawnEgg; + mappings[1063] = ItemType.SlimeSpawnEgg; + mappings[1064] = ItemType.SnifferSpawnEgg; + mappings[1065] = ItemType.SnowGolemSpawnEgg; + mappings[1066] = ItemType.SpiderSpawnEgg; + mappings[1067] = ItemType.SquidSpawnEgg; + mappings[1068] = ItemType.StraySpawnEgg; + mappings[1069] = ItemType.StriderSpawnEgg; + mappings[1070] = ItemType.TadpoleSpawnEgg; + mappings[1071] = ItemType.TraderLlamaSpawnEgg; + mappings[1072] = ItemType.TropicalFishSpawnEgg; + mappings[1073] = ItemType.TurtleSpawnEgg; + mappings[1074] = ItemType.VexSpawnEgg; + mappings[1075] = ItemType.VillagerSpawnEgg; + mappings[1076] = ItemType.VindicatorSpawnEgg; + mappings[1077] = ItemType.WanderingTraderSpawnEgg; + mappings[1078] = ItemType.WardenSpawnEgg; + mappings[1079] = ItemType.WitchSpawnEgg; + mappings[1080] = ItemType.WitherSpawnEgg; + mappings[1081] = ItemType.WitherSkeletonSpawnEgg; + mappings[1082] = ItemType.WolfSpawnEgg; + mappings[1083] = ItemType.ZoglinSpawnEgg; + mappings[1084] = ItemType.ZombieSpawnEgg; + mappings[1085] = ItemType.ZombieHorseSpawnEgg; + mappings[1086] = ItemType.ZombieVillagerSpawnEgg; + mappings[1087] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1088] = ItemType.ExperienceBottle; + mappings[1089] = ItemType.FireCharge; + mappings[1090] = ItemType.WindCharge; + mappings[1091] = ItemType.WritableBook; + mappings[1092] = ItemType.WrittenBook; + mappings[1093] = ItemType.Mace; + mappings[1094] = ItemType.ItemFrame; + mappings[1095] = ItemType.GlowItemFrame; + mappings[1096] = ItemType.FlowerPot; + mappings[1097] = ItemType.Carrot; + mappings[1098] = ItemType.Potato; + mappings[1099] = ItemType.BakedPotato; + mappings[1100] = ItemType.PoisonousPotato; + mappings[1101] = ItemType.Map; + mappings[1102] = ItemType.GoldenCarrot; + mappings[1103] = ItemType.SkeletonSkull; + mappings[1104] = ItemType.WitherSkeletonSkull; + mappings[1105] = ItemType.PlayerHead; + mappings[1106] = ItemType.ZombieHead; + mappings[1107] = ItemType.CreeperHead; + mappings[1108] = ItemType.DragonHead; + mappings[1109] = ItemType.PiglinHead; + mappings[1110] = ItemType.NetherStar; + mappings[1111] = ItemType.PumpkinPie; + mappings[1112] = ItemType.FireworkRocket; + mappings[1113] = ItemType.FireworkStar; + mappings[1114] = ItemType.EnchantedBook; + mappings[1115] = ItemType.NetherBrick; + mappings[1116] = ItemType.PrismarineShard; + mappings[1117] = ItemType.PrismarineCrystals; + mappings[1118] = ItemType.Rabbit; + mappings[1119] = ItemType.CookedRabbit; + mappings[1120] = ItemType.RabbitStew; + mappings[1121] = ItemType.RabbitFoot; + mappings[1122] = ItemType.RabbitHide; + mappings[1123] = ItemType.ArmorStand; + mappings[1124] = ItemType.IronHorseArmor; + mappings[1125] = ItemType.GoldenHorseArmor; + mappings[1126] = ItemType.DiamondHorseArmor; + mappings[1127] = ItemType.LeatherHorseArmor; + mappings[1128] = ItemType.Lead; + mappings[1129] = ItemType.NameTag; + mappings[1130] = ItemType.CommandBlockMinecart; + mappings[1131] = ItemType.Mutton; + mappings[1132] = ItemType.CookedMutton; + mappings[1133] = ItemType.WhiteBanner; + mappings[1134] = ItemType.OrangeBanner; + mappings[1135] = ItemType.MagentaBanner; + mappings[1136] = ItemType.LightBlueBanner; + mappings[1137] = ItemType.YellowBanner; + mappings[1138] = ItemType.LimeBanner; + mappings[1139] = ItemType.PinkBanner; + mappings[1140] = ItemType.GrayBanner; + mappings[1141] = ItemType.LightGrayBanner; + mappings[1142] = ItemType.CyanBanner; + mappings[1143] = ItemType.PurpleBanner; + mappings[1144] = ItemType.BlueBanner; + mappings[1145] = ItemType.BrownBanner; + mappings[1146] = ItemType.GreenBanner; + mappings[1147] = ItemType.RedBanner; + mappings[1148] = ItemType.BlackBanner; + mappings[1149] = ItemType.EndCrystal; + mappings[1150] = ItemType.ChorusFruit; + mappings[1151] = ItemType.PoppedChorusFruit; + mappings[1152] = ItemType.TorchflowerSeeds; + mappings[1153] = ItemType.PitcherPod; + mappings[1154] = ItemType.Beetroot; + mappings[1155] = ItemType.BeetrootSeeds; + mappings[1156] = ItemType.BeetrootSoup; + mappings[1157] = ItemType.DragonBreath; + mappings[1158] = ItemType.SplashPotion; + mappings[1159] = ItemType.SpectralArrow; + mappings[1160] = ItemType.TippedArrow; + mappings[1161] = ItemType.LingeringPotion; + mappings[1162] = ItemType.Shield; + mappings[1163] = ItemType.TotemOfUndying; + mappings[1164] = ItemType.ShulkerShell; + mappings[1165] = ItemType.IronNugget; + mappings[1166] = ItemType.KnowledgeBook; + mappings[1167] = ItemType.DebugStick; + mappings[1168] = ItemType.MusicDisc13; + mappings[1169] = ItemType.MusicDiscCat; + mappings[1170] = ItemType.MusicDiscBlocks; + mappings[1171] = ItemType.MusicDiscChirp; + mappings[1172] = ItemType.MusicDiscCreator; + mappings[1173] = ItemType.MusicDiscCreatorMusicBox; + mappings[1174] = ItemType.MusicDiscFar; + mappings[1175] = ItemType.MusicDiscMall; + mappings[1176] = ItemType.MusicDiscMellohi; + mappings[1177] = ItemType.MusicDiscStal; + mappings[1178] = ItemType.MusicDiscStrad; + mappings[1179] = ItemType.MusicDiscWard; + mappings[1180] = ItemType.MusicDisc11; + mappings[1181] = ItemType.MusicDiscWait; + mappings[1182] = ItemType.MusicDiscOtherside; + mappings[1183] = ItemType.MusicDiscRelic; + mappings[1184] = ItemType.MusicDisc5; + mappings[1185] = ItemType.MusicDiscPigstep; + mappings[1186] = ItemType.MusicDiscPrecipice; + mappings[1187] = ItemType.DiscFragment5; + mappings[1188] = ItemType.Trident; + mappings[1189] = ItemType.PhantomMembrane; + mappings[1190] = ItemType.NautilusShell; + mappings[1191] = ItemType.HeartOfTheSea; + mappings[1192] = ItemType.Crossbow; + mappings[1193] = ItemType.SuspiciousStew; + mappings[1194] = ItemType.Loom; + mappings[1195] = ItemType.FlowerBannerPattern; + mappings[1196] = ItemType.CreeperBannerPattern; + mappings[1197] = ItemType.SkullBannerPattern; + mappings[1198] = ItemType.MojangBannerPattern; + mappings[1199] = ItemType.GlobeBannerPattern; + mappings[1200] = ItemType.PiglinBannerPattern; + mappings[1201] = ItemType.FlowBannerPattern; + mappings[1202] = ItemType.GusterBannerPattern; + mappings[1203] = ItemType.GoatHorn; + mappings[1204] = ItemType.Composter; + mappings[1205] = ItemType.Barrel; + mappings[1206] = ItemType.Smoker; + mappings[1207] = ItemType.BlastFurnace; + mappings[1208] = ItemType.CartographyTable; + mappings[1209] = ItemType.FletchingTable; + mappings[1210] = ItemType.Grindstone; + mappings[1211] = ItemType.SmithingTable; + mappings[1212] = ItemType.Stonecutter; + mappings[1213] = ItemType.Bell; + mappings[1214] = ItemType.Lantern; + mappings[1215] = ItemType.SoulLantern; + mappings[1216] = ItemType.SweetBerries; + mappings[1217] = ItemType.GlowBerries; + mappings[1218] = ItemType.Campfire; + mappings[1219] = ItemType.SoulCampfire; + mappings[1220] = ItemType.Shroomlight; + mappings[1221] = ItemType.Honeycomb; + mappings[1222] = ItemType.BeeNest; + mappings[1223] = ItemType.Beehive; + mappings[1224] = ItemType.HoneyBottle; + mappings[1225] = ItemType.HoneycombBlock; + mappings[1226] = ItemType.Lodestone; + mappings[1227] = ItemType.CryingObsidian; + mappings[1228] = ItemType.Blackstone; + mappings[1229] = ItemType.BlackstoneSlab; + mappings[1230] = ItemType.BlackstoneStairs; + mappings[1231] = ItemType.GildedBlackstone; + mappings[1232] = ItemType.PolishedBlackstone; + mappings[1233] = ItemType.PolishedBlackstoneSlab; + mappings[1234] = ItemType.PolishedBlackstoneStairs; + mappings[1235] = ItemType.ChiseledPolishedBlackstone; + mappings[1236] = ItemType.PolishedBlackstoneBricks; + mappings[1237] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1238] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1239] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1240] = ItemType.RespawnAnchor; + mappings[1241] = ItemType.Candle; + mappings[1242] = ItemType.WhiteCandle; + mappings[1243] = ItemType.OrangeCandle; + mappings[1244] = ItemType.MagentaCandle; + mappings[1245] = ItemType.LightBlueCandle; + mappings[1246] = ItemType.YellowCandle; + mappings[1247] = ItemType.LimeCandle; + mappings[1248] = ItemType.PinkCandle; + mappings[1249] = ItemType.GrayCandle; + mappings[1250] = ItemType.LightGrayCandle; + mappings[1251] = ItemType.CyanCandle; + mappings[1252] = ItemType.PurpleCandle; + mappings[1253] = ItemType.BlueCandle; + mappings[1254] = ItemType.BrownCandle; + mappings[1255] = ItemType.GreenCandle; + mappings[1256] = ItemType.RedCandle; + mappings[1257] = ItemType.BlackCandle; + mappings[1258] = ItemType.SmallAmethystBud; + mappings[1259] = ItemType.MediumAmethystBud; + mappings[1260] = ItemType.LargeAmethystBud; + mappings[1261] = ItemType.AmethystCluster; + mappings[1262] = ItemType.PointedDripstone; + mappings[1263] = ItemType.OchreFroglight; + mappings[1264] = ItemType.VerdantFroglight; + mappings[1265] = ItemType.PearlescentFroglight; + mappings[1266] = ItemType.Frogspawn; + mappings[1267] = ItemType.EchoShard; + mappings[1268] = ItemType.Brush; + mappings[1269] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1270] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1271] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1272] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1273] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1274] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1275] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1276] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1277] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1278] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1279] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1280] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1281] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1282] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1283] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1284] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1285] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1286] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1287] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1288] = ItemType.AnglerPotterySherd; + mappings[1289] = ItemType.ArcherPotterySherd; + mappings[1290] = ItemType.ArmsUpPotterySherd; + mappings[1291] = ItemType.BladePotterySherd; + mappings[1292] = ItemType.BrewerPotterySherd; + mappings[1293] = ItemType.BurnPotterySherd; + mappings[1294] = ItemType.DangerPotterySherd; + mappings[1295] = ItemType.ExplorerPotterySherd; + mappings[1296] = ItemType.FlowPotterySherd; + mappings[1297] = ItemType.FriendPotterySherd; + mappings[1298] = ItemType.GusterPotterySherd; + mappings[1299] = ItemType.HeartPotterySherd; + mappings[1300] = ItemType.HeartbreakPotterySherd; + mappings[1301] = ItemType.HowlPotterySherd; + mappings[1302] = ItemType.MinerPotterySherd; + mappings[1303] = ItemType.MournerPotterySherd; + mappings[1304] = ItemType.PlentyPotterySherd; + mappings[1305] = ItemType.PrizePotterySherd; + mappings[1306] = ItemType.ScrapePotterySherd; + mappings[1307] = ItemType.SheafPotterySherd; + mappings[1308] = ItemType.ShelterPotterySherd; + mappings[1309] = ItemType.SkullPotterySherd; + mappings[1310] = ItemType.SnortPotterySherd; + mappings[1311] = ItemType.CopperGrate; + mappings[1312] = ItemType.ExposedCopperGrate; + mappings[1313] = ItemType.WeatheredCopperGrate; + mappings[1314] = ItemType.OxidizedCopperGrate; + mappings[1315] = ItemType.WaxedCopperGrate; + mappings[1316] = ItemType.WaxedExposedCopperGrate; + mappings[1317] = ItemType.WaxedWeatheredCopperGrate; + mappings[1318] = ItemType.WaxedOxidizedCopperGrate; + mappings[1319] = ItemType.CopperBulb; + mappings[1320] = ItemType.ExposedCopperBulb; + mappings[1321] = ItemType.WeatheredCopperBulb; + mappings[1322] = ItemType.OxidizedCopperBulb; + mappings[1323] = ItemType.WaxedCopperBulb; + mappings[1324] = ItemType.WaxedExposedCopperBulb; + mappings[1325] = ItemType.WaxedWeatheredCopperBulb; + mappings[1326] = ItemType.WaxedOxidizedCopperBulb; + mappings[1327] = ItemType.TrialSpawner; + mappings[1328] = ItemType.TrialKey; + mappings[1329] = ItemType.OminousTrialKey; + mappings[1330] = ItemType.Vault; + mappings[1331] = ItemType.OminousBottle; + mappings[1332] = ItemType.BreezeRod; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette12111.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette12111.cs new file mode 100644 index 00000000..30de90e8 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette12111.cs @@ -0,0 +1,1523 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette12111 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette12111() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.ShortDryGrass; + mappings[210] = ItemType.TallDryGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.OpenEyeblossom; + mappings[231] = ItemType.ClosedEyeblossom; + mappings[232] = ItemType.Poppy; + mappings[233] = ItemType.BlueOrchid; + mappings[234] = ItemType.Allium; + mappings[235] = ItemType.AzureBluet; + mappings[236] = ItemType.RedTulip; + mappings[237] = ItemType.OrangeTulip; + mappings[238] = ItemType.WhiteTulip; + mappings[239] = ItemType.PinkTulip; + mappings[240] = ItemType.OxeyeDaisy; + mappings[241] = ItemType.Cornflower; + mappings[242] = ItemType.LilyOfTheValley; + mappings[243] = ItemType.WitherRose; + mappings[244] = ItemType.Torchflower; + mappings[245] = ItemType.PitcherPlant; + mappings[246] = ItemType.SporeBlossom; + mappings[247] = ItemType.BrownMushroom; + mappings[248] = ItemType.RedMushroom; + mappings[249] = ItemType.CrimsonFungus; + mappings[250] = ItemType.WarpedFungus; + mappings[251] = ItemType.CrimsonRoots; + mappings[252] = ItemType.WarpedRoots; + mappings[253] = ItemType.NetherSprouts; + mappings[254] = ItemType.WeepingVines; + mappings[255] = ItemType.TwistingVines; + mappings[256] = ItemType.SugarCane; + mappings[257] = ItemType.Kelp; + mappings[258] = ItemType.PinkPetals; + mappings[259] = ItemType.Wildflowers; + mappings[260] = ItemType.LeafLitter; + mappings[261] = ItemType.MossCarpet; + mappings[262] = ItemType.MossBlock; + mappings[263] = ItemType.PaleMossCarpet; + mappings[264] = ItemType.PaleHangingMoss; + mappings[265] = ItemType.PaleMossBlock; + mappings[266] = ItemType.HangingRoots; + mappings[267] = ItemType.BigDripleaf; + mappings[268] = ItemType.SmallDripleaf; + mappings[269] = ItemType.Bamboo; + mappings[270] = ItemType.OakSlab; + mappings[271] = ItemType.SpruceSlab; + mappings[272] = ItemType.BirchSlab; + mappings[273] = ItemType.JungleSlab; + mappings[274] = ItemType.AcaciaSlab; + mappings[275] = ItemType.CherrySlab; + mappings[276] = ItemType.DarkOakSlab; + mappings[277] = ItemType.PaleOakSlab; + mappings[278] = ItemType.MangroveSlab; + mappings[279] = ItemType.BambooSlab; + mappings[280] = ItemType.BambooMosaicSlab; + mappings[281] = ItemType.CrimsonSlab; + mappings[282] = ItemType.WarpedSlab; + mappings[283] = ItemType.StoneSlab; + mappings[284] = ItemType.SmoothStoneSlab; + mappings[285] = ItemType.SandstoneSlab; + mappings[286] = ItemType.CutSandstoneSlab; + mappings[287] = ItemType.PetrifiedOakSlab; + mappings[288] = ItemType.CobblestoneSlab; + mappings[289] = ItemType.BrickSlab; + mappings[290] = ItemType.StoneBrickSlab; + mappings[291] = ItemType.MudBrickSlab; + mappings[292] = ItemType.NetherBrickSlab; + mappings[293] = ItemType.QuartzSlab; + mappings[294] = ItemType.RedSandstoneSlab; + mappings[295] = ItemType.CutRedSandstoneSlab; + mappings[296] = ItemType.PurpurSlab; + mappings[297] = ItemType.PrismarineSlab; + mappings[298] = ItemType.PrismarineBrickSlab; + mappings[299] = ItemType.DarkPrismarineSlab; + mappings[300] = ItemType.SmoothQuartz; + mappings[301] = ItemType.SmoothRedSandstone; + mappings[302] = ItemType.SmoothSandstone; + mappings[303] = ItemType.SmoothStone; + mappings[304] = ItemType.Bricks; + mappings[305] = ItemType.AcaciaShelf; + mappings[306] = ItemType.BambooShelf; + mappings[307] = ItemType.BirchShelf; + mappings[308] = ItemType.CherryShelf; + mappings[309] = ItemType.CrimsonShelf; + mappings[310] = ItemType.DarkOakShelf; + mappings[311] = ItemType.JungleShelf; + mappings[312] = ItemType.MangroveShelf; + mappings[313] = ItemType.OakShelf; + mappings[314] = ItemType.PaleOakShelf; + mappings[315] = ItemType.SpruceShelf; + mappings[316] = ItemType.WarpedShelf; + mappings[317] = ItemType.Bookshelf; + mappings[318] = ItemType.ChiseledBookshelf; + mappings[319] = ItemType.DecoratedPot; + mappings[320] = ItemType.MossyCobblestone; + mappings[321] = ItemType.Obsidian; + mappings[322] = ItemType.Torch; + mappings[323] = ItemType.EndRod; + mappings[324] = ItemType.ChorusPlant; + mappings[325] = ItemType.ChorusFlower; + mappings[326] = ItemType.PurpurBlock; + mappings[327] = ItemType.PurpurPillar; + mappings[328] = ItemType.PurpurStairs; + mappings[329] = ItemType.Spawner; + mappings[330] = ItemType.CreakingHeart; + mappings[331] = ItemType.Chest; + mappings[332] = ItemType.CraftingTable; + mappings[333] = ItemType.Farmland; + mappings[334] = ItemType.Furnace; + mappings[335] = ItemType.Ladder; + mappings[336] = ItemType.CobblestoneStairs; + mappings[337] = ItemType.Snow; + mappings[338] = ItemType.Ice; + mappings[339] = ItemType.SnowBlock; + mappings[340] = ItemType.Cactus; + mappings[341] = ItemType.CactusFlower; + mappings[342] = ItemType.Clay; + mappings[343] = ItemType.Jukebox; + mappings[344] = ItemType.OakFence; + mappings[345] = ItemType.SpruceFence; + mappings[346] = ItemType.BirchFence; + mappings[347] = ItemType.JungleFence; + mappings[348] = ItemType.AcaciaFence; + mappings[349] = ItemType.CherryFence; + mappings[350] = ItemType.DarkOakFence; + mappings[351] = ItemType.PaleOakFence; + mappings[352] = ItemType.MangroveFence; + mappings[353] = ItemType.BambooFence; + mappings[354] = ItemType.CrimsonFence; + mappings[355] = ItemType.WarpedFence; + mappings[356] = ItemType.Pumpkin; + mappings[357] = ItemType.CarvedPumpkin; + mappings[358] = ItemType.JackOLantern; + mappings[359] = ItemType.Netherrack; + mappings[360] = ItemType.SoulSand; + mappings[361] = ItemType.SoulSoil; + mappings[362] = ItemType.Basalt; + mappings[363] = ItemType.PolishedBasalt; + mappings[364] = ItemType.SmoothBasalt; + mappings[365] = ItemType.SoulTorch; + mappings[366] = ItemType.CopperTorch; + mappings[367] = ItemType.Glowstone; + mappings[368] = ItemType.InfestedStone; + mappings[369] = ItemType.InfestedCobblestone; + mappings[370] = ItemType.InfestedStoneBricks; + mappings[371] = ItemType.InfestedMossyStoneBricks; + mappings[372] = ItemType.InfestedCrackedStoneBricks; + mappings[373] = ItemType.InfestedChiseledStoneBricks; + mappings[374] = ItemType.InfestedDeepslate; + mappings[375] = ItemType.StoneBricks; + mappings[376] = ItemType.MossyStoneBricks; + mappings[377] = ItemType.CrackedStoneBricks; + mappings[378] = ItemType.ChiseledStoneBricks; + mappings[379] = ItemType.PackedMud; + mappings[380] = ItemType.MudBricks; + mappings[381] = ItemType.DeepslateBricks; + mappings[382] = ItemType.CrackedDeepslateBricks; + mappings[383] = ItemType.DeepslateTiles; + mappings[384] = ItemType.CrackedDeepslateTiles; + mappings[385] = ItemType.ChiseledDeepslate; + mappings[386] = ItemType.ReinforcedDeepslate; + mappings[387] = ItemType.BrownMushroomBlock; + mappings[388] = ItemType.RedMushroomBlock; + mappings[389] = ItemType.MushroomStem; + mappings[390] = ItemType.IronBars; + mappings[391] = ItemType.CopperBars; + mappings[392] = ItemType.ExposedCopperBars; + mappings[393] = ItemType.WeatheredCopperBars; + mappings[394] = ItemType.OxidizedCopperBars; + mappings[395] = ItemType.WaxedCopperBars; + mappings[396] = ItemType.WaxedExposedCopperBars; + mappings[397] = ItemType.WaxedWeatheredCopperBars; + mappings[398] = ItemType.WaxedOxidizedCopperBars; + mappings[399] = ItemType.IronChain; + mappings[400] = ItemType.CopperChain; + mappings[401] = ItemType.ExposedCopperChain; + mappings[402] = ItemType.WeatheredCopperChain; + mappings[403] = ItemType.OxidizedCopperChain; + mappings[404] = ItemType.WaxedCopperChain; + mappings[405] = ItemType.WaxedExposedCopperChain; + mappings[406] = ItemType.WaxedWeatheredCopperChain; + mappings[407] = ItemType.WaxedOxidizedCopperChain; + mappings[408] = ItemType.GlassPane; + mappings[409] = ItemType.Melon; + mappings[410] = ItemType.Vine; + mappings[411] = ItemType.GlowLichen; + mappings[412] = ItemType.ResinClump; + mappings[413] = ItemType.ResinBlock; + mappings[414] = ItemType.ResinBricks; + mappings[415] = ItemType.ResinBrickStairs; + mappings[416] = ItemType.ResinBrickSlab; + mappings[417] = ItemType.ResinBrickWall; + mappings[418] = ItemType.ChiseledResinBricks; + mappings[419] = ItemType.BrickStairs; + mappings[420] = ItemType.StoneBrickStairs; + mappings[421] = ItemType.MudBrickStairs; + mappings[422] = ItemType.Mycelium; + mappings[423] = ItemType.LilyPad; + mappings[424] = ItemType.NetherBricks; + mappings[425] = ItemType.CrackedNetherBricks; + mappings[426] = ItemType.ChiseledNetherBricks; + mappings[427] = ItemType.NetherBrickFence; + mappings[428] = ItemType.NetherBrickStairs; + mappings[429] = ItemType.Sculk; + mappings[430] = ItemType.SculkVein; + mappings[431] = ItemType.SculkCatalyst; + mappings[432] = ItemType.SculkShrieker; + mappings[433] = ItemType.EnchantingTable; + mappings[434] = ItemType.EndPortalFrame; + mappings[435] = ItemType.EndStone; + mappings[436] = ItemType.EndStoneBricks; + mappings[437] = ItemType.DragonEgg; + mappings[438] = ItemType.SandstoneStairs; + mappings[439] = ItemType.EnderChest; + mappings[440] = ItemType.EmeraldBlock; + mappings[441] = ItemType.OakStairs; + mappings[442] = ItemType.SpruceStairs; + mappings[443] = ItemType.BirchStairs; + mappings[444] = ItemType.JungleStairs; + mappings[445] = ItemType.AcaciaStairs; + mappings[446] = ItemType.CherryStairs; + mappings[447] = ItemType.DarkOakStairs; + mappings[448] = ItemType.PaleOakStairs; + mappings[449] = ItemType.MangroveStairs; + mappings[450] = ItemType.BambooStairs; + mappings[451] = ItemType.BambooMosaicStairs; + mappings[452] = ItemType.CrimsonStairs; + mappings[453] = ItemType.WarpedStairs; + mappings[454] = ItemType.CommandBlock; + mappings[455] = ItemType.Beacon; + mappings[456] = ItemType.CobblestoneWall; + mappings[457] = ItemType.MossyCobblestoneWall; + mappings[458] = ItemType.BrickWall; + mappings[459] = ItemType.PrismarineWall; + mappings[460] = ItemType.RedSandstoneWall; + mappings[461] = ItemType.MossyStoneBrickWall; + mappings[462] = ItemType.GraniteWall; + mappings[463] = ItemType.StoneBrickWall; + mappings[464] = ItemType.MudBrickWall; + mappings[465] = ItemType.NetherBrickWall; + mappings[466] = ItemType.AndesiteWall; + mappings[467] = ItemType.RedNetherBrickWall; + mappings[468] = ItemType.SandstoneWall; + mappings[469] = ItemType.EndStoneBrickWall; + mappings[470] = ItemType.DioriteWall; + mappings[471] = ItemType.BlackstoneWall; + mappings[472] = ItemType.PolishedBlackstoneWall; + mappings[473] = ItemType.PolishedBlackstoneBrickWall; + mappings[474] = ItemType.CobbledDeepslateWall; + mappings[475] = ItemType.PolishedDeepslateWall; + mappings[476] = ItemType.DeepslateBrickWall; + mappings[477] = ItemType.DeepslateTileWall; + mappings[478] = ItemType.Anvil; + mappings[479] = ItemType.ChippedAnvil; + mappings[480] = ItemType.DamagedAnvil; + mappings[481] = ItemType.ChiseledQuartzBlock; + mappings[482] = ItemType.QuartzBlock; + mappings[483] = ItemType.QuartzBricks; + mappings[484] = ItemType.QuartzPillar; + mappings[485] = ItemType.QuartzStairs; + mappings[486] = ItemType.WhiteTerracotta; + mappings[487] = ItemType.OrangeTerracotta; + mappings[488] = ItemType.MagentaTerracotta; + mappings[489] = ItemType.LightBlueTerracotta; + mappings[490] = ItemType.YellowTerracotta; + mappings[491] = ItemType.LimeTerracotta; + mappings[492] = ItemType.PinkTerracotta; + mappings[493] = ItemType.GrayTerracotta; + mappings[494] = ItemType.LightGrayTerracotta; + mappings[495] = ItemType.CyanTerracotta; + mappings[496] = ItemType.PurpleTerracotta; + mappings[497] = ItemType.BlueTerracotta; + mappings[498] = ItemType.BrownTerracotta; + mappings[499] = ItemType.GreenTerracotta; + mappings[500] = ItemType.RedTerracotta; + mappings[501] = ItemType.BlackTerracotta; + mappings[502] = ItemType.Barrier; + mappings[503] = ItemType.Light; + mappings[504] = ItemType.HayBlock; + mappings[505] = ItemType.WhiteCarpet; + mappings[506] = ItemType.OrangeCarpet; + mappings[507] = ItemType.MagentaCarpet; + mappings[508] = ItemType.LightBlueCarpet; + mappings[509] = ItemType.YellowCarpet; + mappings[510] = ItemType.LimeCarpet; + mappings[511] = ItemType.PinkCarpet; + mappings[512] = ItemType.GrayCarpet; + mappings[513] = ItemType.LightGrayCarpet; + mappings[514] = ItemType.CyanCarpet; + mappings[515] = ItemType.PurpleCarpet; + mappings[516] = ItemType.BlueCarpet; + mappings[517] = ItemType.BrownCarpet; + mappings[518] = ItemType.GreenCarpet; + mappings[519] = ItemType.RedCarpet; + mappings[520] = ItemType.BlackCarpet; + mappings[521] = ItemType.Terracotta; + mappings[522] = ItemType.PackedIce; + mappings[523] = ItemType.DirtPath; + mappings[524] = ItemType.Sunflower; + mappings[525] = ItemType.Lilac; + mappings[526] = ItemType.RoseBush; + mappings[527] = ItemType.Peony; + mappings[528] = ItemType.TallGrass; + mappings[529] = ItemType.LargeFern; + mappings[530] = ItemType.WhiteStainedGlass; + mappings[531] = ItemType.OrangeStainedGlass; + mappings[532] = ItemType.MagentaStainedGlass; + mappings[533] = ItemType.LightBlueStainedGlass; + mappings[534] = ItemType.YellowStainedGlass; + mappings[535] = ItemType.LimeStainedGlass; + mappings[536] = ItemType.PinkStainedGlass; + mappings[537] = ItemType.GrayStainedGlass; + mappings[538] = ItemType.LightGrayStainedGlass; + mappings[539] = ItemType.CyanStainedGlass; + mappings[540] = ItemType.PurpleStainedGlass; + mappings[541] = ItemType.BlueStainedGlass; + mappings[542] = ItemType.BrownStainedGlass; + mappings[543] = ItemType.GreenStainedGlass; + mappings[544] = ItemType.RedStainedGlass; + mappings[545] = ItemType.BlackStainedGlass; + mappings[546] = ItemType.WhiteStainedGlassPane; + mappings[547] = ItemType.OrangeStainedGlassPane; + mappings[548] = ItemType.MagentaStainedGlassPane; + mappings[549] = ItemType.LightBlueStainedGlassPane; + mappings[550] = ItemType.YellowStainedGlassPane; + mappings[551] = ItemType.LimeStainedGlassPane; + mappings[552] = ItemType.PinkStainedGlassPane; + mappings[553] = ItemType.GrayStainedGlassPane; + mappings[554] = ItemType.LightGrayStainedGlassPane; + mappings[555] = ItemType.CyanStainedGlassPane; + mappings[556] = ItemType.PurpleStainedGlassPane; + mappings[557] = ItemType.BlueStainedGlassPane; + mappings[558] = ItemType.BrownStainedGlassPane; + mappings[559] = ItemType.GreenStainedGlassPane; + mappings[560] = ItemType.RedStainedGlassPane; + mappings[561] = ItemType.BlackStainedGlassPane; + mappings[562] = ItemType.Prismarine; + mappings[563] = ItemType.PrismarineBricks; + mappings[564] = ItemType.DarkPrismarine; + mappings[565] = ItemType.PrismarineStairs; + mappings[566] = ItemType.PrismarineBrickStairs; + mappings[567] = ItemType.DarkPrismarineStairs; + mappings[568] = ItemType.SeaLantern; + mappings[569] = ItemType.RedSandstone; + mappings[570] = ItemType.ChiseledRedSandstone; + mappings[571] = ItemType.CutRedSandstone; + mappings[572] = ItemType.RedSandstoneStairs; + mappings[573] = ItemType.RepeatingCommandBlock; + mappings[574] = ItemType.ChainCommandBlock; + mappings[575] = ItemType.MagmaBlock; + mappings[576] = ItemType.NetherWartBlock; + mappings[577] = ItemType.WarpedWartBlock; + mappings[578] = ItemType.RedNetherBricks; + mappings[579] = ItemType.BoneBlock; + mappings[580] = ItemType.StructureVoid; + mappings[581] = ItemType.ShulkerBox; + mappings[582] = ItemType.WhiteShulkerBox; + mappings[583] = ItemType.OrangeShulkerBox; + mappings[584] = ItemType.MagentaShulkerBox; + mappings[585] = ItemType.LightBlueShulkerBox; + mappings[586] = ItemType.YellowShulkerBox; + mappings[587] = ItemType.LimeShulkerBox; + mappings[588] = ItemType.PinkShulkerBox; + mappings[589] = ItemType.GrayShulkerBox; + mappings[590] = ItemType.LightGrayShulkerBox; + mappings[591] = ItemType.CyanShulkerBox; + mappings[592] = ItemType.PurpleShulkerBox; + mappings[593] = ItemType.BlueShulkerBox; + mappings[594] = ItemType.BrownShulkerBox; + mappings[595] = ItemType.GreenShulkerBox; + mappings[596] = ItemType.RedShulkerBox; + mappings[597] = ItemType.BlackShulkerBox; + mappings[598] = ItemType.WhiteGlazedTerracotta; + mappings[599] = ItemType.OrangeGlazedTerracotta; + mappings[600] = ItemType.MagentaGlazedTerracotta; + mappings[601] = ItemType.LightBlueGlazedTerracotta; + mappings[602] = ItemType.YellowGlazedTerracotta; + mappings[603] = ItemType.LimeGlazedTerracotta; + mappings[604] = ItemType.PinkGlazedTerracotta; + mappings[605] = ItemType.GrayGlazedTerracotta; + mappings[606] = ItemType.LightGrayGlazedTerracotta; + mappings[607] = ItemType.CyanGlazedTerracotta; + mappings[608] = ItemType.PurpleGlazedTerracotta; + mappings[609] = ItemType.BlueGlazedTerracotta; + mappings[610] = ItemType.BrownGlazedTerracotta; + mappings[611] = ItemType.GreenGlazedTerracotta; + mappings[612] = ItemType.RedGlazedTerracotta; + mappings[613] = ItemType.BlackGlazedTerracotta; + mappings[614] = ItemType.WhiteConcrete; + mappings[615] = ItemType.OrangeConcrete; + mappings[616] = ItemType.MagentaConcrete; + mappings[617] = ItemType.LightBlueConcrete; + mappings[618] = ItemType.YellowConcrete; + mappings[619] = ItemType.LimeConcrete; + mappings[620] = ItemType.PinkConcrete; + mappings[621] = ItemType.GrayConcrete; + mappings[622] = ItemType.LightGrayConcrete; + mappings[623] = ItemType.CyanConcrete; + mappings[624] = ItemType.PurpleConcrete; + mappings[625] = ItemType.BlueConcrete; + mappings[626] = ItemType.BrownConcrete; + mappings[627] = ItemType.GreenConcrete; + mappings[628] = ItemType.RedConcrete; + mappings[629] = ItemType.BlackConcrete; + mappings[630] = ItemType.WhiteConcretePowder; + mappings[631] = ItemType.OrangeConcretePowder; + mappings[632] = ItemType.MagentaConcretePowder; + mappings[633] = ItemType.LightBlueConcretePowder; + mappings[634] = ItemType.YellowConcretePowder; + mappings[635] = ItemType.LimeConcretePowder; + mappings[636] = ItemType.PinkConcretePowder; + mappings[637] = ItemType.GrayConcretePowder; + mappings[638] = ItemType.LightGrayConcretePowder; + mappings[639] = ItemType.CyanConcretePowder; + mappings[640] = ItemType.PurpleConcretePowder; + mappings[641] = ItemType.BlueConcretePowder; + mappings[642] = ItemType.BrownConcretePowder; + mappings[643] = ItemType.GreenConcretePowder; + mappings[644] = ItemType.RedConcretePowder; + mappings[645] = ItemType.BlackConcretePowder; + mappings[646] = ItemType.TurtleEgg; + mappings[647] = ItemType.SnifferEgg; + mappings[648] = ItemType.DriedGhast; + mappings[649] = ItemType.DeadTubeCoralBlock; + mappings[650] = ItemType.DeadBrainCoralBlock; + mappings[651] = ItemType.DeadBubbleCoralBlock; + mappings[652] = ItemType.DeadFireCoralBlock; + mappings[653] = ItemType.DeadHornCoralBlock; + mappings[654] = ItemType.TubeCoralBlock; + mappings[655] = ItemType.BrainCoralBlock; + mappings[656] = ItemType.BubbleCoralBlock; + mappings[657] = ItemType.FireCoralBlock; + mappings[658] = ItemType.HornCoralBlock; + mappings[659] = ItemType.TubeCoral; + mappings[660] = ItemType.BrainCoral; + mappings[661] = ItemType.BubbleCoral; + mappings[662] = ItemType.FireCoral; + mappings[663] = ItemType.HornCoral; + mappings[664] = ItemType.DeadBrainCoral; + mappings[665] = ItemType.DeadBubbleCoral; + mappings[666] = ItemType.DeadFireCoral; + mappings[667] = ItemType.DeadHornCoral; + mappings[668] = ItemType.DeadTubeCoral; + mappings[669] = ItemType.TubeCoralFan; + mappings[670] = ItemType.BrainCoralFan; + mappings[671] = ItemType.BubbleCoralFan; + mappings[672] = ItemType.FireCoralFan; + mappings[673] = ItemType.HornCoralFan; + mappings[674] = ItemType.DeadTubeCoralFan; + mappings[675] = ItemType.DeadBrainCoralFan; + mappings[676] = ItemType.DeadBubbleCoralFan; + mappings[677] = ItemType.DeadFireCoralFan; + mappings[678] = ItemType.DeadHornCoralFan; + mappings[679] = ItemType.BlueIce; + mappings[680] = ItemType.Conduit; + mappings[681] = ItemType.PolishedGraniteStairs; + mappings[682] = ItemType.SmoothRedSandstoneStairs; + mappings[683] = ItemType.MossyStoneBrickStairs; + mappings[684] = ItemType.PolishedDioriteStairs; + mappings[685] = ItemType.MossyCobblestoneStairs; + mappings[686] = ItemType.EndStoneBrickStairs; + mappings[687] = ItemType.StoneStairs; + mappings[688] = ItemType.SmoothSandstoneStairs; + mappings[689] = ItemType.SmoothQuartzStairs; + mappings[690] = ItemType.GraniteStairs; + mappings[691] = ItemType.AndesiteStairs; + mappings[692] = ItemType.RedNetherBrickStairs; + mappings[693] = ItemType.PolishedAndesiteStairs; + mappings[694] = ItemType.DioriteStairs; + mappings[695] = ItemType.CobbledDeepslateStairs; + mappings[696] = ItemType.PolishedDeepslateStairs; + mappings[697] = ItemType.DeepslateBrickStairs; + mappings[698] = ItemType.DeepslateTileStairs; + mappings[699] = ItemType.PolishedGraniteSlab; + mappings[700] = ItemType.SmoothRedSandstoneSlab; + mappings[701] = ItemType.MossyStoneBrickSlab; + mappings[702] = ItemType.PolishedDioriteSlab; + mappings[703] = ItemType.MossyCobblestoneSlab; + mappings[704] = ItemType.EndStoneBrickSlab; + mappings[705] = ItemType.SmoothSandstoneSlab; + mappings[706] = ItemType.SmoothQuartzSlab; + mappings[707] = ItemType.GraniteSlab; + mappings[708] = ItemType.AndesiteSlab; + mappings[709] = ItemType.RedNetherBrickSlab; + mappings[710] = ItemType.PolishedAndesiteSlab; + mappings[711] = ItemType.DioriteSlab; + mappings[712] = ItemType.CobbledDeepslateSlab; + mappings[713] = ItemType.PolishedDeepslateSlab; + mappings[714] = ItemType.DeepslateBrickSlab; + mappings[715] = ItemType.DeepslateTileSlab; + mappings[716] = ItemType.Scaffolding; + mappings[717] = ItemType.Redstone; + mappings[718] = ItemType.RedstoneTorch; + mappings[719] = ItemType.RedstoneBlock; + mappings[720] = ItemType.Repeater; + mappings[721] = ItemType.Comparator; + mappings[722] = ItemType.Piston; + mappings[723] = ItemType.StickyPiston; + mappings[724] = ItemType.SlimeBlock; + mappings[725] = ItemType.HoneyBlock; + mappings[726] = ItemType.Observer; + mappings[727] = ItemType.Hopper; + mappings[728] = ItemType.Dispenser; + mappings[729] = ItemType.Dropper; + mappings[730] = ItemType.Lectern; + mappings[731] = ItemType.Target; + mappings[732] = ItemType.Lever; + mappings[733] = ItemType.LightningRod; + mappings[734] = ItemType.ExposedLightningRod; + mappings[735] = ItemType.WeatheredLightningRod; + mappings[736] = ItemType.OxidizedLightningRod; + mappings[737] = ItemType.WaxedLightningRod; + mappings[738] = ItemType.WaxedExposedLightningRod; + mappings[739] = ItemType.WaxedWeatheredLightningRod; + mappings[740] = ItemType.WaxedOxidizedLightningRod; + mappings[741] = ItemType.DaylightDetector; + mappings[742] = ItemType.SculkSensor; + mappings[743] = ItemType.CalibratedSculkSensor; + mappings[744] = ItemType.TripwireHook; + mappings[745] = ItemType.TrappedChest; + mappings[746] = ItemType.Tnt; + mappings[747] = ItemType.RedstoneLamp; + mappings[748] = ItemType.NoteBlock; + mappings[749] = ItemType.StoneButton; + mappings[750] = ItemType.PolishedBlackstoneButton; + mappings[751] = ItemType.OakButton; + mappings[752] = ItemType.SpruceButton; + mappings[753] = ItemType.BirchButton; + mappings[754] = ItemType.JungleButton; + mappings[755] = ItemType.AcaciaButton; + mappings[756] = ItemType.CherryButton; + mappings[757] = ItemType.DarkOakButton; + mappings[758] = ItemType.PaleOakButton; + mappings[759] = ItemType.MangroveButton; + mappings[760] = ItemType.BambooButton; + mappings[761] = ItemType.CrimsonButton; + mappings[762] = ItemType.WarpedButton; + mappings[763] = ItemType.StonePressurePlate; + mappings[764] = ItemType.PolishedBlackstonePressurePlate; + mappings[765] = ItemType.LightWeightedPressurePlate; + mappings[766] = ItemType.HeavyWeightedPressurePlate; + mappings[767] = ItemType.OakPressurePlate; + mappings[768] = ItemType.SprucePressurePlate; + mappings[769] = ItemType.BirchPressurePlate; + mappings[770] = ItemType.JunglePressurePlate; + mappings[771] = ItemType.AcaciaPressurePlate; + mappings[772] = ItemType.CherryPressurePlate; + mappings[773] = ItemType.DarkOakPressurePlate; + mappings[774] = ItemType.PaleOakPressurePlate; + mappings[775] = ItemType.MangrovePressurePlate; + mappings[776] = ItemType.BambooPressurePlate; + mappings[777] = ItemType.CrimsonPressurePlate; + mappings[778] = ItemType.WarpedPressurePlate; + mappings[779] = ItemType.IronDoor; + mappings[780] = ItemType.OakDoor; + mappings[781] = ItemType.SpruceDoor; + mappings[782] = ItemType.BirchDoor; + mappings[783] = ItemType.JungleDoor; + mappings[784] = ItemType.AcaciaDoor; + mappings[785] = ItemType.CherryDoor; + mappings[786] = ItemType.DarkOakDoor; + mappings[787] = ItemType.PaleOakDoor; + mappings[788] = ItemType.MangroveDoor; + mappings[789] = ItemType.BambooDoor; + mappings[790] = ItemType.CrimsonDoor; + mappings[791] = ItemType.WarpedDoor; + mappings[792] = ItemType.CopperDoor; + mappings[793] = ItemType.ExposedCopperDoor; + mappings[794] = ItemType.WeatheredCopperDoor; + mappings[795] = ItemType.OxidizedCopperDoor; + mappings[796] = ItemType.WaxedCopperDoor; + mappings[797] = ItemType.WaxedExposedCopperDoor; + mappings[798] = ItemType.WaxedWeatheredCopperDoor; + mappings[799] = ItemType.WaxedOxidizedCopperDoor; + mappings[800] = ItemType.IronTrapdoor; + mappings[801] = ItemType.OakTrapdoor; + mappings[802] = ItemType.SpruceTrapdoor; + mappings[803] = ItemType.BirchTrapdoor; + mappings[804] = ItemType.JungleTrapdoor; + mappings[805] = ItemType.AcaciaTrapdoor; + mappings[806] = ItemType.CherryTrapdoor; + mappings[807] = ItemType.DarkOakTrapdoor; + mappings[808] = ItemType.PaleOakTrapdoor; + mappings[809] = ItemType.MangroveTrapdoor; + mappings[810] = ItemType.BambooTrapdoor; + mappings[811] = ItemType.CrimsonTrapdoor; + mappings[812] = ItemType.WarpedTrapdoor; + mappings[813] = ItemType.CopperTrapdoor; + mappings[814] = ItemType.ExposedCopperTrapdoor; + mappings[815] = ItemType.WeatheredCopperTrapdoor; + mappings[816] = ItemType.OxidizedCopperTrapdoor; + mappings[817] = ItemType.WaxedCopperTrapdoor; + mappings[818] = ItemType.WaxedExposedCopperTrapdoor; + mappings[819] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[820] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[821] = ItemType.OakFenceGate; + mappings[822] = ItemType.SpruceFenceGate; + mappings[823] = ItemType.BirchFenceGate; + mappings[824] = ItemType.JungleFenceGate; + mappings[825] = ItemType.AcaciaFenceGate; + mappings[826] = ItemType.CherryFenceGate; + mappings[827] = ItemType.DarkOakFenceGate; + mappings[828] = ItemType.PaleOakFenceGate; + mappings[829] = ItemType.MangroveFenceGate; + mappings[830] = ItemType.BambooFenceGate; + mappings[831] = ItemType.CrimsonFenceGate; + mappings[832] = ItemType.WarpedFenceGate; + mappings[833] = ItemType.PoweredRail; + mappings[834] = ItemType.DetectorRail; + mappings[835] = ItemType.Rail; + mappings[836] = ItemType.ActivatorRail; + mappings[837] = ItemType.Saddle; + mappings[838] = ItemType.WhiteHarness; + mappings[839] = ItemType.OrangeHarness; + mappings[840] = ItemType.MagentaHarness; + mappings[841] = ItemType.LightBlueHarness; + mappings[842] = ItemType.YellowHarness; + mappings[843] = ItemType.LimeHarness; + mappings[844] = ItemType.PinkHarness; + mappings[845] = ItemType.GrayHarness; + mappings[846] = ItemType.LightGrayHarness; + mappings[847] = ItemType.CyanHarness; + mappings[848] = ItemType.PurpleHarness; + mappings[849] = ItemType.BlueHarness; + mappings[850] = ItemType.BrownHarness; + mappings[851] = ItemType.GreenHarness; + mappings[852] = ItemType.RedHarness; + mappings[853] = ItemType.BlackHarness; + mappings[854] = ItemType.Minecart; + mappings[855] = ItemType.ChestMinecart; + mappings[856] = ItemType.FurnaceMinecart; + mappings[857] = ItemType.TntMinecart; + mappings[858] = ItemType.HopperMinecart; + mappings[859] = ItemType.CarrotOnAStick; + mappings[860] = ItemType.WarpedFungusOnAStick; + mappings[861] = ItemType.PhantomMembrane; + mappings[862] = ItemType.Elytra; + mappings[863] = ItemType.OakBoat; + mappings[864] = ItemType.OakChestBoat; + mappings[865] = ItemType.SpruceBoat; + mappings[866] = ItemType.SpruceChestBoat; + mappings[867] = ItemType.BirchBoat; + mappings[868] = ItemType.BirchChestBoat; + mappings[869] = ItemType.JungleBoat; + mappings[870] = ItemType.JungleChestBoat; + mappings[871] = ItemType.AcaciaBoat; + mappings[872] = ItemType.AcaciaChestBoat; + mappings[873] = ItemType.CherryBoat; + mappings[874] = ItemType.CherryChestBoat; + mappings[875] = ItemType.DarkOakBoat; + mappings[876] = ItemType.DarkOakChestBoat; + mappings[877] = ItemType.PaleOakBoat; + mappings[878] = ItemType.PaleOakChestBoat; + mappings[879] = ItemType.MangroveBoat; + mappings[880] = ItemType.MangroveChestBoat; + mappings[881] = ItemType.BambooRaft; + mappings[882] = ItemType.BambooChestRaft; + mappings[883] = ItemType.StructureBlock; + mappings[884] = ItemType.Jigsaw; + mappings[885] = ItemType.TestBlock; + mappings[886] = ItemType.TestInstanceBlock; + mappings[887] = ItemType.TurtleHelmet; + mappings[888] = ItemType.TurtleScute; + mappings[889] = ItemType.ArmadilloScute; + mappings[890] = ItemType.WolfArmor; + mappings[891] = ItemType.FlintAndSteel; + mappings[892] = ItemType.Bowl; + mappings[893] = ItemType.Apple; + mappings[894] = ItemType.Bow; + mappings[895] = ItemType.Arrow; + mappings[896] = ItemType.Coal; + mappings[897] = ItemType.Charcoal; + mappings[898] = ItemType.Diamond; + mappings[899] = ItemType.Emerald; + mappings[900] = ItemType.LapisLazuli; + mappings[901] = ItemType.Quartz; + mappings[902] = ItemType.AmethystShard; + mappings[903] = ItemType.RawIron; + mappings[904] = ItemType.IronIngot; + mappings[905] = ItemType.RawCopper; + mappings[906] = ItemType.CopperIngot; + mappings[907] = ItemType.RawGold; + mappings[908] = ItemType.GoldIngot; + mappings[909] = ItemType.NetheriteIngot; + mappings[910] = ItemType.NetheriteScrap; + mappings[911] = ItemType.WoodenSword; + mappings[912] = ItemType.WoodenShovel; + mappings[913] = ItemType.WoodenPickaxe; + mappings[914] = ItemType.WoodenAxe; + mappings[915] = ItemType.WoodenHoe; + mappings[916] = ItemType.CopperSword; + mappings[917] = ItemType.CopperShovel; + mappings[918] = ItemType.CopperPickaxe; + mappings[919] = ItemType.CopperAxe; + mappings[920] = ItemType.CopperHoe; + mappings[921] = ItemType.StoneSword; + mappings[922] = ItemType.StoneShovel; + mappings[923] = ItemType.StonePickaxe; + mappings[924] = ItemType.StoneAxe; + mappings[925] = ItemType.StoneHoe; + mappings[926] = ItemType.GoldenSword; + mappings[927] = ItemType.GoldenShovel; + mappings[928] = ItemType.GoldenPickaxe; + mappings[929] = ItemType.GoldenAxe; + mappings[930] = ItemType.GoldenHoe; + mappings[931] = ItemType.IronSword; + mappings[932] = ItemType.IronShovel; + mappings[933] = ItemType.IronPickaxe; + mappings[934] = ItemType.IronAxe; + mappings[935] = ItemType.IronHoe; + mappings[936] = ItemType.DiamondSword; + mappings[937] = ItemType.DiamondShovel; + mappings[938] = ItemType.DiamondPickaxe; + mappings[939] = ItemType.DiamondAxe; + mappings[940] = ItemType.DiamondHoe; + mappings[941] = ItemType.NetheriteSword; + mappings[942] = ItemType.NetheriteShovel; + mappings[943] = ItemType.NetheritePickaxe; + mappings[944] = ItemType.NetheriteAxe; + mappings[945] = ItemType.NetheriteHoe; + mappings[946] = ItemType.Stick; + mappings[947] = ItemType.MushroomStew; + mappings[948] = ItemType.String; + mappings[949] = ItemType.Feather; + mappings[950] = ItemType.Gunpowder; + mappings[951] = ItemType.WheatSeeds; + mappings[952] = ItemType.Wheat; + mappings[953] = ItemType.Bread; + mappings[954] = ItemType.LeatherHelmet; + mappings[955] = ItemType.LeatherChestplate; + mappings[956] = ItemType.LeatherLeggings; + mappings[957] = ItemType.LeatherBoots; + mappings[958] = ItemType.CopperHelmet; + mappings[959] = ItemType.CopperChestplate; + mappings[960] = ItemType.CopperLeggings; + mappings[961] = ItemType.CopperBoots; + mappings[962] = ItemType.ChainmailHelmet; + mappings[963] = ItemType.ChainmailChestplate; + mappings[964] = ItemType.ChainmailLeggings; + mappings[965] = ItemType.ChainmailBoots; + mappings[966] = ItemType.IronHelmet; + mappings[967] = ItemType.IronChestplate; + mappings[968] = ItemType.IronLeggings; + mappings[969] = ItemType.IronBoots; + mappings[970] = ItemType.DiamondHelmet; + mappings[971] = ItemType.DiamondChestplate; + mappings[972] = ItemType.DiamondLeggings; + mappings[973] = ItemType.DiamondBoots; + mappings[974] = ItemType.GoldenHelmet; + mappings[975] = ItemType.GoldenChestplate; + mappings[976] = ItemType.GoldenLeggings; + mappings[977] = ItemType.GoldenBoots; + mappings[978] = ItemType.NetheriteHelmet; + mappings[979] = ItemType.NetheriteChestplate; + mappings[980] = ItemType.NetheriteLeggings; + mappings[981] = ItemType.NetheriteBoots; + mappings[982] = ItemType.Flint; + mappings[983] = ItemType.Porkchop; + mappings[984] = ItemType.CookedPorkchop; + mappings[985] = ItemType.Painting; + mappings[986] = ItemType.GoldenApple; + mappings[987] = ItemType.EnchantedGoldenApple; + mappings[988] = ItemType.OakSign; + mappings[989] = ItemType.SpruceSign; + mappings[990] = ItemType.BirchSign; + mappings[991] = ItemType.JungleSign; + mappings[992] = ItemType.AcaciaSign; + mappings[993] = ItemType.CherrySign; + mappings[994] = ItemType.DarkOakSign; + mappings[995] = ItemType.PaleOakSign; + mappings[996] = ItemType.MangroveSign; + mappings[997] = ItemType.BambooSign; + mappings[998] = ItemType.CrimsonSign; + mappings[999] = ItemType.WarpedSign; + mappings[1000] = ItemType.OakHangingSign; + mappings[1001] = ItemType.SpruceHangingSign; + mappings[1002] = ItemType.BirchHangingSign; + mappings[1003] = ItemType.JungleHangingSign; + mappings[1004] = ItemType.AcaciaHangingSign; + mappings[1005] = ItemType.CherryHangingSign; + mappings[1006] = ItemType.DarkOakHangingSign; + mappings[1007] = ItemType.PaleOakHangingSign; + mappings[1008] = ItemType.MangroveHangingSign; + mappings[1009] = ItemType.BambooHangingSign; + mappings[1010] = ItemType.CrimsonHangingSign; + mappings[1011] = ItemType.WarpedHangingSign; + mappings[1012] = ItemType.Bucket; + mappings[1013] = ItemType.WaterBucket; + mappings[1014] = ItemType.LavaBucket; + mappings[1015] = ItemType.PowderSnowBucket; + mappings[1016] = ItemType.Snowball; + mappings[1017] = ItemType.Leather; + mappings[1018] = ItemType.MilkBucket; + mappings[1019] = ItemType.PufferfishBucket; + mappings[1020] = ItemType.SalmonBucket; + mappings[1021] = ItemType.CodBucket; + mappings[1022] = ItemType.TropicalFishBucket; + mappings[1023] = ItemType.AxolotlBucket; + mappings[1024] = ItemType.TadpoleBucket; + mappings[1025] = ItemType.Brick; + mappings[1026] = ItemType.ClayBall; + mappings[1027] = ItemType.DriedKelpBlock; + mappings[1028] = ItemType.Paper; + mappings[1029] = ItemType.Book; + mappings[1030] = ItemType.SlimeBall; + mappings[1031] = ItemType.Egg; + mappings[1032] = ItemType.BlueEgg; + mappings[1033] = ItemType.BrownEgg; + mappings[1034] = ItemType.Compass; + mappings[1035] = ItemType.RecoveryCompass; + mappings[1036] = ItemType.Bundle; + mappings[1037] = ItemType.WhiteBundle; + mappings[1038] = ItemType.OrangeBundle; + mappings[1039] = ItemType.MagentaBundle; + mappings[1040] = ItemType.LightBlueBundle; + mappings[1041] = ItemType.YellowBundle; + mappings[1042] = ItemType.LimeBundle; + mappings[1043] = ItemType.PinkBundle; + mappings[1044] = ItemType.GrayBundle; + mappings[1045] = ItemType.LightGrayBundle; + mappings[1046] = ItemType.CyanBundle; + mappings[1047] = ItemType.PurpleBundle; + mappings[1048] = ItemType.BlueBundle; + mappings[1049] = ItemType.BrownBundle; + mappings[1050] = ItemType.GreenBundle; + mappings[1051] = ItemType.RedBundle; + mappings[1052] = ItemType.BlackBundle; + mappings[1053] = ItemType.FishingRod; + mappings[1054] = ItemType.Clock; + mappings[1055] = ItemType.Spyglass; + mappings[1056] = ItemType.GlowstoneDust; + mappings[1057] = ItemType.Cod; + mappings[1058] = ItemType.Salmon; + mappings[1059] = ItemType.TropicalFish; + mappings[1060] = ItemType.Pufferfish; + mappings[1061] = ItemType.CookedCod; + mappings[1062] = ItemType.CookedSalmon; + mappings[1063] = ItemType.InkSac; + mappings[1064] = ItemType.GlowInkSac; + mappings[1065] = ItemType.CocoaBeans; + mappings[1066] = ItemType.WhiteDye; + mappings[1067] = ItemType.OrangeDye; + mappings[1068] = ItemType.MagentaDye; + mappings[1069] = ItemType.LightBlueDye; + mappings[1070] = ItemType.YellowDye; + mappings[1071] = ItemType.LimeDye; + mappings[1072] = ItemType.PinkDye; + mappings[1073] = ItemType.GrayDye; + mappings[1074] = ItemType.LightGrayDye; + mappings[1075] = ItemType.CyanDye; + mappings[1076] = ItemType.PurpleDye; + mappings[1077] = ItemType.BlueDye; + mappings[1078] = ItemType.BrownDye; + mappings[1079] = ItemType.GreenDye; + mappings[1080] = ItemType.RedDye; + mappings[1081] = ItemType.BlackDye; + mappings[1082] = ItemType.BoneMeal; + mappings[1083] = ItemType.Bone; + mappings[1084] = ItemType.Sugar; + mappings[1085] = ItemType.Cake; + mappings[1086] = ItemType.WhiteBed; + mappings[1087] = ItemType.OrangeBed; + mappings[1088] = ItemType.MagentaBed; + mappings[1089] = ItemType.LightBlueBed; + mappings[1090] = ItemType.YellowBed; + mappings[1091] = ItemType.LimeBed; + mappings[1092] = ItemType.PinkBed; + mappings[1093] = ItemType.GrayBed; + mappings[1094] = ItemType.LightGrayBed; + mappings[1095] = ItemType.CyanBed; + mappings[1096] = ItemType.PurpleBed; + mappings[1097] = ItemType.BlueBed; + mappings[1098] = ItemType.BrownBed; + mappings[1099] = ItemType.GreenBed; + mappings[1100] = ItemType.RedBed; + mappings[1101] = ItemType.BlackBed; + mappings[1102] = ItemType.Cookie; + mappings[1103] = ItemType.Crafter; + mappings[1104] = ItemType.FilledMap; + mappings[1105] = ItemType.Shears; + mappings[1106] = ItemType.MelonSlice; + mappings[1107] = ItemType.DriedKelp; + mappings[1108] = ItemType.PumpkinSeeds; + mappings[1109] = ItemType.MelonSeeds; + mappings[1110] = ItemType.Beef; + mappings[1111] = ItemType.CookedBeef; + mappings[1112] = ItemType.Chicken; + mappings[1113] = ItemType.CookedChicken; + mappings[1114] = ItemType.RottenFlesh; + mappings[1115] = ItemType.EnderPearl; + mappings[1116] = ItemType.BlazeRod; + mappings[1117] = ItemType.GhastTear; + mappings[1118] = ItemType.GoldNugget; + mappings[1119] = ItemType.NetherWart; + mappings[1120] = ItemType.GlassBottle; + mappings[1121] = ItemType.Potion; + mappings[1122] = ItemType.SpiderEye; + mappings[1123] = ItemType.FermentedSpiderEye; + mappings[1124] = ItemType.BlazePowder; + mappings[1125] = ItemType.MagmaCream; + mappings[1126] = ItemType.BrewingStand; + mappings[1127] = ItemType.Cauldron; + mappings[1128] = ItemType.EnderEye; + mappings[1129] = ItemType.GlisteringMelonSlice; + mappings[1130] = ItemType.ChickenSpawnEgg; + mappings[1131] = ItemType.CowSpawnEgg; + mappings[1132] = ItemType.PigSpawnEgg; + mappings[1133] = ItemType.SheepSpawnEgg; + mappings[1134] = ItemType.CamelSpawnEgg; + mappings[1135] = ItemType.DonkeySpawnEgg; + mappings[1136] = ItemType.HorseSpawnEgg; + mappings[1137] = ItemType.MuleSpawnEgg; + mappings[1138] = ItemType.CatSpawnEgg; + mappings[1139] = ItemType.ParrotSpawnEgg; + mappings[1140] = ItemType.WolfSpawnEgg; + mappings[1141] = ItemType.ArmadilloSpawnEgg; + mappings[1142] = ItemType.BatSpawnEgg; + mappings[1143] = ItemType.BeeSpawnEgg; + mappings[1144] = ItemType.FoxSpawnEgg; + mappings[1145] = ItemType.GoatSpawnEgg; + mappings[1146] = ItemType.LlamaSpawnEgg; + mappings[1147] = ItemType.OcelotSpawnEgg; + mappings[1148] = ItemType.PandaSpawnEgg; + mappings[1149] = ItemType.PolarBearSpawnEgg; + mappings[1150] = ItemType.RabbitSpawnEgg; + mappings[1151] = ItemType.AxolotlSpawnEgg; + mappings[1152] = ItemType.CodSpawnEgg; + mappings[1153] = ItemType.DolphinSpawnEgg; + mappings[1154] = ItemType.FrogSpawnEgg; + mappings[1155] = ItemType.GlowSquidSpawnEgg; + mappings[1156] = ItemType.NautilusSpawnEgg; + mappings[1157] = ItemType.PufferfishSpawnEgg; + mappings[1158] = ItemType.SalmonSpawnEgg; + mappings[1159] = ItemType.SquidSpawnEgg; + mappings[1160] = ItemType.TadpoleSpawnEgg; + mappings[1161] = ItemType.TropicalFishSpawnEgg; + mappings[1162] = ItemType.TurtleSpawnEgg; + mappings[1163] = ItemType.AllaySpawnEgg; + mappings[1164] = ItemType.MooshroomSpawnEgg; + mappings[1165] = ItemType.SnifferSpawnEgg; + mappings[1166] = ItemType.CopperGolemSpawnEgg; + mappings[1167] = ItemType.IronGolemSpawnEgg; + mappings[1168] = ItemType.SnowGolemSpawnEgg; + mappings[1169] = ItemType.TraderLlamaSpawnEgg; + mappings[1170] = ItemType.VillagerSpawnEgg; + mappings[1171] = ItemType.WanderingTraderSpawnEgg; + mappings[1172] = ItemType.BoggedSpawnEgg; + mappings[1173] = ItemType.CamelHuskSpawnEgg; + mappings[1174] = ItemType.DrownedSpawnEgg; + mappings[1175] = ItemType.HuskSpawnEgg; + mappings[1176] = ItemType.ParchedSpawnEgg; + mappings[1177] = ItemType.SkeletonSpawnEgg; + mappings[1178] = ItemType.SkeletonHorseSpawnEgg; + mappings[1179] = ItemType.StraySpawnEgg; + mappings[1180] = ItemType.WitherSpawnEgg; + mappings[1181] = ItemType.WitherSkeletonSpawnEgg; + mappings[1182] = ItemType.ZombieSpawnEgg; + mappings[1183] = ItemType.ZombieHorseSpawnEgg; + mappings[1184] = ItemType.ZombieNautilusSpawnEgg; + mappings[1185] = ItemType.ZombieVillagerSpawnEgg; + mappings[1186] = ItemType.CaveSpiderSpawnEgg; + mappings[1187] = ItemType.SpiderSpawnEgg; + mappings[1188] = ItemType.BreezeSpawnEgg; + mappings[1189] = ItemType.CreakingSpawnEgg; + mappings[1190] = ItemType.CreeperSpawnEgg; + mappings[1191] = ItemType.ElderGuardianSpawnEgg; + mappings[1192] = ItemType.GuardianSpawnEgg; + mappings[1193] = ItemType.PhantomSpawnEgg; + mappings[1194] = ItemType.SilverfishSpawnEgg; + mappings[1195] = ItemType.SlimeSpawnEgg; + mappings[1196] = ItemType.WardenSpawnEgg; + mappings[1197] = ItemType.WitchSpawnEgg; + mappings[1198] = ItemType.EvokerSpawnEgg; + mappings[1199] = ItemType.PillagerSpawnEgg; + mappings[1200] = ItemType.RavagerSpawnEgg; + mappings[1201] = ItemType.VindicatorSpawnEgg; + mappings[1202] = ItemType.VexSpawnEgg; + mappings[1203] = ItemType.BlazeSpawnEgg; + mappings[1204] = ItemType.GhastSpawnEgg; + mappings[1205] = ItemType.HappyGhastSpawnEgg; + mappings[1206] = ItemType.HoglinSpawnEgg; + mappings[1207] = ItemType.MagmaCubeSpawnEgg; + mappings[1208] = ItemType.PiglinSpawnEgg; + mappings[1209] = ItemType.PiglinBruteSpawnEgg; + mappings[1210] = ItemType.StriderSpawnEgg; + mappings[1211] = ItemType.ZoglinSpawnEgg; + mappings[1212] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1213] = ItemType.EnderDragonSpawnEgg; + mappings[1214] = ItemType.EndermanSpawnEgg; + mappings[1215] = ItemType.EndermiteSpawnEgg; + mappings[1216] = ItemType.ShulkerSpawnEgg; + mappings[1217] = ItemType.ExperienceBottle; + mappings[1218] = ItemType.FireCharge; + mappings[1219] = ItemType.WindCharge; + mappings[1220] = ItemType.WritableBook; + mappings[1221] = ItemType.WrittenBook; + mappings[1222] = ItemType.BreezeRod; + mappings[1223] = ItemType.Mace; + mappings[1224] = ItemType.ItemFrame; + mappings[1225] = ItemType.GlowItemFrame; + mappings[1226] = ItemType.FlowerPot; + mappings[1227] = ItemType.Carrot; + mappings[1228] = ItemType.Potato; + mappings[1229] = ItemType.BakedPotato; + mappings[1230] = ItemType.PoisonousPotato; + mappings[1231] = ItemType.Map; + mappings[1232] = ItemType.GoldenCarrot; + mappings[1233] = ItemType.SkeletonSkull; + mappings[1234] = ItemType.WitherSkeletonSkull; + mappings[1235] = ItemType.PlayerHead; + mappings[1236] = ItemType.ZombieHead; + mappings[1237] = ItemType.CreeperHead; + mappings[1238] = ItemType.DragonHead; + mappings[1239] = ItemType.PiglinHead; + mappings[1240] = ItemType.NetherStar; + mappings[1241] = ItemType.PumpkinPie; + mappings[1242] = ItemType.FireworkRocket; + mappings[1243] = ItemType.FireworkStar; + mappings[1244] = ItemType.EnchantedBook; + mappings[1245] = ItemType.NetherBrick; + mappings[1246] = ItemType.ResinBrick; + mappings[1247] = ItemType.PrismarineShard; + mappings[1248] = ItemType.PrismarineCrystals; + mappings[1249] = ItemType.Rabbit; + mappings[1250] = ItemType.CookedRabbit; + mappings[1251] = ItemType.RabbitStew; + mappings[1252] = ItemType.RabbitFoot; + mappings[1253] = ItemType.RabbitHide; + mappings[1254] = ItemType.ArmorStand; + mappings[1255] = ItemType.CopperHorseArmor; + mappings[1256] = ItemType.IronHorseArmor; + mappings[1257] = ItemType.GoldenHorseArmor; + mappings[1258] = ItemType.DiamondHorseArmor; + mappings[1259] = ItemType.NetheriteHorseArmor; + mappings[1260] = ItemType.LeatherHorseArmor; + mappings[1261] = ItemType.Lead; + mappings[1262] = ItemType.NameTag; + mappings[1263] = ItemType.CommandBlockMinecart; + mappings[1264] = ItemType.Mutton; + mappings[1265] = ItemType.CookedMutton; + mappings[1266] = ItemType.WhiteBanner; + mappings[1267] = ItemType.OrangeBanner; + mappings[1268] = ItemType.MagentaBanner; + mappings[1269] = ItemType.LightBlueBanner; + mappings[1270] = ItemType.YellowBanner; + mappings[1271] = ItemType.LimeBanner; + mappings[1272] = ItemType.PinkBanner; + mappings[1273] = ItemType.GrayBanner; + mappings[1274] = ItemType.LightGrayBanner; + mappings[1275] = ItemType.CyanBanner; + mappings[1276] = ItemType.PurpleBanner; + mappings[1277] = ItemType.BlueBanner; + mappings[1278] = ItemType.BrownBanner; + mappings[1279] = ItemType.GreenBanner; + mappings[1280] = ItemType.RedBanner; + mappings[1281] = ItemType.BlackBanner; + mappings[1282] = ItemType.EndCrystal; + mappings[1283] = ItemType.ChorusFruit; + mappings[1284] = ItemType.PoppedChorusFruit; + mappings[1285] = ItemType.TorchflowerSeeds; + mappings[1286] = ItemType.PitcherPod; + mappings[1287] = ItemType.Beetroot; + mappings[1288] = ItemType.BeetrootSeeds; + mappings[1289] = ItemType.BeetrootSoup; + mappings[1290] = ItemType.DragonBreath; + mappings[1291] = ItemType.SplashPotion; + mappings[1292] = ItemType.SpectralArrow; + mappings[1293] = ItemType.TippedArrow; + mappings[1294] = ItemType.LingeringPotion; + mappings[1295] = ItemType.Shield; + mappings[1296] = ItemType.WoodenSpear; + mappings[1297] = ItemType.StoneSpear; + mappings[1298] = ItemType.CopperSpear; + mappings[1299] = ItemType.IronSpear; + mappings[1300] = ItemType.GoldenSpear; + mappings[1301] = ItemType.DiamondSpear; + mappings[1302] = ItemType.NetheriteSpear; + mappings[1303] = ItemType.TotemOfUndying; + mappings[1304] = ItemType.ShulkerShell; + mappings[1305] = ItemType.IronNugget; + mappings[1306] = ItemType.CopperNugget; + mappings[1307] = ItemType.KnowledgeBook; + mappings[1308] = ItemType.DebugStick; + mappings[1309] = ItemType.MusicDisc13; + mappings[1310] = ItemType.MusicDiscCat; + mappings[1311] = ItemType.MusicDiscBlocks; + mappings[1312] = ItemType.MusicDiscChirp; + mappings[1313] = ItemType.MusicDiscCreator; + mappings[1314] = ItemType.MusicDiscCreatorMusicBox; + mappings[1315] = ItemType.MusicDiscFar; + mappings[1316] = ItemType.MusicDiscLavaChicken; + mappings[1317] = ItemType.MusicDiscMall; + mappings[1318] = ItemType.MusicDiscMellohi; + mappings[1319] = ItemType.MusicDiscStal; + mappings[1320] = ItemType.MusicDiscStrad; + mappings[1321] = ItemType.MusicDiscWard; + mappings[1322] = ItemType.MusicDisc11; + mappings[1323] = ItemType.MusicDiscWait; + mappings[1324] = ItemType.MusicDiscOtherside; + mappings[1325] = ItemType.MusicDiscRelic; + mappings[1326] = ItemType.MusicDisc5; + mappings[1327] = ItemType.MusicDiscPigstep; + mappings[1328] = ItemType.MusicDiscPrecipice; + mappings[1329] = ItemType.MusicDiscTears; + mappings[1330] = ItemType.DiscFragment5; + mappings[1331] = ItemType.Trident; + mappings[1332] = ItemType.NautilusShell; + mappings[1333] = ItemType.IronNautilusArmor; + mappings[1334] = ItemType.GoldenNautilusArmor; + mappings[1335] = ItemType.DiamondNautilusArmor; + mappings[1336] = ItemType.NetheriteNautilusArmor; + mappings[1337] = ItemType.CopperNautilusArmor; + mappings[1338] = ItemType.HeartOfTheSea; + mappings[1339] = ItemType.Crossbow; + mappings[1340] = ItemType.SuspiciousStew; + mappings[1341] = ItemType.Loom; + mappings[1342] = ItemType.FlowerBannerPattern; + mappings[1343] = ItemType.CreeperBannerPattern; + mappings[1344] = ItemType.SkullBannerPattern; + mappings[1345] = ItemType.MojangBannerPattern; + mappings[1346] = ItemType.GlobeBannerPattern; + mappings[1347] = ItemType.PiglinBannerPattern; + mappings[1348] = ItemType.FlowBannerPattern; + mappings[1349] = ItemType.GusterBannerPattern; + mappings[1350] = ItemType.FieldMasonedBannerPattern; + mappings[1351] = ItemType.BordureIndentedBannerPattern; + mappings[1352] = ItemType.GoatHorn; + mappings[1353] = ItemType.Composter; + mappings[1354] = ItemType.Barrel; + mappings[1355] = ItemType.Smoker; + mappings[1356] = ItemType.BlastFurnace; + mappings[1357] = ItemType.CartographyTable; + mappings[1358] = ItemType.FletchingTable; + mappings[1359] = ItemType.Grindstone; + mappings[1360] = ItemType.SmithingTable; + mappings[1361] = ItemType.Stonecutter; + mappings[1362] = ItemType.Bell; + mappings[1363] = ItemType.Lantern; + mappings[1364] = ItemType.SoulLantern; + mappings[1365] = ItemType.CopperLantern; + mappings[1366] = ItemType.ExposedCopperLantern; + mappings[1367] = ItemType.WeatheredCopperLantern; + mappings[1368] = ItemType.OxidizedCopperLantern; + mappings[1369] = ItemType.WaxedCopperLantern; + mappings[1370] = ItemType.WaxedExposedCopperLantern; + mappings[1371] = ItemType.WaxedWeatheredCopperLantern; + mappings[1372] = ItemType.WaxedOxidizedCopperLantern; + mappings[1373] = ItemType.SweetBerries; + mappings[1374] = ItemType.GlowBerries; + mappings[1375] = ItemType.Campfire; + mappings[1376] = ItemType.SoulCampfire; + mappings[1377] = ItemType.Shroomlight; + mappings[1378] = ItemType.Honeycomb; + mappings[1379] = ItemType.BeeNest; + mappings[1380] = ItemType.Beehive; + mappings[1381] = ItemType.HoneyBottle; + mappings[1382] = ItemType.HoneycombBlock; + mappings[1383] = ItemType.Lodestone; + mappings[1384] = ItemType.CryingObsidian; + mappings[1385] = ItemType.Blackstone; + mappings[1386] = ItemType.BlackstoneSlab; + mappings[1387] = ItemType.BlackstoneStairs; + mappings[1388] = ItemType.GildedBlackstone; + mappings[1389] = ItemType.PolishedBlackstone; + mappings[1390] = ItemType.PolishedBlackstoneSlab; + mappings[1391] = ItemType.PolishedBlackstoneStairs; + mappings[1392] = ItemType.ChiseledPolishedBlackstone; + mappings[1393] = ItemType.PolishedBlackstoneBricks; + mappings[1394] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1395] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1396] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1397] = ItemType.RespawnAnchor; + mappings[1398] = ItemType.Candle; + mappings[1399] = ItemType.WhiteCandle; + mappings[1400] = ItemType.OrangeCandle; + mappings[1401] = ItemType.MagentaCandle; + mappings[1402] = ItemType.LightBlueCandle; + mappings[1403] = ItemType.YellowCandle; + mappings[1404] = ItemType.LimeCandle; + mappings[1405] = ItemType.PinkCandle; + mappings[1406] = ItemType.GrayCandle; + mappings[1407] = ItemType.LightGrayCandle; + mappings[1408] = ItemType.CyanCandle; + mappings[1409] = ItemType.PurpleCandle; + mappings[1410] = ItemType.BlueCandle; + mappings[1411] = ItemType.BrownCandle; + mappings[1412] = ItemType.GreenCandle; + mappings[1413] = ItemType.RedCandle; + mappings[1414] = ItemType.BlackCandle; + mappings[1415] = ItemType.SmallAmethystBud; + mappings[1416] = ItemType.MediumAmethystBud; + mappings[1417] = ItemType.LargeAmethystBud; + mappings[1418] = ItemType.AmethystCluster; + mappings[1419] = ItemType.PointedDripstone; + mappings[1420] = ItemType.OchreFroglight; + mappings[1421] = ItemType.VerdantFroglight; + mappings[1422] = ItemType.PearlescentFroglight; + mappings[1423] = ItemType.Frogspawn; + mappings[1424] = ItemType.EchoShard; + mappings[1425] = ItemType.Brush; + mappings[1426] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1427] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1428] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1429] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1430] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1431] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1432] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1433] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1434] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1435] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1436] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1437] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1438] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1439] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1440] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1441] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1442] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1443] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1444] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1445] = ItemType.AnglerPotterySherd; + mappings[1446] = ItemType.ArcherPotterySherd; + mappings[1447] = ItemType.ArmsUpPotterySherd; + mappings[1448] = ItemType.BladePotterySherd; + mappings[1449] = ItemType.BrewerPotterySherd; + mappings[1450] = ItemType.BurnPotterySherd; + mappings[1451] = ItemType.DangerPotterySherd; + mappings[1452] = ItemType.ExplorerPotterySherd; + mappings[1453] = ItemType.FlowPotterySherd; + mappings[1454] = ItemType.FriendPotterySherd; + mappings[1455] = ItemType.GusterPotterySherd; + mappings[1456] = ItemType.HeartPotterySherd; + mappings[1457] = ItemType.HeartbreakPotterySherd; + mappings[1458] = ItemType.HowlPotterySherd; + mappings[1459] = ItemType.MinerPotterySherd; + mappings[1460] = ItemType.MournerPotterySherd; + mappings[1461] = ItemType.PlentyPotterySherd; + mappings[1462] = ItemType.PrizePotterySherd; + mappings[1463] = ItemType.ScrapePotterySherd; + mappings[1464] = ItemType.SheafPotterySherd; + mappings[1465] = ItemType.ShelterPotterySherd; + mappings[1466] = ItemType.SkullPotterySherd; + mappings[1467] = ItemType.SnortPotterySherd; + mappings[1468] = ItemType.CopperGrate; + mappings[1469] = ItemType.ExposedCopperGrate; + mappings[1470] = ItemType.WeatheredCopperGrate; + mappings[1471] = ItemType.OxidizedCopperGrate; + mappings[1472] = ItemType.WaxedCopperGrate; + mappings[1473] = ItemType.WaxedExposedCopperGrate; + mappings[1474] = ItemType.WaxedWeatheredCopperGrate; + mappings[1475] = ItemType.WaxedOxidizedCopperGrate; + mappings[1476] = ItemType.CopperBulb; + mappings[1477] = ItemType.ExposedCopperBulb; + mappings[1478] = ItemType.WeatheredCopperBulb; + mappings[1479] = ItemType.OxidizedCopperBulb; + mappings[1480] = ItemType.WaxedCopperBulb; + mappings[1481] = ItemType.WaxedExposedCopperBulb; + mappings[1482] = ItemType.WaxedWeatheredCopperBulb; + mappings[1483] = ItemType.WaxedOxidizedCopperBulb; + mappings[1484] = ItemType.CopperChest; + mappings[1485] = ItemType.ExposedCopperChest; + mappings[1486] = ItemType.WeatheredCopperChest; + mappings[1487] = ItemType.OxidizedCopperChest; + mappings[1488] = ItemType.WaxedCopperChest; + mappings[1489] = ItemType.WaxedExposedCopperChest; + mappings[1490] = ItemType.WaxedWeatheredCopperChest; + mappings[1491] = ItemType.WaxedOxidizedCopperChest; + mappings[1492] = ItemType.CopperGolemStatue; + mappings[1493] = ItemType.ExposedCopperGolemStatue; + mappings[1494] = ItemType.WeatheredCopperGolemStatue; + mappings[1495] = ItemType.OxidizedCopperGolemStatue; + mappings[1496] = ItemType.WaxedCopperGolemStatue; + mappings[1497] = ItemType.WaxedExposedCopperGolemStatue; + mappings[1498] = ItemType.WaxedWeatheredCopperGolemStatue; + mappings[1499] = ItemType.WaxedOxidizedCopperGolemStatue; + mappings[1500] = ItemType.TrialSpawner; + mappings[1501] = ItemType.TrialKey; + mappings[1502] = ItemType.OminousTrialKey; + mappings[1503] = ItemType.Vault; + mappings[1504] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1212.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1212.cs new file mode 100644 index 00000000..42edc7fd --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1212.cs @@ -0,0 +1,1393 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1212 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1212() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Azalea; + mappings[205] = ItemType.FloweringAzalea; + mappings[206] = ItemType.DeadBush; + mappings[207] = ItemType.Seagrass; + mappings[208] = ItemType.SeaPickle; + mappings[209] = ItemType.WhiteWool; + mappings[210] = ItemType.OrangeWool; + mappings[211] = ItemType.MagentaWool; + mappings[212] = ItemType.LightBlueWool; + mappings[213] = ItemType.YellowWool; + mappings[214] = ItemType.LimeWool; + mappings[215] = ItemType.PinkWool; + mappings[216] = ItemType.GrayWool; + mappings[217] = ItemType.LightGrayWool; + mappings[218] = ItemType.CyanWool; + mappings[219] = ItemType.PurpleWool; + mappings[220] = ItemType.BlueWool; + mappings[221] = ItemType.BrownWool; + mappings[222] = ItemType.GreenWool; + mappings[223] = ItemType.RedWool; + mappings[224] = ItemType.BlackWool; + mappings[225] = ItemType.Dandelion; + mappings[226] = ItemType.Poppy; + mappings[227] = ItemType.BlueOrchid; + mappings[228] = ItemType.Allium; + mappings[229] = ItemType.AzureBluet; + mappings[230] = ItemType.RedTulip; + mappings[231] = ItemType.OrangeTulip; + mappings[232] = ItemType.WhiteTulip; + mappings[233] = ItemType.PinkTulip; + mappings[234] = ItemType.OxeyeDaisy; + mappings[235] = ItemType.Cornflower; + mappings[236] = ItemType.LilyOfTheValley; + mappings[237] = ItemType.WitherRose; + mappings[238] = ItemType.Torchflower; + mappings[239] = ItemType.PitcherPlant; + mappings[240] = ItemType.SporeBlossom; + mappings[241] = ItemType.BrownMushroom; + mappings[242] = ItemType.RedMushroom; + mappings[243] = ItemType.CrimsonFungus; + mappings[244] = ItemType.WarpedFungus; + mappings[245] = ItemType.CrimsonRoots; + mappings[246] = ItemType.WarpedRoots; + mappings[247] = ItemType.NetherSprouts; + mappings[248] = ItemType.WeepingVines; + mappings[249] = ItemType.TwistingVines; + mappings[250] = ItemType.SugarCane; + mappings[251] = ItemType.Kelp; + mappings[252] = ItemType.PinkPetals; + mappings[253] = ItemType.MossCarpet; + mappings[254] = ItemType.MossBlock; + mappings[255] = ItemType.PaleMossCarpet; + mappings[256] = ItemType.PaleHangingMoss; + mappings[257] = ItemType.PaleMossBlock; + mappings[258] = ItemType.HangingRoots; + mappings[259] = ItemType.BigDripleaf; + mappings[260] = ItemType.SmallDripleaf; + mappings[261] = ItemType.Bamboo; + mappings[262] = ItemType.OakSlab; + mappings[263] = ItemType.SpruceSlab; + mappings[264] = ItemType.BirchSlab; + mappings[265] = ItemType.JungleSlab; + mappings[266] = ItemType.AcaciaSlab; + mappings[267] = ItemType.CherrySlab; + mappings[268] = ItemType.DarkOakSlab; + mappings[269] = ItemType.PaleOakSlab; + mappings[270] = ItemType.MangroveSlab; + mappings[271] = ItemType.BambooSlab; + mappings[272] = ItemType.BambooMosaicSlab; + mappings[273] = ItemType.CrimsonSlab; + mappings[274] = ItemType.WarpedSlab; + mappings[275] = ItemType.StoneSlab; + mappings[276] = ItemType.SmoothStoneSlab; + mappings[277] = ItemType.SandstoneSlab; + mappings[278] = ItemType.CutSandstoneSlab; + mappings[279] = ItemType.PetrifiedOakSlab; + mappings[280] = ItemType.CobblestoneSlab; + mappings[281] = ItemType.BrickSlab; + mappings[282] = ItemType.StoneBrickSlab; + mappings[283] = ItemType.MudBrickSlab; + mappings[284] = ItemType.NetherBrickSlab; + mappings[285] = ItemType.QuartzSlab; + mappings[286] = ItemType.RedSandstoneSlab; + mappings[287] = ItemType.CutRedSandstoneSlab; + mappings[288] = ItemType.PurpurSlab; + mappings[289] = ItemType.PrismarineSlab; + mappings[290] = ItemType.PrismarineBrickSlab; + mappings[291] = ItemType.DarkPrismarineSlab; + mappings[292] = ItemType.SmoothQuartz; + mappings[293] = ItemType.SmoothRedSandstone; + mappings[294] = ItemType.SmoothSandstone; + mappings[295] = ItemType.SmoothStone; + mappings[296] = ItemType.Bricks; + mappings[297] = ItemType.Bookshelf; + mappings[298] = ItemType.ChiseledBookshelf; + mappings[299] = ItemType.DecoratedPot; + mappings[300] = ItemType.MossyCobblestone; + mappings[301] = ItemType.Obsidian; + mappings[302] = ItemType.Torch; + mappings[303] = ItemType.EndRod; + mappings[304] = ItemType.ChorusPlant; + mappings[305] = ItemType.ChorusFlower; + mappings[306] = ItemType.PurpurBlock; + mappings[307] = ItemType.PurpurPillar; + mappings[308] = ItemType.PurpurStairs; + mappings[309] = ItemType.Spawner; + mappings[310] = ItemType.CreakingHeart; + mappings[311] = ItemType.Chest; + mappings[312] = ItemType.CraftingTable; + mappings[313] = ItemType.Farmland; + mappings[314] = ItemType.Furnace; + mappings[315] = ItemType.Ladder; + mappings[316] = ItemType.CobblestoneStairs; + mappings[317] = ItemType.Snow; + mappings[318] = ItemType.Ice; + mappings[319] = ItemType.SnowBlock; + mappings[320] = ItemType.Cactus; + mappings[321] = ItemType.Clay; + mappings[322] = ItemType.Jukebox; + mappings[323] = ItemType.OakFence; + mappings[324] = ItemType.SpruceFence; + mappings[325] = ItemType.BirchFence; + mappings[326] = ItemType.JungleFence; + mappings[327] = ItemType.AcaciaFence; + mappings[328] = ItemType.CherryFence; + mappings[329] = ItemType.DarkOakFence; + mappings[330] = ItemType.PaleOakFence; + mappings[331] = ItemType.MangroveFence; + mappings[332] = ItemType.BambooFence; + mappings[333] = ItemType.CrimsonFence; + mappings[334] = ItemType.WarpedFence; + mappings[335] = ItemType.Pumpkin; + mappings[336] = ItemType.CarvedPumpkin; + mappings[337] = ItemType.JackOLantern; + mappings[338] = ItemType.Netherrack; + mappings[339] = ItemType.SoulSand; + mappings[340] = ItemType.SoulSoil; + mappings[341] = ItemType.Basalt; + mappings[342] = ItemType.PolishedBasalt; + mappings[343] = ItemType.SmoothBasalt; + mappings[344] = ItemType.SoulTorch; + mappings[345] = ItemType.Glowstone; + mappings[346] = ItemType.InfestedStone; + mappings[347] = ItemType.InfestedCobblestone; + mappings[348] = ItemType.InfestedStoneBricks; + mappings[349] = ItemType.InfestedMossyStoneBricks; + mappings[350] = ItemType.InfestedCrackedStoneBricks; + mappings[351] = ItemType.InfestedChiseledStoneBricks; + mappings[352] = ItemType.InfestedDeepslate; + mappings[353] = ItemType.StoneBricks; + mappings[354] = ItemType.MossyStoneBricks; + mappings[355] = ItemType.CrackedStoneBricks; + mappings[356] = ItemType.ChiseledStoneBricks; + mappings[357] = ItemType.PackedMud; + mappings[358] = ItemType.MudBricks; + mappings[359] = ItemType.DeepslateBricks; + mappings[360] = ItemType.CrackedDeepslateBricks; + mappings[361] = ItemType.DeepslateTiles; + mappings[362] = ItemType.CrackedDeepslateTiles; + mappings[363] = ItemType.ChiseledDeepslate; + mappings[364] = ItemType.ReinforcedDeepslate; + mappings[365] = ItemType.BrownMushroomBlock; + mappings[366] = ItemType.RedMushroomBlock; + mappings[367] = ItemType.MushroomStem; + mappings[368] = ItemType.IronBars; + mappings[369] = ItemType.Chain; + mappings[370] = ItemType.GlassPane; + mappings[371] = ItemType.Melon; + mappings[372] = ItemType.Vine; + mappings[373] = ItemType.GlowLichen; + mappings[374] = ItemType.BrickStairs; + mappings[375] = ItemType.StoneBrickStairs; + mappings[376] = ItemType.MudBrickStairs; + mappings[377] = ItemType.Mycelium; + mappings[378] = ItemType.LilyPad; + mappings[379] = ItemType.NetherBricks; + mappings[380] = ItemType.CrackedNetherBricks; + mappings[381] = ItemType.ChiseledNetherBricks; + mappings[382] = ItemType.NetherBrickFence; + mappings[383] = ItemType.NetherBrickStairs; + mappings[384] = ItemType.Sculk; + mappings[385] = ItemType.SculkVein; + mappings[386] = ItemType.SculkCatalyst; + mappings[387] = ItemType.SculkShrieker; + mappings[388] = ItemType.EnchantingTable; + mappings[389] = ItemType.EndPortalFrame; + mappings[390] = ItemType.EndStone; + mappings[391] = ItemType.EndStoneBricks; + mappings[392] = ItemType.DragonEgg; + mappings[393] = ItemType.SandstoneStairs; + mappings[394] = ItemType.EnderChest; + mappings[395] = ItemType.EmeraldBlock; + mappings[396] = ItemType.OakStairs; + mappings[397] = ItemType.SpruceStairs; + mappings[398] = ItemType.BirchStairs; + mappings[399] = ItemType.JungleStairs; + mappings[400] = ItemType.AcaciaStairs; + mappings[401] = ItemType.CherryStairs; + mappings[402] = ItemType.DarkOakStairs; + mappings[403] = ItemType.PaleOakStairs; + mappings[404] = ItemType.MangroveStairs; + mappings[405] = ItemType.BambooStairs; + mappings[406] = ItemType.BambooMosaicStairs; + mappings[407] = ItemType.CrimsonStairs; + mappings[408] = ItemType.WarpedStairs; + mappings[409] = ItemType.CommandBlock; + mappings[410] = ItemType.Beacon; + mappings[411] = ItemType.CobblestoneWall; + mappings[412] = ItemType.MossyCobblestoneWall; + mappings[413] = ItemType.BrickWall; + mappings[414] = ItemType.PrismarineWall; + mappings[415] = ItemType.RedSandstoneWall; + mappings[416] = ItemType.MossyStoneBrickWall; + mappings[417] = ItemType.GraniteWall; + mappings[418] = ItemType.StoneBrickWall; + mappings[419] = ItemType.MudBrickWall; + mappings[420] = ItemType.NetherBrickWall; + mappings[421] = ItemType.AndesiteWall; + mappings[422] = ItemType.RedNetherBrickWall; + mappings[423] = ItemType.SandstoneWall; + mappings[424] = ItemType.EndStoneBrickWall; + mappings[425] = ItemType.DioriteWall; + mappings[426] = ItemType.BlackstoneWall; + mappings[427] = ItemType.PolishedBlackstoneWall; + mappings[428] = ItemType.PolishedBlackstoneBrickWall; + mappings[429] = ItemType.CobbledDeepslateWall; + mappings[430] = ItemType.PolishedDeepslateWall; + mappings[431] = ItemType.DeepslateBrickWall; + mappings[432] = ItemType.DeepslateTileWall; + mappings[433] = ItemType.Anvil; + mappings[434] = ItemType.ChippedAnvil; + mappings[435] = ItemType.DamagedAnvil; + mappings[436] = ItemType.ChiseledQuartzBlock; + mappings[437] = ItemType.QuartzBlock; + mappings[438] = ItemType.QuartzBricks; + mappings[439] = ItemType.QuartzPillar; + mappings[440] = ItemType.QuartzStairs; + mappings[441] = ItemType.WhiteTerracotta; + mappings[442] = ItemType.OrangeTerracotta; + mappings[443] = ItemType.MagentaTerracotta; + mappings[444] = ItemType.LightBlueTerracotta; + mappings[445] = ItemType.YellowTerracotta; + mappings[446] = ItemType.LimeTerracotta; + mappings[447] = ItemType.PinkTerracotta; + mappings[448] = ItemType.GrayTerracotta; + mappings[449] = ItemType.LightGrayTerracotta; + mappings[450] = ItemType.CyanTerracotta; + mappings[451] = ItemType.PurpleTerracotta; + mappings[452] = ItemType.BlueTerracotta; + mappings[453] = ItemType.BrownTerracotta; + mappings[454] = ItemType.GreenTerracotta; + mappings[455] = ItemType.RedTerracotta; + mappings[456] = ItemType.BlackTerracotta; + mappings[457] = ItemType.Barrier; + mappings[458] = ItemType.Light; + mappings[459] = ItemType.HayBlock; + mappings[460] = ItemType.WhiteCarpet; + mappings[461] = ItemType.OrangeCarpet; + mappings[462] = ItemType.MagentaCarpet; + mappings[463] = ItemType.LightBlueCarpet; + mappings[464] = ItemType.YellowCarpet; + mappings[465] = ItemType.LimeCarpet; + mappings[466] = ItemType.PinkCarpet; + mappings[467] = ItemType.GrayCarpet; + mappings[468] = ItemType.LightGrayCarpet; + mappings[469] = ItemType.CyanCarpet; + mappings[470] = ItemType.PurpleCarpet; + mappings[471] = ItemType.BlueCarpet; + mappings[472] = ItemType.BrownCarpet; + mappings[473] = ItemType.GreenCarpet; + mappings[474] = ItemType.RedCarpet; + mappings[475] = ItemType.BlackCarpet; + mappings[476] = ItemType.Terracotta; + mappings[477] = ItemType.PackedIce; + mappings[478] = ItemType.DirtPath; + mappings[479] = ItemType.Sunflower; + mappings[480] = ItemType.Lilac; + mappings[481] = ItemType.RoseBush; + mappings[482] = ItemType.Peony; + mappings[483] = ItemType.TallGrass; + mappings[484] = ItemType.LargeFern; + mappings[485] = ItemType.WhiteStainedGlass; + mappings[486] = ItemType.OrangeStainedGlass; + mappings[487] = ItemType.MagentaStainedGlass; + mappings[488] = ItemType.LightBlueStainedGlass; + mappings[489] = ItemType.YellowStainedGlass; + mappings[490] = ItemType.LimeStainedGlass; + mappings[491] = ItemType.PinkStainedGlass; + mappings[492] = ItemType.GrayStainedGlass; + mappings[493] = ItemType.LightGrayStainedGlass; + mappings[494] = ItemType.CyanStainedGlass; + mappings[495] = ItemType.PurpleStainedGlass; + mappings[496] = ItemType.BlueStainedGlass; + mappings[497] = ItemType.BrownStainedGlass; + mappings[498] = ItemType.GreenStainedGlass; + mappings[499] = ItemType.RedStainedGlass; + mappings[500] = ItemType.BlackStainedGlass; + mappings[501] = ItemType.WhiteStainedGlassPane; + mappings[502] = ItemType.OrangeStainedGlassPane; + mappings[503] = ItemType.MagentaStainedGlassPane; + mappings[504] = ItemType.LightBlueStainedGlassPane; + mappings[505] = ItemType.YellowStainedGlassPane; + mappings[506] = ItemType.LimeStainedGlassPane; + mappings[507] = ItemType.PinkStainedGlassPane; + mappings[508] = ItemType.GrayStainedGlassPane; + mappings[509] = ItemType.LightGrayStainedGlassPane; + mappings[510] = ItemType.CyanStainedGlassPane; + mappings[511] = ItemType.PurpleStainedGlassPane; + mappings[512] = ItemType.BlueStainedGlassPane; + mappings[513] = ItemType.BrownStainedGlassPane; + mappings[514] = ItemType.GreenStainedGlassPane; + mappings[515] = ItemType.RedStainedGlassPane; + mappings[516] = ItemType.BlackStainedGlassPane; + mappings[517] = ItemType.Prismarine; + mappings[518] = ItemType.PrismarineBricks; + mappings[519] = ItemType.DarkPrismarine; + mappings[520] = ItemType.PrismarineStairs; + mappings[521] = ItemType.PrismarineBrickStairs; + mappings[522] = ItemType.DarkPrismarineStairs; + mappings[523] = ItemType.SeaLantern; + mappings[524] = ItemType.RedSandstone; + mappings[525] = ItemType.ChiseledRedSandstone; + mappings[526] = ItemType.CutRedSandstone; + mappings[527] = ItemType.RedSandstoneStairs; + mappings[528] = ItemType.RepeatingCommandBlock; + mappings[529] = ItemType.ChainCommandBlock; + mappings[530] = ItemType.MagmaBlock; + mappings[531] = ItemType.NetherWartBlock; + mappings[532] = ItemType.WarpedWartBlock; + mappings[533] = ItemType.RedNetherBricks; + mappings[534] = ItemType.BoneBlock; + mappings[535] = ItemType.StructureVoid; + mappings[536] = ItemType.ShulkerBox; + mappings[537] = ItemType.WhiteShulkerBox; + mappings[538] = ItemType.OrangeShulkerBox; + mappings[539] = ItemType.MagentaShulkerBox; + mappings[540] = ItemType.LightBlueShulkerBox; + mappings[541] = ItemType.YellowShulkerBox; + mappings[542] = ItemType.LimeShulkerBox; + mappings[543] = ItemType.PinkShulkerBox; + mappings[544] = ItemType.GrayShulkerBox; + mappings[545] = ItemType.LightGrayShulkerBox; + mappings[546] = ItemType.CyanShulkerBox; + mappings[547] = ItemType.PurpleShulkerBox; + mappings[548] = ItemType.BlueShulkerBox; + mappings[549] = ItemType.BrownShulkerBox; + mappings[550] = ItemType.GreenShulkerBox; + mappings[551] = ItemType.RedShulkerBox; + mappings[552] = ItemType.BlackShulkerBox; + mappings[553] = ItemType.WhiteGlazedTerracotta; + mappings[554] = ItemType.OrangeGlazedTerracotta; + mappings[555] = ItemType.MagentaGlazedTerracotta; + mappings[556] = ItemType.LightBlueGlazedTerracotta; + mappings[557] = ItemType.YellowGlazedTerracotta; + mappings[558] = ItemType.LimeGlazedTerracotta; + mappings[559] = ItemType.PinkGlazedTerracotta; + mappings[560] = ItemType.GrayGlazedTerracotta; + mappings[561] = ItemType.LightGrayGlazedTerracotta; + mappings[562] = ItemType.CyanGlazedTerracotta; + mappings[563] = ItemType.PurpleGlazedTerracotta; + mappings[564] = ItemType.BlueGlazedTerracotta; + mappings[565] = ItemType.BrownGlazedTerracotta; + mappings[566] = ItemType.GreenGlazedTerracotta; + mappings[567] = ItemType.RedGlazedTerracotta; + mappings[568] = ItemType.BlackGlazedTerracotta; + mappings[569] = ItemType.WhiteConcrete; + mappings[570] = ItemType.OrangeConcrete; + mappings[571] = ItemType.MagentaConcrete; + mappings[572] = ItemType.LightBlueConcrete; + mappings[573] = ItemType.YellowConcrete; + mappings[574] = ItemType.LimeConcrete; + mappings[575] = ItemType.PinkConcrete; + mappings[576] = ItemType.GrayConcrete; + mappings[577] = ItemType.LightGrayConcrete; + mappings[578] = ItemType.CyanConcrete; + mappings[579] = ItemType.PurpleConcrete; + mappings[580] = ItemType.BlueConcrete; + mappings[581] = ItemType.BrownConcrete; + mappings[582] = ItemType.GreenConcrete; + mappings[583] = ItemType.RedConcrete; + mappings[584] = ItemType.BlackConcrete; + mappings[585] = ItemType.WhiteConcretePowder; + mappings[586] = ItemType.OrangeConcretePowder; + mappings[587] = ItemType.MagentaConcretePowder; + mappings[588] = ItemType.LightBlueConcretePowder; + mappings[589] = ItemType.YellowConcretePowder; + mappings[590] = ItemType.LimeConcretePowder; + mappings[591] = ItemType.PinkConcretePowder; + mappings[592] = ItemType.GrayConcretePowder; + mappings[593] = ItemType.LightGrayConcretePowder; + mappings[594] = ItemType.CyanConcretePowder; + mappings[595] = ItemType.PurpleConcretePowder; + mappings[596] = ItemType.BlueConcretePowder; + mappings[597] = ItemType.BrownConcretePowder; + mappings[598] = ItemType.GreenConcretePowder; + mappings[599] = ItemType.RedConcretePowder; + mappings[600] = ItemType.BlackConcretePowder; + mappings[601] = ItemType.TurtleEgg; + mappings[602] = ItemType.SnifferEgg; + mappings[603] = ItemType.DeadTubeCoralBlock; + mappings[604] = ItemType.DeadBrainCoralBlock; + mappings[605] = ItemType.DeadBubbleCoralBlock; + mappings[606] = ItemType.DeadFireCoralBlock; + mappings[607] = ItemType.DeadHornCoralBlock; + mappings[608] = ItemType.TubeCoralBlock; + mappings[609] = ItemType.BrainCoralBlock; + mappings[610] = ItemType.BubbleCoralBlock; + mappings[611] = ItemType.FireCoralBlock; + mappings[612] = ItemType.HornCoralBlock; + mappings[613] = ItemType.TubeCoral; + mappings[614] = ItemType.BrainCoral; + mappings[615] = ItemType.BubbleCoral; + mappings[616] = ItemType.FireCoral; + mappings[617] = ItemType.HornCoral; + mappings[618] = ItemType.DeadBrainCoral; + mappings[619] = ItemType.DeadBubbleCoral; + mappings[620] = ItemType.DeadFireCoral; + mappings[621] = ItemType.DeadHornCoral; + mappings[622] = ItemType.DeadTubeCoral; + mappings[623] = ItemType.TubeCoralFan; + mappings[624] = ItemType.BrainCoralFan; + mappings[625] = ItemType.BubbleCoralFan; + mappings[626] = ItemType.FireCoralFan; + mappings[627] = ItemType.HornCoralFan; + mappings[628] = ItemType.DeadTubeCoralFan; + mappings[629] = ItemType.DeadBrainCoralFan; + mappings[630] = ItemType.DeadBubbleCoralFan; + mappings[631] = ItemType.DeadFireCoralFan; + mappings[632] = ItemType.DeadHornCoralFan; + mappings[633] = ItemType.BlueIce; + mappings[634] = ItemType.Conduit; + mappings[635] = ItemType.PolishedGraniteStairs; + mappings[636] = ItemType.SmoothRedSandstoneStairs; + mappings[637] = ItemType.MossyStoneBrickStairs; + mappings[638] = ItemType.PolishedDioriteStairs; + mappings[639] = ItemType.MossyCobblestoneStairs; + mappings[640] = ItemType.EndStoneBrickStairs; + mappings[641] = ItemType.StoneStairs; + mappings[642] = ItemType.SmoothSandstoneStairs; + mappings[643] = ItemType.SmoothQuartzStairs; + mappings[644] = ItemType.GraniteStairs; + mappings[645] = ItemType.AndesiteStairs; + mappings[646] = ItemType.RedNetherBrickStairs; + mappings[647] = ItemType.PolishedAndesiteStairs; + mappings[648] = ItemType.DioriteStairs; + mappings[649] = ItemType.CobbledDeepslateStairs; + mappings[650] = ItemType.PolishedDeepslateStairs; + mappings[651] = ItemType.DeepslateBrickStairs; + mappings[652] = ItemType.DeepslateTileStairs; + mappings[653] = ItemType.PolishedGraniteSlab; + mappings[654] = ItemType.SmoothRedSandstoneSlab; + mappings[655] = ItemType.MossyStoneBrickSlab; + mappings[656] = ItemType.PolishedDioriteSlab; + mappings[657] = ItemType.MossyCobblestoneSlab; + mappings[658] = ItemType.EndStoneBrickSlab; + mappings[659] = ItemType.SmoothSandstoneSlab; + mappings[660] = ItemType.SmoothQuartzSlab; + mappings[661] = ItemType.GraniteSlab; + mappings[662] = ItemType.AndesiteSlab; + mappings[663] = ItemType.RedNetherBrickSlab; + mappings[664] = ItemType.PolishedAndesiteSlab; + mappings[665] = ItemType.DioriteSlab; + mappings[666] = ItemType.CobbledDeepslateSlab; + mappings[667] = ItemType.PolishedDeepslateSlab; + mappings[668] = ItemType.DeepslateBrickSlab; + mappings[669] = ItemType.DeepslateTileSlab; + mappings[670] = ItemType.Scaffolding; + mappings[671] = ItemType.Redstone; + mappings[672] = ItemType.RedstoneTorch; + mappings[673] = ItemType.RedstoneBlock; + mappings[674] = ItemType.Repeater; + mappings[675] = ItemType.Comparator; + mappings[676] = ItemType.Piston; + mappings[677] = ItemType.StickyPiston; + mappings[678] = ItemType.SlimeBlock; + mappings[679] = ItemType.HoneyBlock; + mappings[680] = ItemType.Observer; + mappings[681] = ItemType.Hopper; + mappings[682] = ItemType.Dispenser; + mappings[683] = ItemType.Dropper; + mappings[684] = ItemType.Lectern; + mappings[685] = ItemType.Target; + mappings[686] = ItemType.Lever; + mappings[687] = ItemType.LightningRod; + mappings[688] = ItemType.DaylightDetector; + mappings[689] = ItemType.SculkSensor; + mappings[690] = ItemType.CalibratedSculkSensor; + mappings[691] = ItemType.TripwireHook; + mappings[692] = ItemType.TrappedChest; + mappings[693] = ItemType.Tnt; + mappings[694] = ItemType.RedstoneLamp; + mappings[695] = ItemType.NoteBlock; + mappings[696] = ItemType.StoneButton; + mappings[697] = ItemType.PolishedBlackstoneButton; + mappings[698] = ItemType.OakButton; + mappings[699] = ItemType.SpruceButton; + mappings[700] = ItemType.BirchButton; + mappings[701] = ItemType.JungleButton; + mappings[702] = ItemType.AcaciaButton; + mappings[703] = ItemType.CherryButton; + mappings[704] = ItemType.DarkOakButton; + mappings[705] = ItemType.PaleOakButton; + mappings[706] = ItemType.MangroveButton; + mappings[707] = ItemType.BambooButton; + mappings[708] = ItemType.CrimsonButton; + mappings[709] = ItemType.WarpedButton; + mappings[710] = ItemType.StonePressurePlate; + mappings[711] = ItemType.PolishedBlackstonePressurePlate; + mappings[712] = ItemType.LightWeightedPressurePlate; + mappings[713] = ItemType.HeavyWeightedPressurePlate; + mappings[714] = ItemType.OakPressurePlate; + mappings[715] = ItemType.SprucePressurePlate; + mappings[716] = ItemType.BirchPressurePlate; + mappings[717] = ItemType.JunglePressurePlate; + mappings[718] = ItemType.AcaciaPressurePlate; + mappings[719] = ItemType.CherryPressurePlate; + mappings[720] = ItemType.DarkOakPressurePlate; + mappings[721] = ItemType.PaleOakPressurePlate; + mappings[722] = ItemType.MangrovePressurePlate; + mappings[723] = ItemType.BambooPressurePlate; + mappings[724] = ItemType.CrimsonPressurePlate; + mappings[725] = ItemType.WarpedPressurePlate; + mappings[726] = ItemType.IronDoor; + mappings[727] = ItemType.OakDoor; + mappings[728] = ItemType.SpruceDoor; + mappings[729] = ItemType.BirchDoor; + mappings[730] = ItemType.JungleDoor; + mappings[731] = ItemType.AcaciaDoor; + mappings[732] = ItemType.CherryDoor; + mappings[733] = ItemType.DarkOakDoor; + mappings[734] = ItemType.PaleOakDoor; + mappings[735] = ItemType.MangroveDoor; + mappings[736] = ItemType.BambooDoor; + mappings[737] = ItemType.CrimsonDoor; + mappings[738] = ItemType.WarpedDoor; + mappings[739] = ItemType.CopperDoor; + mappings[740] = ItemType.ExposedCopperDoor; + mappings[741] = ItemType.WeatheredCopperDoor; + mappings[742] = ItemType.OxidizedCopperDoor; + mappings[743] = ItemType.WaxedCopperDoor; + mappings[744] = ItemType.WaxedExposedCopperDoor; + mappings[745] = ItemType.WaxedWeatheredCopperDoor; + mappings[746] = ItemType.WaxedOxidizedCopperDoor; + mappings[747] = ItemType.IronTrapdoor; + mappings[748] = ItemType.OakTrapdoor; + mappings[749] = ItemType.SpruceTrapdoor; + mappings[750] = ItemType.BirchTrapdoor; + mappings[751] = ItemType.JungleTrapdoor; + mappings[752] = ItemType.AcaciaTrapdoor; + mappings[753] = ItemType.CherryTrapdoor; + mappings[754] = ItemType.DarkOakTrapdoor; + mappings[755] = ItemType.PaleOakTrapdoor; + mappings[756] = ItemType.MangroveTrapdoor; + mappings[757] = ItemType.BambooTrapdoor; + mappings[758] = ItemType.CrimsonTrapdoor; + mappings[759] = ItemType.WarpedTrapdoor; + mappings[760] = ItemType.CopperTrapdoor; + mappings[761] = ItemType.ExposedCopperTrapdoor; + mappings[762] = ItemType.WeatheredCopperTrapdoor; + mappings[763] = ItemType.OxidizedCopperTrapdoor; + mappings[764] = ItemType.WaxedCopperTrapdoor; + mappings[765] = ItemType.WaxedExposedCopperTrapdoor; + mappings[766] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[767] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[768] = ItemType.OakFenceGate; + mappings[769] = ItemType.SpruceFenceGate; + mappings[770] = ItemType.BirchFenceGate; + mappings[771] = ItemType.JungleFenceGate; + mappings[772] = ItemType.AcaciaFenceGate; + mappings[773] = ItemType.CherryFenceGate; + mappings[774] = ItemType.DarkOakFenceGate; + mappings[775] = ItemType.PaleOakFenceGate; + mappings[776] = ItemType.MangroveFenceGate; + mappings[777] = ItemType.BambooFenceGate; + mappings[778] = ItemType.CrimsonFenceGate; + mappings[779] = ItemType.WarpedFenceGate; + mappings[780] = ItemType.PoweredRail; + mappings[781] = ItemType.DetectorRail; + mappings[782] = ItemType.Rail; + mappings[783] = ItemType.ActivatorRail; + mappings[784] = ItemType.Saddle; + mappings[785] = ItemType.Minecart; + mappings[786] = ItemType.ChestMinecart; + mappings[787] = ItemType.FurnaceMinecart; + mappings[788] = ItemType.TntMinecart; + mappings[789] = ItemType.HopperMinecart; + mappings[790] = ItemType.CarrotOnAStick; + mappings[791] = ItemType.WarpedFungusOnAStick; + mappings[792] = ItemType.PhantomMembrane; + mappings[793] = ItemType.Elytra; + mappings[794] = ItemType.OakBoat; + mappings[795] = ItemType.OakChestBoat; + mappings[796] = ItemType.SpruceBoat; + mappings[797] = ItemType.SpruceChestBoat; + mappings[798] = ItemType.BirchBoat; + mappings[799] = ItemType.BirchChestBoat; + mappings[800] = ItemType.JungleBoat; + mappings[801] = ItemType.JungleChestBoat; + mappings[802] = ItemType.AcaciaBoat; + mappings[803] = ItemType.AcaciaChestBoat; + mappings[804] = ItemType.CherryBoat; + mappings[805] = ItemType.CherryChestBoat; + mappings[806] = ItemType.DarkOakBoat; + mappings[807] = ItemType.DarkOakChestBoat; + mappings[808] = ItemType.PaleOakBoat; + mappings[809] = ItemType.PaleOakChestBoat; + mappings[810] = ItemType.MangroveBoat; + mappings[811] = ItemType.MangroveChestBoat; + mappings[812] = ItemType.BambooRaft; + mappings[813] = ItemType.BambooChestRaft; + mappings[814] = ItemType.StructureBlock; + mappings[815] = ItemType.Jigsaw; + mappings[816] = ItemType.TurtleHelmet; + mappings[817] = ItemType.TurtleScute; + mappings[818] = ItemType.ArmadilloScute; + mappings[819] = ItemType.WolfArmor; + mappings[820] = ItemType.FlintAndSteel; + mappings[821] = ItemType.Bowl; + mappings[822] = ItemType.Apple; + mappings[823] = ItemType.Bow; + mappings[824] = ItemType.Arrow; + mappings[825] = ItemType.Coal; + mappings[826] = ItemType.Charcoal; + mappings[827] = ItemType.Diamond; + mappings[828] = ItemType.Emerald; + mappings[829] = ItemType.LapisLazuli; + mappings[830] = ItemType.Quartz; + mappings[831] = ItemType.AmethystShard; + mappings[832] = ItemType.RawIron; + mappings[833] = ItemType.IronIngot; + mappings[834] = ItemType.RawCopper; + mappings[835] = ItemType.CopperIngot; + mappings[836] = ItemType.RawGold; + mappings[837] = ItemType.GoldIngot; + mappings[838] = ItemType.NetheriteIngot; + mappings[839] = ItemType.NetheriteScrap; + mappings[840] = ItemType.WoodenSword; + mappings[841] = ItemType.WoodenShovel; + mappings[842] = ItemType.WoodenPickaxe; + mappings[843] = ItemType.WoodenAxe; + mappings[844] = ItemType.WoodenHoe; + mappings[845] = ItemType.StoneSword; + mappings[846] = ItemType.StoneShovel; + mappings[847] = ItemType.StonePickaxe; + mappings[848] = ItemType.StoneAxe; + mappings[849] = ItemType.StoneHoe; + mappings[850] = ItemType.GoldenSword; + mappings[851] = ItemType.GoldenShovel; + mappings[852] = ItemType.GoldenPickaxe; + mappings[853] = ItemType.GoldenAxe; + mappings[854] = ItemType.GoldenHoe; + mappings[855] = ItemType.IronSword; + mappings[856] = ItemType.IronShovel; + mappings[857] = ItemType.IronPickaxe; + mappings[858] = ItemType.IronAxe; + mappings[859] = ItemType.IronHoe; + mappings[860] = ItemType.DiamondSword; + mappings[861] = ItemType.DiamondShovel; + mappings[862] = ItemType.DiamondPickaxe; + mappings[863] = ItemType.DiamondAxe; + mappings[864] = ItemType.DiamondHoe; + mappings[865] = ItemType.NetheriteSword; + mappings[866] = ItemType.NetheriteShovel; + mappings[867] = ItemType.NetheritePickaxe; + mappings[868] = ItemType.NetheriteAxe; + mappings[869] = ItemType.NetheriteHoe; + mappings[870] = ItemType.Stick; + mappings[871] = ItemType.MushroomStew; + mappings[872] = ItemType.String; + mappings[873] = ItemType.Feather; + mappings[874] = ItemType.Gunpowder; + mappings[875] = ItemType.WheatSeeds; + mappings[876] = ItemType.Wheat; + mappings[877] = ItemType.Bread; + mappings[878] = ItemType.LeatherHelmet; + mappings[879] = ItemType.LeatherChestplate; + mappings[880] = ItemType.LeatherLeggings; + mappings[881] = ItemType.LeatherBoots; + mappings[882] = ItemType.ChainmailHelmet; + mappings[883] = ItemType.ChainmailChestplate; + mappings[884] = ItemType.ChainmailLeggings; + mappings[885] = ItemType.ChainmailBoots; + mappings[886] = ItemType.IronHelmet; + mappings[887] = ItemType.IronChestplate; + mappings[888] = ItemType.IronLeggings; + mappings[889] = ItemType.IronBoots; + mappings[890] = ItemType.DiamondHelmet; + mappings[891] = ItemType.DiamondChestplate; + mappings[892] = ItemType.DiamondLeggings; + mappings[893] = ItemType.DiamondBoots; + mappings[894] = ItemType.GoldenHelmet; + mappings[895] = ItemType.GoldenChestplate; + mappings[896] = ItemType.GoldenLeggings; + mappings[897] = ItemType.GoldenBoots; + mappings[898] = ItemType.NetheriteHelmet; + mappings[899] = ItemType.NetheriteChestplate; + mappings[900] = ItemType.NetheriteLeggings; + mappings[901] = ItemType.NetheriteBoots; + mappings[902] = ItemType.Flint; + mappings[903] = ItemType.Porkchop; + mappings[904] = ItemType.CookedPorkchop; + mappings[905] = ItemType.Painting; + mappings[906] = ItemType.GoldenApple; + mappings[907] = ItemType.EnchantedGoldenApple; + mappings[908] = ItemType.OakSign; + mappings[909] = ItemType.SpruceSign; + mappings[910] = ItemType.BirchSign; + mappings[911] = ItemType.JungleSign; + mappings[912] = ItemType.AcaciaSign; + mappings[913] = ItemType.CherrySign; + mappings[914] = ItemType.DarkOakSign; + mappings[915] = ItemType.PaleOakSign; + mappings[916] = ItemType.MangroveSign; + mappings[917] = ItemType.BambooSign; + mappings[918] = ItemType.CrimsonSign; + mappings[919] = ItemType.WarpedSign; + mappings[920] = ItemType.OakHangingSign; + mappings[921] = ItemType.SpruceHangingSign; + mappings[922] = ItemType.BirchHangingSign; + mappings[923] = ItemType.JungleHangingSign; + mappings[924] = ItemType.AcaciaHangingSign; + mappings[925] = ItemType.CherryHangingSign; + mappings[926] = ItemType.DarkOakHangingSign; + mappings[927] = ItemType.PaleOakHangingSign; + mappings[928] = ItemType.MangroveHangingSign; + mappings[929] = ItemType.BambooHangingSign; + mappings[930] = ItemType.CrimsonHangingSign; + mappings[931] = ItemType.WarpedHangingSign; + mappings[932] = ItemType.Bucket; + mappings[933] = ItemType.WaterBucket; + mappings[934] = ItemType.LavaBucket; + mappings[935] = ItemType.PowderSnowBucket; + mappings[936] = ItemType.Snowball; + mappings[937] = ItemType.Leather; + mappings[938] = ItemType.MilkBucket; + mappings[939] = ItemType.PufferfishBucket; + mappings[940] = ItemType.SalmonBucket; + mappings[941] = ItemType.CodBucket; + mappings[942] = ItemType.TropicalFishBucket; + mappings[943] = ItemType.AxolotlBucket; + mappings[944] = ItemType.TadpoleBucket; + mappings[945] = ItemType.Brick; + mappings[946] = ItemType.ClayBall; + mappings[947] = ItemType.DriedKelpBlock; + mappings[948] = ItemType.Paper; + mappings[949] = ItemType.Book; + mappings[950] = ItemType.SlimeBall; + mappings[951] = ItemType.Egg; + mappings[952] = ItemType.Compass; + mappings[953] = ItemType.RecoveryCompass; + mappings[954] = ItemType.Bundle; + mappings[955] = ItemType.WhiteBundle; + mappings[956] = ItemType.OrangeBundle; + mappings[957] = ItemType.MagentaBundle; + mappings[958] = ItemType.LightBlueBundle; + mappings[959] = ItemType.YellowBundle; + mappings[960] = ItemType.LimeBundle; + mappings[961] = ItemType.PinkBundle; + mappings[962] = ItemType.GrayBundle; + mappings[963] = ItemType.LightGrayBundle; + mappings[964] = ItemType.CyanBundle; + mappings[965] = ItemType.PurpleBundle; + mappings[966] = ItemType.BlueBundle; + mappings[967] = ItemType.BrownBundle; + mappings[968] = ItemType.GreenBundle; + mappings[969] = ItemType.RedBundle; + mappings[970] = ItemType.BlackBundle; + mappings[971] = ItemType.FishingRod; + mappings[972] = ItemType.Clock; + mappings[973] = ItemType.Spyglass; + mappings[974] = ItemType.GlowstoneDust; + mappings[975] = ItemType.Cod; + mappings[976] = ItemType.Salmon; + mappings[977] = ItemType.TropicalFish; + mappings[978] = ItemType.Pufferfish; + mappings[979] = ItemType.CookedCod; + mappings[980] = ItemType.CookedSalmon; + mappings[981] = ItemType.InkSac; + mappings[982] = ItemType.GlowInkSac; + mappings[983] = ItemType.CocoaBeans; + mappings[984] = ItemType.WhiteDye; + mappings[985] = ItemType.OrangeDye; + mappings[986] = ItemType.MagentaDye; + mappings[987] = ItemType.LightBlueDye; + mappings[988] = ItemType.YellowDye; + mappings[989] = ItemType.LimeDye; + mappings[990] = ItemType.PinkDye; + mappings[991] = ItemType.GrayDye; + mappings[992] = ItemType.LightGrayDye; + mappings[993] = ItemType.CyanDye; + mappings[994] = ItemType.PurpleDye; + mappings[995] = ItemType.BlueDye; + mappings[996] = ItemType.BrownDye; + mappings[997] = ItemType.GreenDye; + mappings[998] = ItemType.RedDye; + mappings[999] = ItemType.BlackDye; + mappings[1000] = ItemType.BoneMeal; + mappings[1001] = ItemType.Bone; + mappings[1002] = ItemType.Sugar; + mappings[1003] = ItemType.Cake; + mappings[1004] = ItemType.WhiteBed; + mappings[1005] = ItemType.OrangeBed; + mappings[1006] = ItemType.MagentaBed; + mappings[1007] = ItemType.LightBlueBed; + mappings[1008] = ItemType.YellowBed; + mappings[1009] = ItemType.LimeBed; + mappings[1010] = ItemType.PinkBed; + mappings[1011] = ItemType.GrayBed; + mappings[1012] = ItemType.LightGrayBed; + mappings[1013] = ItemType.CyanBed; + mappings[1014] = ItemType.PurpleBed; + mappings[1015] = ItemType.BlueBed; + mappings[1016] = ItemType.BrownBed; + mappings[1017] = ItemType.GreenBed; + mappings[1018] = ItemType.RedBed; + mappings[1019] = ItemType.BlackBed; + mappings[1020] = ItemType.Cookie; + mappings[1021] = ItemType.Crafter; + mappings[1022] = ItemType.FilledMap; + mappings[1023] = ItemType.Shears; + mappings[1024] = ItemType.MelonSlice; + mappings[1025] = ItemType.DriedKelp; + mappings[1026] = ItemType.PumpkinSeeds; + mappings[1027] = ItemType.MelonSeeds; + mappings[1028] = ItemType.Beef; + mappings[1029] = ItemType.CookedBeef; + mappings[1030] = ItemType.Chicken; + mappings[1031] = ItemType.CookedChicken; + mappings[1032] = ItemType.RottenFlesh; + mappings[1033] = ItemType.EnderPearl; + mappings[1034] = ItemType.BlazeRod; + mappings[1035] = ItemType.GhastTear; + mappings[1036] = ItemType.GoldNugget; + mappings[1037] = ItemType.NetherWart; + mappings[1038] = ItemType.GlassBottle; + mappings[1039] = ItemType.Potion; + mappings[1040] = ItemType.SpiderEye; + mappings[1041] = ItemType.FermentedSpiderEye; + mappings[1042] = ItemType.BlazePowder; + mappings[1043] = ItemType.MagmaCream; + mappings[1044] = ItemType.BrewingStand; + mappings[1045] = ItemType.Cauldron; + mappings[1046] = ItemType.EnderEye; + mappings[1047] = ItemType.GlisteringMelonSlice; + mappings[1048] = ItemType.ArmadilloSpawnEgg; + mappings[1049] = ItemType.AllaySpawnEgg; + mappings[1050] = ItemType.AxolotlSpawnEgg; + mappings[1051] = ItemType.BatSpawnEgg; + mappings[1052] = ItemType.BeeSpawnEgg; + mappings[1053] = ItemType.BlazeSpawnEgg; + mappings[1054] = ItemType.BoggedSpawnEgg; + mappings[1055] = ItemType.BreezeSpawnEgg; + mappings[1056] = ItemType.CatSpawnEgg; + mappings[1057] = ItemType.CamelSpawnEgg; + mappings[1058] = ItemType.CaveSpiderSpawnEgg; + mappings[1059] = ItemType.ChickenSpawnEgg; + mappings[1060] = ItemType.CodSpawnEgg; + mappings[1061] = ItemType.CowSpawnEgg; + mappings[1062] = ItemType.CreeperSpawnEgg; + mappings[1063] = ItemType.DolphinSpawnEgg; + mappings[1064] = ItemType.DonkeySpawnEgg; + mappings[1065] = ItemType.DrownedSpawnEgg; + mappings[1066] = ItemType.ElderGuardianSpawnEgg; + mappings[1067] = ItemType.EnderDragonSpawnEgg; + mappings[1068] = ItemType.EndermanSpawnEgg; + mappings[1069] = ItemType.EndermiteSpawnEgg; + mappings[1070] = ItemType.EvokerSpawnEgg; + mappings[1071] = ItemType.FoxSpawnEgg; + mappings[1072] = ItemType.FrogSpawnEgg; + mappings[1073] = ItemType.GhastSpawnEgg; + mappings[1074] = ItemType.GlowSquidSpawnEgg; + mappings[1075] = ItemType.GoatSpawnEgg; + mappings[1076] = ItemType.GuardianSpawnEgg; + mappings[1077] = ItemType.HoglinSpawnEgg; + mappings[1078] = ItemType.HorseSpawnEgg; + mappings[1079] = ItemType.HuskSpawnEgg; + mappings[1080] = ItemType.IronGolemSpawnEgg; + mappings[1081] = ItemType.LlamaSpawnEgg; + mappings[1082] = ItemType.MagmaCubeSpawnEgg; + mappings[1083] = ItemType.MooshroomSpawnEgg; + mappings[1084] = ItemType.MuleSpawnEgg; + mappings[1085] = ItemType.OcelotSpawnEgg; + mappings[1086] = ItemType.PandaSpawnEgg; + mappings[1087] = ItemType.ParrotSpawnEgg; + mappings[1088] = ItemType.PhantomSpawnEgg; + mappings[1089] = ItemType.PigSpawnEgg; + mappings[1090] = ItemType.PiglinSpawnEgg; + mappings[1091] = ItemType.PiglinBruteSpawnEgg; + mappings[1092] = ItemType.PillagerSpawnEgg; + mappings[1093] = ItemType.PolarBearSpawnEgg; + mappings[1094] = ItemType.PufferfishSpawnEgg; + mappings[1095] = ItemType.RabbitSpawnEgg; + mappings[1096] = ItemType.RavagerSpawnEgg; + mappings[1097] = ItemType.SalmonSpawnEgg; + mappings[1098] = ItemType.SheepSpawnEgg; + mappings[1099] = ItemType.ShulkerSpawnEgg; + mappings[1100] = ItemType.SilverfishSpawnEgg; + mappings[1101] = ItemType.SkeletonSpawnEgg; + mappings[1102] = ItemType.SkeletonHorseSpawnEgg; + mappings[1103] = ItemType.SlimeSpawnEgg; + mappings[1104] = ItemType.SnifferSpawnEgg; + mappings[1105] = ItemType.SnowGolemSpawnEgg; + mappings[1106] = ItemType.SpiderSpawnEgg; + mappings[1107] = ItemType.SquidSpawnEgg; + mappings[1108] = ItemType.StraySpawnEgg; + mappings[1109] = ItemType.StriderSpawnEgg; + mappings[1110] = ItemType.TadpoleSpawnEgg; + mappings[1111] = ItemType.TraderLlamaSpawnEgg; + mappings[1112] = ItemType.TropicalFishSpawnEgg; + mappings[1113] = ItemType.TurtleSpawnEgg; + mappings[1114] = ItemType.VexSpawnEgg; + mappings[1115] = ItemType.VillagerSpawnEgg; + mappings[1116] = ItemType.VindicatorSpawnEgg; + mappings[1117] = ItemType.WanderingTraderSpawnEgg; + mappings[1118] = ItemType.WardenSpawnEgg; + mappings[1119] = ItemType.WitchSpawnEgg; + mappings[1120] = ItemType.WitherSpawnEgg; + mappings[1121] = ItemType.WitherSkeletonSpawnEgg; + mappings[1122] = ItemType.WolfSpawnEgg; + mappings[1123] = ItemType.ZoglinSpawnEgg; + mappings[1124] = ItemType.CreakingSpawnEgg; + mappings[1125] = ItemType.ZombieSpawnEgg; + mappings[1126] = ItemType.ZombieHorseSpawnEgg; + mappings[1127] = ItemType.ZombieVillagerSpawnEgg; + mappings[1128] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1129] = ItemType.ExperienceBottle; + mappings[1130] = ItemType.FireCharge; + mappings[1131] = ItemType.WindCharge; + mappings[1132] = ItemType.WritableBook; + mappings[1133] = ItemType.WrittenBook; + mappings[1134] = ItemType.BreezeRod; + mappings[1135] = ItemType.Mace; + mappings[1136] = ItemType.ItemFrame; + mappings[1137] = ItemType.GlowItemFrame; + mappings[1138] = ItemType.FlowerPot; + mappings[1139] = ItemType.Carrot; + mappings[1140] = ItemType.Potato; + mappings[1141] = ItemType.BakedPotato; + mappings[1142] = ItemType.PoisonousPotato; + mappings[1143] = ItemType.Map; + mappings[1144] = ItemType.GoldenCarrot; + mappings[1145] = ItemType.SkeletonSkull; + mappings[1146] = ItemType.WitherSkeletonSkull; + mappings[1147] = ItemType.PlayerHead; + mappings[1148] = ItemType.ZombieHead; + mappings[1149] = ItemType.CreeperHead; + mappings[1150] = ItemType.DragonHead; + mappings[1151] = ItemType.PiglinHead; + mappings[1152] = ItemType.NetherStar; + mappings[1153] = ItemType.PumpkinPie; + mappings[1154] = ItemType.FireworkRocket; + mappings[1155] = ItemType.FireworkStar; + mappings[1156] = ItemType.EnchantedBook; + mappings[1157] = ItemType.NetherBrick; + mappings[1158] = ItemType.PrismarineShard; + mappings[1159] = ItemType.PrismarineCrystals; + mappings[1160] = ItemType.Rabbit; + mappings[1161] = ItemType.CookedRabbit; + mappings[1162] = ItemType.RabbitStew; + mappings[1163] = ItemType.RabbitFoot; + mappings[1164] = ItemType.RabbitHide; + mappings[1165] = ItemType.ArmorStand; + mappings[1166] = ItemType.IronHorseArmor; + mappings[1167] = ItemType.GoldenHorseArmor; + mappings[1168] = ItemType.DiamondHorseArmor; + mappings[1169] = ItemType.LeatherHorseArmor; + mappings[1170] = ItemType.Lead; + mappings[1171] = ItemType.NameTag; + mappings[1172] = ItemType.CommandBlockMinecart; + mappings[1173] = ItemType.Mutton; + mappings[1174] = ItemType.CookedMutton; + mappings[1175] = ItemType.WhiteBanner; + mappings[1176] = ItemType.OrangeBanner; + mappings[1177] = ItemType.MagentaBanner; + mappings[1178] = ItemType.LightBlueBanner; + mappings[1179] = ItemType.YellowBanner; + mappings[1180] = ItemType.LimeBanner; + mappings[1181] = ItemType.PinkBanner; + mappings[1182] = ItemType.GrayBanner; + mappings[1183] = ItemType.LightGrayBanner; + mappings[1184] = ItemType.CyanBanner; + mappings[1185] = ItemType.PurpleBanner; + mappings[1186] = ItemType.BlueBanner; + mappings[1187] = ItemType.BrownBanner; + mappings[1188] = ItemType.GreenBanner; + mappings[1189] = ItemType.RedBanner; + mappings[1190] = ItemType.BlackBanner; + mappings[1191] = ItemType.EndCrystal; + mappings[1192] = ItemType.ChorusFruit; + mappings[1193] = ItemType.PoppedChorusFruit; + mappings[1194] = ItemType.TorchflowerSeeds; + mappings[1195] = ItemType.PitcherPod; + mappings[1196] = ItemType.Beetroot; + mappings[1197] = ItemType.BeetrootSeeds; + mappings[1198] = ItemType.BeetrootSoup; + mappings[1199] = ItemType.DragonBreath; + mappings[1200] = ItemType.SplashPotion; + mappings[1201] = ItemType.SpectralArrow; + mappings[1202] = ItemType.TippedArrow; + mappings[1203] = ItemType.LingeringPotion; + mappings[1204] = ItemType.Shield; + mappings[1205] = ItemType.TotemOfUndying; + mappings[1206] = ItemType.ShulkerShell; + mappings[1207] = ItemType.IronNugget; + mappings[1208] = ItemType.KnowledgeBook; + mappings[1209] = ItemType.DebugStick; + mappings[1210] = ItemType.MusicDisc13; + mappings[1211] = ItemType.MusicDiscCat; + mappings[1212] = ItemType.MusicDiscBlocks; + mappings[1213] = ItemType.MusicDiscChirp; + mappings[1214] = ItemType.MusicDiscCreator; + mappings[1215] = ItemType.MusicDiscCreatorMusicBox; + mappings[1216] = ItemType.MusicDiscFar; + mappings[1217] = ItemType.MusicDiscMall; + mappings[1218] = ItemType.MusicDiscMellohi; + mappings[1219] = ItemType.MusicDiscStal; + mappings[1220] = ItemType.MusicDiscStrad; + mappings[1221] = ItemType.MusicDiscWard; + mappings[1222] = ItemType.MusicDisc11; + mappings[1223] = ItemType.MusicDiscWait; + mappings[1224] = ItemType.MusicDiscOtherside; + mappings[1225] = ItemType.MusicDiscRelic; + mappings[1226] = ItemType.MusicDisc5; + mappings[1227] = ItemType.MusicDiscPigstep; + mappings[1228] = ItemType.MusicDiscPrecipice; + mappings[1229] = ItemType.DiscFragment5; + mappings[1230] = ItemType.Trident; + mappings[1231] = ItemType.NautilusShell; + mappings[1232] = ItemType.HeartOfTheSea; + mappings[1233] = ItemType.Crossbow; + mappings[1234] = ItemType.SuspiciousStew; + mappings[1235] = ItemType.Loom; + mappings[1236] = ItemType.FlowerBannerPattern; + mappings[1237] = ItemType.CreeperBannerPattern; + mappings[1238] = ItemType.SkullBannerPattern; + mappings[1239] = ItemType.MojangBannerPattern; + mappings[1240] = ItemType.GlobeBannerPattern; + mappings[1241] = ItemType.PiglinBannerPattern; + mappings[1242] = ItemType.FlowBannerPattern; + mappings[1243] = ItemType.GusterBannerPattern; + mappings[1244] = ItemType.FieldMasonedBannerPattern; + mappings[1245] = ItemType.BordureIndentedBannerPattern; + mappings[1246] = ItemType.GoatHorn; + mappings[1247] = ItemType.Composter; + mappings[1248] = ItemType.Barrel; + mappings[1249] = ItemType.Smoker; + mappings[1250] = ItemType.BlastFurnace; + mappings[1251] = ItemType.CartographyTable; + mappings[1252] = ItemType.FletchingTable; + mappings[1253] = ItemType.Grindstone; + mappings[1254] = ItemType.SmithingTable; + mappings[1255] = ItemType.Stonecutter; + mappings[1256] = ItemType.Bell; + mappings[1257] = ItemType.Lantern; + mappings[1258] = ItemType.SoulLantern; + mappings[1259] = ItemType.SweetBerries; + mappings[1260] = ItemType.GlowBerries; + mappings[1261] = ItemType.Campfire; + mappings[1262] = ItemType.SoulCampfire; + mappings[1263] = ItemType.Shroomlight; + mappings[1264] = ItemType.Honeycomb; + mappings[1265] = ItemType.BeeNest; + mappings[1266] = ItemType.Beehive; + mappings[1267] = ItemType.HoneyBottle; + mappings[1268] = ItemType.HoneycombBlock; + mappings[1269] = ItemType.Lodestone; + mappings[1270] = ItemType.CryingObsidian; + mappings[1271] = ItemType.Blackstone; + mappings[1272] = ItemType.BlackstoneSlab; + mappings[1273] = ItemType.BlackstoneStairs; + mappings[1274] = ItemType.GildedBlackstone; + mappings[1275] = ItemType.PolishedBlackstone; + mappings[1276] = ItemType.PolishedBlackstoneSlab; + mappings[1277] = ItemType.PolishedBlackstoneStairs; + mappings[1278] = ItemType.ChiseledPolishedBlackstone; + mappings[1279] = ItemType.PolishedBlackstoneBricks; + mappings[1280] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1281] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1282] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1283] = ItemType.RespawnAnchor; + mappings[1284] = ItemType.Candle; + mappings[1285] = ItemType.WhiteCandle; + mappings[1286] = ItemType.OrangeCandle; + mappings[1287] = ItemType.MagentaCandle; + mappings[1288] = ItemType.LightBlueCandle; + mappings[1289] = ItemType.YellowCandle; + mappings[1290] = ItemType.LimeCandle; + mappings[1291] = ItemType.PinkCandle; + mappings[1292] = ItemType.GrayCandle; + mappings[1293] = ItemType.LightGrayCandle; + mappings[1294] = ItemType.CyanCandle; + mappings[1295] = ItemType.PurpleCandle; + mappings[1296] = ItemType.BlueCandle; + mappings[1297] = ItemType.BrownCandle; + mappings[1298] = ItemType.GreenCandle; + mappings[1299] = ItemType.RedCandle; + mappings[1300] = ItemType.BlackCandle; + mappings[1301] = ItemType.SmallAmethystBud; + mappings[1302] = ItemType.MediumAmethystBud; + mappings[1303] = ItemType.LargeAmethystBud; + mappings[1304] = ItemType.AmethystCluster; + mappings[1305] = ItemType.PointedDripstone; + mappings[1306] = ItemType.OchreFroglight; + mappings[1307] = ItemType.VerdantFroglight; + mappings[1308] = ItemType.PearlescentFroglight; + mappings[1309] = ItemType.Frogspawn; + mappings[1310] = ItemType.EchoShard; + mappings[1311] = ItemType.Brush; + mappings[1312] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1313] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1314] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1315] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1316] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1317] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1318] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1319] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1320] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1321] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1322] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1323] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1324] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1325] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1326] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1327] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1328] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1329] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1330] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1331] = ItemType.AnglerPotterySherd; + mappings[1332] = ItemType.ArcherPotterySherd; + mappings[1333] = ItemType.ArmsUpPotterySherd; + mappings[1334] = ItemType.BladePotterySherd; + mappings[1335] = ItemType.BrewerPotterySherd; + mappings[1336] = ItemType.BurnPotterySherd; + mappings[1337] = ItemType.DangerPotterySherd; + mappings[1338] = ItemType.ExplorerPotterySherd; + mappings[1339] = ItemType.FlowPotterySherd; + mappings[1340] = ItemType.FriendPotterySherd; + mappings[1341] = ItemType.GusterPotterySherd; + mappings[1342] = ItemType.HeartPotterySherd; + mappings[1343] = ItemType.HeartbreakPotterySherd; + mappings[1344] = ItemType.HowlPotterySherd; + mappings[1345] = ItemType.MinerPotterySherd; + mappings[1346] = ItemType.MournerPotterySherd; + mappings[1347] = ItemType.PlentyPotterySherd; + mappings[1348] = ItemType.PrizePotterySherd; + mappings[1349] = ItemType.ScrapePotterySherd; + mappings[1350] = ItemType.SheafPotterySherd; + mappings[1351] = ItemType.ShelterPotterySherd; + mappings[1352] = ItemType.SkullPotterySherd; + mappings[1353] = ItemType.SnortPotterySherd; + mappings[1354] = ItemType.CopperGrate; + mappings[1355] = ItemType.ExposedCopperGrate; + mappings[1356] = ItemType.WeatheredCopperGrate; + mappings[1357] = ItemType.OxidizedCopperGrate; + mappings[1358] = ItemType.WaxedCopperGrate; + mappings[1359] = ItemType.WaxedExposedCopperGrate; + mappings[1360] = ItemType.WaxedWeatheredCopperGrate; + mappings[1361] = ItemType.WaxedOxidizedCopperGrate; + mappings[1362] = ItemType.CopperBulb; + mappings[1363] = ItemType.ExposedCopperBulb; + mappings[1364] = ItemType.WeatheredCopperBulb; + mappings[1365] = ItemType.OxidizedCopperBulb; + mappings[1366] = ItemType.WaxedCopperBulb; + mappings[1367] = ItemType.WaxedExposedCopperBulb; + mappings[1368] = ItemType.WaxedWeatheredCopperBulb; + mappings[1369] = ItemType.WaxedOxidizedCopperBulb; + mappings[1370] = ItemType.TrialSpawner; + mappings[1371] = ItemType.TrialKey; + mappings[1372] = ItemType.OminousTrialKey; + mappings[1373] = ItemType.Vault; + mappings[1374] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1214.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1214.cs new file mode 100644 index 00000000..c0cf85a8 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1214.cs @@ -0,0 +1,1403 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1214 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1214() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Azalea; + mappings[205] = ItemType.FloweringAzalea; + mappings[206] = ItemType.DeadBush; + mappings[207] = ItemType.Seagrass; + mappings[208] = ItemType.SeaPickle; + mappings[209] = ItemType.WhiteWool; + mappings[210] = ItemType.OrangeWool; + mappings[211] = ItemType.MagentaWool; + mappings[212] = ItemType.LightBlueWool; + mappings[213] = ItemType.YellowWool; + mappings[214] = ItemType.LimeWool; + mappings[215] = ItemType.PinkWool; + mappings[216] = ItemType.GrayWool; + mappings[217] = ItemType.LightGrayWool; + mappings[218] = ItemType.CyanWool; + mappings[219] = ItemType.PurpleWool; + mappings[220] = ItemType.BlueWool; + mappings[221] = ItemType.BrownWool; + mappings[222] = ItemType.GreenWool; + mappings[223] = ItemType.RedWool; + mappings[224] = ItemType.BlackWool; + mappings[225] = ItemType.Dandelion; + mappings[226] = ItemType.OpenEyeblossom; + mappings[227] = ItemType.ClosedEyeblossom; + mappings[228] = ItemType.Poppy; + mappings[229] = ItemType.BlueOrchid; + mappings[230] = ItemType.Allium; + mappings[231] = ItemType.AzureBluet; + mappings[232] = ItemType.RedTulip; + mappings[233] = ItemType.OrangeTulip; + mappings[234] = ItemType.WhiteTulip; + mappings[235] = ItemType.PinkTulip; + mappings[236] = ItemType.OxeyeDaisy; + mappings[237] = ItemType.Cornflower; + mappings[238] = ItemType.LilyOfTheValley; + mappings[239] = ItemType.WitherRose; + mappings[240] = ItemType.Torchflower; + mappings[241] = ItemType.PitcherPlant; + mappings[242] = ItemType.SporeBlossom; + mappings[243] = ItemType.BrownMushroom; + mappings[244] = ItemType.RedMushroom; + mappings[245] = ItemType.CrimsonFungus; + mappings[246] = ItemType.WarpedFungus; + mappings[247] = ItemType.CrimsonRoots; + mappings[248] = ItemType.WarpedRoots; + mappings[249] = ItemType.NetherSprouts; + mappings[250] = ItemType.WeepingVines; + mappings[251] = ItemType.TwistingVines; + mappings[252] = ItemType.SugarCane; + mappings[253] = ItemType.Kelp; + mappings[254] = ItemType.PinkPetals; + mappings[255] = ItemType.MossCarpet; + mappings[256] = ItemType.MossBlock; + mappings[257] = ItemType.PaleMossCarpet; + mappings[258] = ItemType.PaleHangingMoss; + mappings[259] = ItemType.PaleMossBlock; + mappings[260] = ItemType.HangingRoots; + mappings[261] = ItemType.BigDripleaf; + mappings[262] = ItemType.SmallDripleaf; + mappings[263] = ItemType.Bamboo; + mappings[264] = ItemType.OakSlab; + mappings[265] = ItemType.SpruceSlab; + mappings[266] = ItemType.BirchSlab; + mappings[267] = ItemType.JungleSlab; + mappings[268] = ItemType.AcaciaSlab; + mappings[269] = ItemType.CherrySlab; + mappings[270] = ItemType.DarkOakSlab; + mappings[271] = ItemType.PaleOakSlab; + mappings[272] = ItemType.MangroveSlab; + mappings[273] = ItemType.BambooSlab; + mappings[274] = ItemType.BambooMosaicSlab; + mappings[275] = ItemType.CrimsonSlab; + mappings[276] = ItemType.WarpedSlab; + mappings[277] = ItemType.StoneSlab; + mappings[278] = ItemType.SmoothStoneSlab; + mappings[279] = ItemType.SandstoneSlab; + mappings[280] = ItemType.CutSandstoneSlab; + mappings[281] = ItemType.PetrifiedOakSlab; + mappings[282] = ItemType.CobblestoneSlab; + mappings[283] = ItemType.BrickSlab; + mappings[284] = ItemType.StoneBrickSlab; + mappings[285] = ItemType.MudBrickSlab; + mappings[286] = ItemType.NetherBrickSlab; + mappings[287] = ItemType.QuartzSlab; + mappings[288] = ItemType.RedSandstoneSlab; + mappings[289] = ItemType.CutRedSandstoneSlab; + mappings[290] = ItemType.PurpurSlab; + mappings[291] = ItemType.PrismarineSlab; + mappings[292] = ItemType.PrismarineBrickSlab; + mappings[293] = ItemType.DarkPrismarineSlab; + mappings[294] = ItemType.SmoothQuartz; + mappings[295] = ItemType.SmoothRedSandstone; + mappings[296] = ItemType.SmoothSandstone; + mappings[297] = ItemType.SmoothStone; + mappings[298] = ItemType.Bricks; + mappings[299] = ItemType.Bookshelf; + mappings[300] = ItemType.ChiseledBookshelf; + mappings[301] = ItemType.DecoratedPot; + mappings[302] = ItemType.MossyCobblestone; + mappings[303] = ItemType.Obsidian; + mappings[304] = ItemType.Torch; + mappings[305] = ItemType.EndRod; + mappings[306] = ItemType.ChorusPlant; + mappings[307] = ItemType.ChorusFlower; + mappings[308] = ItemType.PurpurBlock; + mappings[309] = ItemType.PurpurPillar; + mappings[310] = ItemType.PurpurStairs; + mappings[311] = ItemType.Spawner; + mappings[312] = ItemType.CreakingHeart; + mappings[313] = ItemType.Chest; + mappings[314] = ItemType.CraftingTable; + mappings[315] = ItemType.Farmland; + mappings[316] = ItemType.Furnace; + mappings[317] = ItemType.Ladder; + mappings[318] = ItemType.CobblestoneStairs; + mappings[319] = ItemType.Snow; + mappings[320] = ItemType.Ice; + mappings[321] = ItemType.SnowBlock; + mappings[322] = ItemType.Cactus; + mappings[323] = ItemType.Clay; + mappings[324] = ItemType.Jukebox; + mappings[325] = ItemType.OakFence; + mappings[326] = ItemType.SpruceFence; + mappings[327] = ItemType.BirchFence; + mappings[328] = ItemType.JungleFence; + mappings[329] = ItemType.AcaciaFence; + mappings[330] = ItemType.CherryFence; + mappings[331] = ItemType.DarkOakFence; + mappings[332] = ItemType.PaleOakFence; + mappings[333] = ItemType.MangroveFence; + mappings[334] = ItemType.BambooFence; + mappings[335] = ItemType.CrimsonFence; + mappings[336] = ItemType.WarpedFence; + mappings[337] = ItemType.Pumpkin; + mappings[338] = ItemType.CarvedPumpkin; + mappings[339] = ItemType.JackOLantern; + mappings[340] = ItemType.Netherrack; + mappings[341] = ItemType.SoulSand; + mappings[342] = ItemType.SoulSoil; + mappings[343] = ItemType.Basalt; + mappings[344] = ItemType.PolishedBasalt; + mappings[345] = ItemType.SmoothBasalt; + mappings[346] = ItemType.SoulTorch; + mappings[347] = ItemType.Glowstone; + mappings[348] = ItemType.InfestedStone; + mappings[349] = ItemType.InfestedCobblestone; + mappings[350] = ItemType.InfestedStoneBricks; + mappings[351] = ItemType.InfestedMossyStoneBricks; + mappings[352] = ItemType.InfestedCrackedStoneBricks; + mappings[353] = ItemType.InfestedChiseledStoneBricks; + mappings[354] = ItemType.InfestedDeepslate; + mappings[355] = ItemType.StoneBricks; + mappings[356] = ItemType.MossyStoneBricks; + mappings[357] = ItemType.CrackedStoneBricks; + mappings[358] = ItemType.ChiseledStoneBricks; + mappings[359] = ItemType.PackedMud; + mappings[360] = ItemType.MudBricks; + mappings[361] = ItemType.DeepslateBricks; + mappings[362] = ItemType.CrackedDeepslateBricks; + mappings[363] = ItemType.DeepslateTiles; + mappings[364] = ItemType.CrackedDeepslateTiles; + mappings[365] = ItemType.ChiseledDeepslate; + mappings[366] = ItemType.ReinforcedDeepslate; + mappings[367] = ItemType.BrownMushroomBlock; + mappings[368] = ItemType.RedMushroomBlock; + mappings[369] = ItemType.MushroomStem; + mappings[370] = ItemType.IronBars; + mappings[371] = ItemType.Chain; + mappings[372] = ItemType.GlassPane; + mappings[373] = ItemType.Melon; + mappings[374] = ItemType.Vine; + mappings[375] = ItemType.GlowLichen; + mappings[376] = ItemType.ResinClump; + mappings[377] = ItemType.ResinBlock; + mappings[378] = ItemType.ResinBricks; + mappings[379] = ItemType.ResinBrickStairs; + mappings[380] = ItemType.ResinBrickSlab; + mappings[381] = ItemType.ResinBrickWall; + mappings[382] = ItemType.ChiseledResinBricks; + mappings[383] = ItemType.BrickStairs; + mappings[384] = ItemType.StoneBrickStairs; + mappings[385] = ItemType.MudBrickStairs; + mappings[386] = ItemType.Mycelium; + mappings[387] = ItemType.LilyPad; + mappings[388] = ItemType.NetherBricks; + mappings[389] = ItemType.CrackedNetherBricks; + mappings[390] = ItemType.ChiseledNetherBricks; + mappings[391] = ItemType.NetherBrickFence; + mappings[392] = ItemType.NetherBrickStairs; + mappings[393] = ItemType.Sculk; + mappings[394] = ItemType.SculkVein; + mappings[395] = ItemType.SculkCatalyst; + mappings[396] = ItemType.SculkShrieker; + mappings[397] = ItemType.EnchantingTable; + mappings[398] = ItemType.EndPortalFrame; + mappings[399] = ItemType.EndStone; + mappings[400] = ItemType.EndStoneBricks; + mappings[401] = ItemType.DragonEgg; + mappings[402] = ItemType.SandstoneStairs; + mappings[403] = ItemType.EnderChest; + mappings[404] = ItemType.EmeraldBlock; + mappings[405] = ItemType.OakStairs; + mappings[406] = ItemType.SpruceStairs; + mappings[407] = ItemType.BirchStairs; + mappings[408] = ItemType.JungleStairs; + mappings[409] = ItemType.AcaciaStairs; + mappings[410] = ItemType.CherryStairs; + mappings[411] = ItemType.DarkOakStairs; + mappings[412] = ItemType.PaleOakStairs; + mappings[413] = ItemType.MangroveStairs; + mappings[414] = ItemType.BambooStairs; + mappings[415] = ItemType.BambooMosaicStairs; + mappings[416] = ItemType.CrimsonStairs; + mappings[417] = ItemType.WarpedStairs; + mappings[418] = ItemType.CommandBlock; + mappings[419] = ItemType.Beacon; + mappings[420] = ItemType.CobblestoneWall; + mappings[421] = ItemType.MossyCobblestoneWall; + mappings[422] = ItemType.BrickWall; + mappings[423] = ItemType.PrismarineWall; + mappings[424] = ItemType.RedSandstoneWall; + mappings[425] = ItemType.MossyStoneBrickWall; + mappings[426] = ItemType.GraniteWall; + mappings[427] = ItemType.StoneBrickWall; + mappings[428] = ItemType.MudBrickWall; + mappings[429] = ItemType.NetherBrickWall; + mappings[430] = ItemType.AndesiteWall; + mappings[431] = ItemType.RedNetherBrickWall; + mappings[432] = ItemType.SandstoneWall; + mappings[433] = ItemType.EndStoneBrickWall; + mappings[434] = ItemType.DioriteWall; + mappings[435] = ItemType.BlackstoneWall; + mappings[436] = ItemType.PolishedBlackstoneWall; + mappings[437] = ItemType.PolishedBlackstoneBrickWall; + mappings[438] = ItemType.CobbledDeepslateWall; + mappings[439] = ItemType.PolishedDeepslateWall; + mappings[440] = ItemType.DeepslateBrickWall; + mappings[441] = ItemType.DeepslateTileWall; + mappings[442] = ItemType.Anvil; + mappings[443] = ItemType.ChippedAnvil; + mappings[444] = ItemType.DamagedAnvil; + mappings[445] = ItemType.ChiseledQuartzBlock; + mappings[446] = ItemType.QuartzBlock; + mappings[447] = ItemType.QuartzBricks; + mappings[448] = ItemType.QuartzPillar; + mappings[449] = ItemType.QuartzStairs; + mappings[450] = ItemType.WhiteTerracotta; + mappings[451] = ItemType.OrangeTerracotta; + mappings[452] = ItemType.MagentaTerracotta; + mappings[453] = ItemType.LightBlueTerracotta; + mappings[454] = ItemType.YellowTerracotta; + mappings[455] = ItemType.LimeTerracotta; + mappings[456] = ItemType.PinkTerracotta; + mappings[457] = ItemType.GrayTerracotta; + mappings[458] = ItemType.LightGrayTerracotta; + mappings[459] = ItemType.CyanTerracotta; + mappings[460] = ItemType.PurpleTerracotta; + mappings[461] = ItemType.BlueTerracotta; + mappings[462] = ItemType.BrownTerracotta; + mappings[463] = ItemType.GreenTerracotta; + mappings[464] = ItemType.RedTerracotta; + mappings[465] = ItemType.BlackTerracotta; + mappings[466] = ItemType.Barrier; + mappings[467] = ItemType.Light; + mappings[468] = ItemType.HayBlock; + mappings[469] = ItemType.WhiteCarpet; + mappings[470] = ItemType.OrangeCarpet; + mappings[471] = ItemType.MagentaCarpet; + mappings[472] = ItemType.LightBlueCarpet; + mappings[473] = ItemType.YellowCarpet; + mappings[474] = ItemType.LimeCarpet; + mappings[475] = ItemType.PinkCarpet; + mappings[476] = ItemType.GrayCarpet; + mappings[477] = ItemType.LightGrayCarpet; + mappings[478] = ItemType.CyanCarpet; + mappings[479] = ItemType.PurpleCarpet; + mappings[480] = ItemType.BlueCarpet; + mappings[481] = ItemType.BrownCarpet; + mappings[482] = ItemType.GreenCarpet; + mappings[483] = ItemType.RedCarpet; + mappings[484] = ItemType.BlackCarpet; + mappings[485] = ItemType.Terracotta; + mappings[486] = ItemType.PackedIce; + mappings[487] = ItemType.DirtPath; + mappings[488] = ItemType.Sunflower; + mappings[489] = ItemType.Lilac; + mappings[490] = ItemType.RoseBush; + mappings[491] = ItemType.Peony; + mappings[492] = ItemType.TallGrass; + mappings[493] = ItemType.LargeFern; + mappings[494] = ItemType.WhiteStainedGlass; + mappings[495] = ItemType.OrangeStainedGlass; + mappings[496] = ItemType.MagentaStainedGlass; + mappings[497] = ItemType.LightBlueStainedGlass; + mappings[498] = ItemType.YellowStainedGlass; + mappings[499] = ItemType.LimeStainedGlass; + mappings[500] = ItemType.PinkStainedGlass; + mappings[501] = ItemType.GrayStainedGlass; + mappings[502] = ItemType.LightGrayStainedGlass; + mappings[503] = ItemType.CyanStainedGlass; + mappings[504] = ItemType.PurpleStainedGlass; + mappings[505] = ItemType.BlueStainedGlass; + mappings[506] = ItemType.BrownStainedGlass; + mappings[507] = ItemType.GreenStainedGlass; + mappings[508] = ItemType.RedStainedGlass; + mappings[509] = ItemType.BlackStainedGlass; + mappings[510] = ItemType.WhiteStainedGlassPane; + mappings[511] = ItemType.OrangeStainedGlassPane; + mappings[512] = ItemType.MagentaStainedGlassPane; + mappings[513] = ItemType.LightBlueStainedGlassPane; + mappings[514] = ItemType.YellowStainedGlassPane; + mappings[515] = ItemType.LimeStainedGlassPane; + mappings[516] = ItemType.PinkStainedGlassPane; + mappings[517] = ItemType.GrayStainedGlassPane; + mappings[518] = ItemType.LightGrayStainedGlassPane; + mappings[519] = ItemType.CyanStainedGlassPane; + mappings[520] = ItemType.PurpleStainedGlassPane; + mappings[521] = ItemType.BlueStainedGlassPane; + mappings[522] = ItemType.BrownStainedGlassPane; + mappings[523] = ItemType.GreenStainedGlassPane; + mappings[524] = ItemType.RedStainedGlassPane; + mappings[525] = ItemType.BlackStainedGlassPane; + mappings[526] = ItemType.Prismarine; + mappings[527] = ItemType.PrismarineBricks; + mappings[528] = ItemType.DarkPrismarine; + mappings[529] = ItemType.PrismarineStairs; + mappings[530] = ItemType.PrismarineBrickStairs; + mappings[531] = ItemType.DarkPrismarineStairs; + mappings[532] = ItemType.SeaLantern; + mappings[533] = ItemType.RedSandstone; + mappings[534] = ItemType.ChiseledRedSandstone; + mappings[535] = ItemType.CutRedSandstone; + mappings[536] = ItemType.RedSandstoneStairs; + mappings[537] = ItemType.RepeatingCommandBlock; + mappings[538] = ItemType.ChainCommandBlock; + mappings[539] = ItemType.MagmaBlock; + mappings[540] = ItemType.NetherWartBlock; + mappings[541] = ItemType.WarpedWartBlock; + mappings[542] = ItemType.RedNetherBricks; + mappings[543] = ItemType.BoneBlock; + mappings[544] = ItemType.StructureVoid; + mappings[545] = ItemType.ShulkerBox; + mappings[546] = ItemType.WhiteShulkerBox; + mappings[547] = ItemType.OrangeShulkerBox; + mappings[548] = ItemType.MagentaShulkerBox; + mappings[549] = ItemType.LightBlueShulkerBox; + mappings[550] = ItemType.YellowShulkerBox; + mappings[551] = ItemType.LimeShulkerBox; + mappings[552] = ItemType.PinkShulkerBox; + mappings[553] = ItemType.GrayShulkerBox; + mappings[554] = ItemType.LightGrayShulkerBox; + mappings[555] = ItemType.CyanShulkerBox; + mappings[556] = ItemType.PurpleShulkerBox; + mappings[557] = ItemType.BlueShulkerBox; + mappings[558] = ItemType.BrownShulkerBox; + mappings[559] = ItemType.GreenShulkerBox; + mappings[560] = ItemType.RedShulkerBox; + mappings[561] = ItemType.BlackShulkerBox; + mappings[562] = ItemType.WhiteGlazedTerracotta; + mappings[563] = ItemType.OrangeGlazedTerracotta; + mappings[564] = ItemType.MagentaGlazedTerracotta; + mappings[565] = ItemType.LightBlueGlazedTerracotta; + mappings[566] = ItemType.YellowGlazedTerracotta; + mappings[567] = ItemType.LimeGlazedTerracotta; + mappings[568] = ItemType.PinkGlazedTerracotta; + mappings[569] = ItemType.GrayGlazedTerracotta; + mappings[570] = ItemType.LightGrayGlazedTerracotta; + mappings[571] = ItemType.CyanGlazedTerracotta; + mappings[572] = ItemType.PurpleGlazedTerracotta; + mappings[573] = ItemType.BlueGlazedTerracotta; + mappings[574] = ItemType.BrownGlazedTerracotta; + mappings[575] = ItemType.GreenGlazedTerracotta; + mappings[576] = ItemType.RedGlazedTerracotta; + mappings[577] = ItemType.BlackGlazedTerracotta; + mappings[578] = ItemType.WhiteConcrete; + mappings[579] = ItemType.OrangeConcrete; + mappings[580] = ItemType.MagentaConcrete; + mappings[581] = ItemType.LightBlueConcrete; + mappings[582] = ItemType.YellowConcrete; + mappings[583] = ItemType.LimeConcrete; + mappings[584] = ItemType.PinkConcrete; + mappings[585] = ItemType.GrayConcrete; + mappings[586] = ItemType.LightGrayConcrete; + mappings[587] = ItemType.CyanConcrete; + mappings[588] = ItemType.PurpleConcrete; + mappings[589] = ItemType.BlueConcrete; + mappings[590] = ItemType.BrownConcrete; + mappings[591] = ItemType.GreenConcrete; + mappings[592] = ItemType.RedConcrete; + mappings[593] = ItemType.BlackConcrete; + mappings[594] = ItemType.WhiteConcretePowder; + mappings[595] = ItemType.OrangeConcretePowder; + mappings[596] = ItemType.MagentaConcretePowder; + mappings[597] = ItemType.LightBlueConcretePowder; + mappings[598] = ItemType.YellowConcretePowder; + mappings[599] = ItemType.LimeConcretePowder; + mappings[600] = ItemType.PinkConcretePowder; + mappings[601] = ItemType.GrayConcretePowder; + mappings[602] = ItemType.LightGrayConcretePowder; + mappings[603] = ItemType.CyanConcretePowder; + mappings[604] = ItemType.PurpleConcretePowder; + mappings[605] = ItemType.BlueConcretePowder; + mappings[606] = ItemType.BrownConcretePowder; + mappings[607] = ItemType.GreenConcretePowder; + mappings[608] = ItemType.RedConcretePowder; + mappings[609] = ItemType.BlackConcretePowder; + mappings[610] = ItemType.TurtleEgg; + mappings[611] = ItemType.SnifferEgg; + mappings[612] = ItemType.DeadTubeCoralBlock; + mappings[613] = ItemType.DeadBrainCoralBlock; + mappings[614] = ItemType.DeadBubbleCoralBlock; + mappings[615] = ItemType.DeadFireCoralBlock; + mappings[616] = ItemType.DeadHornCoralBlock; + mappings[617] = ItemType.TubeCoralBlock; + mappings[618] = ItemType.BrainCoralBlock; + mappings[619] = ItemType.BubbleCoralBlock; + mappings[620] = ItemType.FireCoralBlock; + mappings[621] = ItemType.HornCoralBlock; + mappings[622] = ItemType.TubeCoral; + mappings[623] = ItemType.BrainCoral; + mappings[624] = ItemType.BubbleCoral; + mappings[625] = ItemType.FireCoral; + mappings[626] = ItemType.HornCoral; + mappings[627] = ItemType.DeadBrainCoral; + mappings[628] = ItemType.DeadBubbleCoral; + mappings[629] = ItemType.DeadFireCoral; + mappings[630] = ItemType.DeadHornCoral; + mappings[631] = ItemType.DeadTubeCoral; + mappings[632] = ItemType.TubeCoralFan; + mappings[633] = ItemType.BrainCoralFan; + mappings[634] = ItemType.BubbleCoralFan; + mappings[635] = ItemType.FireCoralFan; + mappings[636] = ItemType.HornCoralFan; + mappings[637] = ItemType.DeadTubeCoralFan; + mappings[638] = ItemType.DeadBrainCoralFan; + mappings[639] = ItemType.DeadBubbleCoralFan; + mappings[640] = ItemType.DeadFireCoralFan; + mappings[641] = ItemType.DeadHornCoralFan; + mappings[642] = ItemType.BlueIce; + mappings[643] = ItemType.Conduit; + mappings[644] = ItemType.PolishedGraniteStairs; + mappings[645] = ItemType.SmoothRedSandstoneStairs; + mappings[646] = ItemType.MossyStoneBrickStairs; + mappings[647] = ItemType.PolishedDioriteStairs; + mappings[648] = ItemType.MossyCobblestoneStairs; + mappings[649] = ItemType.EndStoneBrickStairs; + mappings[650] = ItemType.StoneStairs; + mappings[651] = ItemType.SmoothSandstoneStairs; + mappings[652] = ItemType.SmoothQuartzStairs; + mappings[653] = ItemType.GraniteStairs; + mappings[654] = ItemType.AndesiteStairs; + mappings[655] = ItemType.RedNetherBrickStairs; + mappings[656] = ItemType.PolishedAndesiteStairs; + mappings[657] = ItemType.DioriteStairs; + mappings[658] = ItemType.CobbledDeepslateStairs; + mappings[659] = ItemType.PolishedDeepslateStairs; + mappings[660] = ItemType.DeepslateBrickStairs; + mappings[661] = ItemType.DeepslateTileStairs; + mappings[662] = ItemType.PolishedGraniteSlab; + mappings[663] = ItemType.SmoothRedSandstoneSlab; + mappings[664] = ItemType.MossyStoneBrickSlab; + mappings[665] = ItemType.PolishedDioriteSlab; + mappings[666] = ItemType.MossyCobblestoneSlab; + mappings[667] = ItemType.EndStoneBrickSlab; + mappings[668] = ItemType.SmoothSandstoneSlab; + mappings[669] = ItemType.SmoothQuartzSlab; + mappings[670] = ItemType.GraniteSlab; + mappings[671] = ItemType.AndesiteSlab; + mappings[672] = ItemType.RedNetherBrickSlab; + mappings[673] = ItemType.PolishedAndesiteSlab; + mappings[674] = ItemType.DioriteSlab; + mappings[675] = ItemType.CobbledDeepslateSlab; + mappings[676] = ItemType.PolishedDeepslateSlab; + mappings[677] = ItemType.DeepslateBrickSlab; + mappings[678] = ItemType.DeepslateTileSlab; + mappings[679] = ItemType.Scaffolding; + mappings[680] = ItemType.Redstone; + mappings[681] = ItemType.RedstoneTorch; + mappings[682] = ItemType.RedstoneBlock; + mappings[683] = ItemType.Repeater; + mappings[684] = ItemType.Comparator; + mappings[685] = ItemType.Piston; + mappings[686] = ItemType.StickyPiston; + mappings[687] = ItemType.SlimeBlock; + mappings[688] = ItemType.HoneyBlock; + mappings[689] = ItemType.Observer; + mappings[690] = ItemType.Hopper; + mappings[691] = ItemType.Dispenser; + mappings[692] = ItemType.Dropper; + mappings[693] = ItemType.Lectern; + mappings[694] = ItemType.Target; + mappings[695] = ItemType.Lever; + mappings[696] = ItemType.LightningRod; + mappings[697] = ItemType.DaylightDetector; + mappings[698] = ItemType.SculkSensor; + mappings[699] = ItemType.CalibratedSculkSensor; + mappings[700] = ItemType.TripwireHook; + mappings[701] = ItemType.TrappedChest; + mappings[702] = ItemType.Tnt; + mappings[703] = ItemType.RedstoneLamp; + mappings[704] = ItemType.NoteBlock; + mappings[705] = ItemType.StoneButton; + mappings[706] = ItemType.PolishedBlackstoneButton; + mappings[707] = ItemType.OakButton; + mappings[708] = ItemType.SpruceButton; + mappings[709] = ItemType.BirchButton; + mappings[710] = ItemType.JungleButton; + mappings[711] = ItemType.AcaciaButton; + mappings[712] = ItemType.CherryButton; + mappings[713] = ItemType.DarkOakButton; + mappings[714] = ItemType.PaleOakButton; + mappings[715] = ItemType.MangroveButton; + mappings[716] = ItemType.BambooButton; + mappings[717] = ItemType.CrimsonButton; + mappings[718] = ItemType.WarpedButton; + mappings[719] = ItemType.StonePressurePlate; + mappings[720] = ItemType.PolishedBlackstonePressurePlate; + mappings[721] = ItemType.LightWeightedPressurePlate; + mappings[722] = ItemType.HeavyWeightedPressurePlate; + mappings[723] = ItemType.OakPressurePlate; + mappings[724] = ItemType.SprucePressurePlate; + mappings[725] = ItemType.BirchPressurePlate; + mappings[726] = ItemType.JunglePressurePlate; + mappings[727] = ItemType.AcaciaPressurePlate; + mappings[728] = ItemType.CherryPressurePlate; + mappings[729] = ItemType.DarkOakPressurePlate; + mappings[730] = ItemType.PaleOakPressurePlate; + mappings[731] = ItemType.MangrovePressurePlate; + mappings[732] = ItemType.BambooPressurePlate; + mappings[733] = ItemType.CrimsonPressurePlate; + mappings[734] = ItemType.WarpedPressurePlate; + mappings[735] = ItemType.IronDoor; + mappings[736] = ItemType.OakDoor; + mappings[737] = ItemType.SpruceDoor; + mappings[738] = ItemType.BirchDoor; + mappings[739] = ItemType.JungleDoor; + mappings[740] = ItemType.AcaciaDoor; + mappings[741] = ItemType.CherryDoor; + mappings[742] = ItemType.DarkOakDoor; + mappings[743] = ItemType.PaleOakDoor; + mappings[744] = ItemType.MangroveDoor; + mappings[745] = ItemType.BambooDoor; + mappings[746] = ItemType.CrimsonDoor; + mappings[747] = ItemType.WarpedDoor; + mappings[748] = ItemType.CopperDoor; + mappings[749] = ItemType.ExposedCopperDoor; + mappings[750] = ItemType.WeatheredCopperDoor; + mappings[751] = ItemType.OxidizedCopperDoor; + mappings[752] = ItemType.WaxedCopperDoor; + mappings[753] = ItemType.WaxedExposedCopperDoor; + mappings[754] = ItemType.WaxedWeatheredCopperDoor; + mappings[755] = ItemType.WaxedOxidizedCopperDoor; + mappings[756] = ItemType.IronTrapdoor; + mappings[757] = ItemType.OakTrapdoor; + mappings[758] = ItemType.SpruceTrapdoor; + mappings[759] = ItemType.BirchTrapdoor; + mappings[760] = ItemType.JungleTrapdoor; + mappings[761] = ItemType.AcaciaTrapdoor; + mappings[762] = ItemType.CherryTrapdoor; + mappings[763] = ItemType.DarkOakTrapdoor; + mappings[764] = ItemType.PaleOakTrapdoor; + mappings[765] = ItemType.MangroveTrapdoor; + mappings[766] = ItemType.BambooTrapdoor; + mappings[767] = ItemType.CrimsonTrapdoor; + mappings[768] = ItemType.WarpedTrapdoor; + mappings[769] = ItemType.CopperTrapdoor; + mappings[770] = ItemType.ExposedCopperTrapdoor; + mappings[771] = ItemType.WeatheredCopperTrapdoor; + mappings[772] = ItemType.OxidizedCopperTrapdoor; + mappings[773] = ItemType.WaxedCopperTrapdoor; + mappings[774] = ItemType.WaxedExposedCopperTrapdoor; + mappings[775] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[776] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[777] = ItemType.OakFenceGate; + mappings[778] = ItemType.SpruceFenceGate; + mappings[779] = ItemType.BirchFenceGate; + mappings[780] = ItemType.JungleFenceGate; + mappings[781] = ItemType.AcaciaFenceGate; + mappings[782] = ItemType.CherryFenceGate; + mappings[783] = ItemType.DarkOakFenceGate; + mappings[784] = ItemType.PaleOakFenceGate; + mappings[785] = ItemType.MangroveFenceGate; + mappings[786] = ItemType.BambooFenceGate; + mappings[787] = ItemType.CrimsonFenceGate; + mappings[788] = ItemType.WarpedFenceGate; + mappings[789] = ItemType.PoweredRail; + mappings[790] = ItemType.DetectorRail; + mappings[791] = ItemType.Rail; + mappings[792] = ItemType.ActivatorRail; + mappings[793] = ItemType.Saddle; + mappings[794] = ItemType.Minecart; + mappings[795] = ItemType.ChestMinecart; + mappings[796] = ItemType.FurnaceMinecart; + mappings[797] = ItemType.TntMinecart; + mappings[798] = ItemType.HopperMinecart; + mappings[799] = ItemType.CarrotOnAStick; + mappings[800] = ItemType.WarpedFungusOnAStick; + mappings[801] = ItemType.PhantomMembrane; + mappings[802] = ItemType.Elytra; + mappings[803] = ItemType.OakBoat; + mappings[804] = ItemType.OakChestBoat; + mappings[805] = ItemType.SpruceBoat; + mappings[806] = ItemType.SpruceChestBoat; + mappings[807] = ItemType.BirchBoat; + mappings[808] = ItemType.BirchChestBoat; + mappings[809] = ItemType.JungleBoat; + mappings[810] = ItemType.JungleChestBoat; + mappings[811] = ItemType.AcaciaBoat; + mappings[812] = ItemType.AcaciaChestBoat; + mappings[813] = ItemType.CherryBoat; + mappings[814] = ItemType.CherryChestBoat; + mappings[815] = ItemType.DarkOakBoat; + mappings[816] = ItemType.DarkOakChestBoat; + mappings[817] = ItemType.PaleOakBoat; + mappings[818] = ItemType.PaleOakChestBoat; + mappings[819] = ItemType.MangroveBoat; + mappings[820] = ItemType.MangroveChestBoat; + mappings[821] = ItemType.BambooRaft; + mappings[822] = ItemType.BambooChestRaft; + mappings[823] = ItemType.StructureBlock; + mappings[824] = ItemType.Jigsaw; + mappings[825] = ItemType.TurtleHelmet; + mappings[826] = ItemType.TurtleScute; + mappings[827] = ItemType.ArmadilloScute; + mappings[828] = ItemType.WolfArmor; + mappings[829] = ItemType.FlintAndSteel; + mappings[830] = ItemType.Bowl; + mappings[831] = ItemType.Apple; + mappings[832] = ItemType.Bow; + mappings[833] = ItemType.Arrow; + mappings[834] = ItemType.Coal; + mappings[835] = ItemType.Charcoal; + mappings[836] = ItemType.Diamond; + mappings[837] = ItemType.Emerald; + mappings[838] = ItemType.LapisLazuli; + mappings[839] = ItemType.Quartz; + mappings[840] = ItemType.AmethystShard; + mappings[841] = ItemType.RawIron; + mappings[842] = ItemType.IronIngot; + mappings[843] = ItemType.RawCopper; + mappings[844] = ItemType.CopperIngot; + mappings[845] = ItemType.RawGold; + mappings[846] = ItemType.GoldIngot; + mappings[847] = ItemType.NetheriteIngot; + mappings[848] = ItemType.NetheriteScrap; + mappings[849] = ItemType.WoodenSword; + mappings[850] = ItemType.WoodenShovel; + mappings[851] = ItemType.WoodenPickaxe; + mappings[852] = ItemType.WoodenAxe; + mappings[853] = ItemType.WoodenHoe; + mappings[854] = ItemType.StoneSword; + mappings[855] = ItemType.StoneShovel; + mappings[856] = ItemType.StonePickaxe; + mappings[857] = ItemType.StoneAxe; + mappings[858] = ItemType.StoneHoe; + mappings[859] = ItemType.GoldenSword; + mappings[860] = ItemType.GoldenShovel; + mappings[861] = ItemType.GoldenPickaxe; + mappings[862] = ItemType.GoldenAxe; + mappings[863] = ItemType.GoldenHoe; + mappings[864] = ItemType.IronSword; + mappings[865] = ItemType.IronShovel; + mappings[866] = ItemType.IronPickaxe; + mappings[867] = ItemType.IronAxe; + mappings[868] = ItemType.IronHoe; + mappings[869] = ItemType.DiamondSword; + mappings[870] = ItemType.DiamondShovel; + mappings[871] = ItemType.DiamondPickaxe; + mappings[872] = ItemType.DiamondAxe; + mappings[873] = ItemType.DiamondHoe; + mappings[874] = ItemType.NetheriteSword; + mappings[875] = ItemType.NetheriteShovel; + mappings[876] = ItemType.NetheritePickaxe; + mappings[877] = ItemType.NetheriteAxe; + mappings[878] = ItemType.NetheriteHoe; + mappings[879] = ItemType.Stick; + mappings[880] = ItemType.MushroomStew; + mappings[881] = ItemType.String; + mappings[882] = ItemType.Feather; + mappings[883] = ItemType.Gunpowder; + mappings[884] = ItemType.WheatSeeds; + mappings[885] = ItemType.Wheat; + mappings[886] = ItemType.Bread; + mappings[887] = ItemType.LeatherHelmet; + mappings[888] = ItemType.LeatherChestplate; + mappings[889] = ItemType.LeatherLeggings; + mappings[890] = ItemType.LeatherBoots; + mappings[891] = ItemType.ChainmailHelmet; + mappings[892] = ItemType.ChainmailChestplate; + mappings[893] = ItemType.ChainmailLeggings; + mappings[894] = ItemType.ChainmailBoots; + mappings[895] = ItemType.IronHelmet; + mappings[896] = ItemType.IronChestplate; + mappings[897] = ItemType.IronLeggings; + mappings[898] = ItemType.IronBoots; + mappings[899] = ItemType.DiamondHelmet; + mappings[900] = ItemType.DiamondChestplate; + mappings[901] = ItemType.DiamondLeggings; + mappings[902] = ItemType.DiamondBoots; + mappings[903] = ItemType.GoldenHelmet; + mappings[904] = ItemType.GoldenChestplate; + mappings[905] = ItemType.GoldenLeggings; + mappings[906] = ItemType.GoldenBoots; + mappings[907] = ItemType.NetheriteHelmet; + mappings[908] = ItemType.NetheriteChestplate; + mappings[909] = ItemType.NetheriteLeggings; + mappings[910] = ItemType.NetheriteBoots; + mappings[911] = ItemType.Flint; + mappings[912] = ItemType.Porkchop; + mappings[913] = ItemType.CookedPorkchop; + mappings[914] = ItemType.Painting; + mappings[915] = ItemType.GoldenApple; + mappings[916] = ItemType.EnchantedGoldenApple; + mappings[917] = ItemType.OakSign; + mappings[918] = ItemType.SpruceSign; + mappings[919] = ItemType.BirchSign; + mappings[920] = ItemType.JungleSign; + mappings[921] = ItemType.AcaciaSign; + mappings[922] = ItemType.CherrySign; + mappings[923] = ItemType.DarkOakSign; + mappings[924] = ItemType.PaleOakSign; + mappings[925] = ItemType.MangroveSign; + mappings[926] = ItemType.BambooSign; + mappings[927] = ItemType.CrimsonSign; + mappings[928] = ItemType.WarpedSign; + mappings[929] = ItemType.OakHangingSign; + mappings[930] = ItemType.SpruceHangingSign; + mappings[931] = ItemType.BirchHangingSign; + mappings[932] = ItemType.JungleHangingSign; + mappings[933] = ItemType.AcaciaHangingSign; + mappings[934] = ItemType.CherryHangingSign; + mappings[935] = ItemType.DarkOakHangingSign; + mappings[936] = ItemType.PaleOakHangingSign; + mappings[937] = ItemType.MangroveHangingSign; + mappings[938] = ItemType.BambooHangingSign; + mappings[939] = ItemType.CrimsonHangingSign; + mappings[940] = ItemType.WarpedHangingSign; + mappings[941] = ItemType.Bucket; + mappings[942] = ItemType.WaterBucket; + mappings[943] = ItemType.LavaBucket; + mappings[944] = ItemType.PowderSnowBucket; + mappings[945] = ItemType.Snowball; + mappings[946] = ItemType.Leather; + mappings[947] = ItemType.MilkBucket; + mappings[948] = ItemType.PufferfishBucket; + mappings[949] = ItemType.SalmonBucket; + mappings[950] = ItemType.CodBucket; + mappings[951] = ItemType.TropicalFishBucket; + mappings[952] = ItemType.AxolotlBucket; + mappings[953] = ItemType.TadpoleBucket; + mappings[954] = ItemType.Brick; + mappings[955] = ItemType.ClayBall; + mappings[956] = ItemType.DriedKelpBlock; + mappings[957] = ItemType.Paper; + mappings[958] = ItemType.Book; + mappings[959] = ItemType.SlimeBall; + mappings[960] = ItemType.Egg; + mappings[961] = ItemType.Compass; + mappings[962] = ItemType.RecoveryCompass; + mappings[963] = ItemType.Bundle; + mappings[964] = ItemType.WhiteBundle; + mappings[965] = ItemType.OrangeBundle; + mappings[966] = ItemType.MagentaBundle; + mappings[967] = ItemType.LightBlueBundle; + mappings[968] = ItemType.YellowBundle; + mappings[969] = ItemType.LimeBundle; + mappings[970] = ItemType.PinkBundle; + mappings[971] = ItemType.GrayBundle; + mappings[972] = ItemType.LightGrayBundle; + mappings[973] = ItemType.CyanBundle; + mappings[974] = ItemType.PurpleBundle; + mappings[975] = ItemType.BlueBundle; + mappings[976] = ItemType.BrownBundle; + mappings[977] = ItemType.GreenBundle; + mappings[978] = ItemType.RedBundle; + mappings[979] = ItemType.BlackBundle; + mappings[980] = ItemType.FishingRod; + mappings[981] = ItemType.Clock; + mappings[982] = ItemType.Spyglass; + mappings[983] = ItemType.GlowstoneDust; + mappings[984] = ItemType.Cod; + mappings[985] = ItemType.Salmon; + mappings[986] = ItemType.TropicalFish; + mappings[987] = ItemType.Pufferfish; + mappings[988] = ItemType.CookedCod; + mappings[989] = ItemType.CookedSalmon; + mappings[990] = ItemType.InkSac; + mappings[991] = ItemType.GlowInkSac; + mappings[992] = ItemType.CocoaBeans; + mappings[993] = ItemType.WhiteDye; + mappings[994] = ItemType.OrangeDye; + mappings[995] = ItemType.MagentaDye; + mappings[996] = ItemType.LightBlueDye; + mappings[997] = ItemType.YellowDye; + mappings[998] = ItemType.LimeDye; + mappings[999] = ItemType.PinkDye; + mappings[1000] = ItemType.GrayDye; + mappings[1001] = ItemType.LightGrayDye; + mappings[1002] = ItemType.CyanDye; + mappings[1003] = ItemType.PurpleDye; + mappings[1004] = ItemType.BlueDye; + mappings[1005] = ItemType.BrownDye; + mappings[1006] = ItemType.GreenDye; + mappings[1007] = ItemType.RedDye; + mappings[1008] = ItemType.BlackDye; + mappings[1009] = ItemType.BoneMeal; + mappings[1010] = ItemType.Bone; + mappings[1011] = ItemType.Sugar; + mappings[1012] = ItemType.Cake; + mappings[1013] = ItemType.WhiteBed; + mappings[1014] = ItemType.OrangeBed; + mappings[1015] = ItemType.MagentaBed; + mappings[1016] = ItemType.LightBlueBed; + mappings[1017] = ItemType.YellowBed; + mappings[1018] = ItemType.LimeBed; + mappings[1019] = ItemType.PinkBed; + mappings[1020] = ItemType.GrayBed; + mappings[1021] = ItemType.LightGrayBed; + mappings[1022] = ItemType.CyanBed; + mappings[1023] = ItemType.PurpleBed; + mappings[1024] = ItemType.BlueBed; + mappings[1025] = ItemType.BrownBed; + mappings[1026] = ItemType.GreenBed; + mappings[1027] = ItemType.RedBed; + mappings[1028] = ItemType.BlackBed; + mappings[1029] = ItemType.Cookie; + mappings[1030] = ItemType.Crafter; + mappings[1031] = ItemType.FilledMap; + mappings[1032] = ItemType.Shears; + mappings[1033] = ItemType.MelonSlice; + mappings[1034] = ItemType.DriedKelp; + mappings[1035] = ItemType.PumpkinSeeds; + mappings[1036] = ItemType.MelonSeeds; + mappings[1037] = ItemType.Beef; + mappings[1038] = ItemType.CookedBeef; + mappings[1039] = ItemType.Chicken; + mappings[1040] = ItemType.CookedChicken; + mappings[1041] = ItemType.RottenFlesh; + mappings[1042] = ItemType.EnderPearl; + mappings[1043] = ItemType.BlazeRod; + mappings[1044] = ItemType.GhastTear; + mappings[1045] = ItemType.GoldNugget; + mappings[1046] = ItemType.NetherWart; + mappings[1047] = ItemType.GlassBottle; + mappings[1048] = ItemType.Potion; + mappings[1049] = ItemType.SpiderEye; + mappings[1050] = ItemType.FermentedSpiderEye; + mappings[1051] = ItemType.BlazePowder; + mappings[1052] = ItemType.MagmaCream; + mappings[1053] = ItemType.BrewingStand; + mappings[1054] = ItemType.Cauldron; + mappings[1055] = ItemType.EnderEye; + mappings[1056] = ItemType.GlisteringMelonSlice; + mappings[1057] = ItemType.ArmadilloSpawnEgg; + mappings[1058] = ItemType.AllaySpawnEgg; + mappings[1059] = ItemType.AxolotlSpawnEgg; + mappings[1060] = ItemType.BatSpawnEgg; + mappings[1061] = ItemType.BeeSpawnEgg; + mappings[1062] = ItemType.BlazeSpawnEgg; + mappings[1063] = ItemType.BoggedSpawnEgg; + mappings[1064] = ItemType.BreezeSpawnEgg; + mappings[1065] = ItemType.CatSpawnEgg; + mappings[1066] = ItemType.CamelSpawnEgg; + mappings[1067] = ItemType.CaveSpiderSpawnEgg; + mappings[1068] = ItemType.ChickenSpawnEgg; + mappings[1069] = ItemType.CodSpawnEgg; + mappings[1070] = ItemType.CowSpawnEgg; + mappings[1071] = ItemType.CreeperSpawnEgg; + mappings[1072] = ItemType.DolphinSpawnEgg; + mappings[1073] = ItemType.DonkeySpawnEgg; + mappings[1074] = ItemType.DrownedSpawnEgg; + mappings[1075] = ItemType.ElderGuardianSpawnEgg; + mappings[1076] = ItemType.EnderDragonSpawnEgg; + mappings[1077] = ItemType.EndermanSpawnEgg; + mappings[1078] = ItemType.EndermiteSpawnEgg; + mappings[1079] = ItemType.EvokerSpawnEgg; + mappings[1080] = ItemType.FoxSpawnEgg; + mappings[1081] = ItemType.FrogSpawnEgg; + mappings[1082] = ItemType.GhastSpawnEgg; + mappings[1083] = ItemType.GlowSquidSpawnEgg; + mappings[1084] = ItemType.GoatSpawnEgg; + mappings[1085] = ItemType.GuardianSpawnEgg; + mappings[1086] = ItemType.HoglinSpawnEgg; + mappings[1087] = ItemType.HorseSpawnEgg; + mappings[1088] = ItemType.HuskSpawnEgg; + mappings[1089] = ItemType.IronGolemSpawnEgg; + mappings[1090] = ItemType.LlamaSpawnEgg; + mappings[1091] = ItemType.MagmaCubeSpawnEgg; + mappings[1092] = ItemType.MooshroomSpawnEgg; + mappings[1093] = ItemType.MuleSpawnEgg; + mappings[1094] = ItemType.OcelotSpawnEgg; + mappings[1095] = ItemType.PandaSpawnEgg; + mappings[1096] = ItemType.ParrotSpawnEgg; + mappings[1097] = ItemType.PhantomSpawnEgg; + mappings[1098] = ItemType.PigSpawnEgg; + mappings[1099] = ItemType.PiglinSpawnEgg; + mappings[1100] = ItemType.PiglinBruteSpawnEgg; + mappings[1101] = ItemType.PillagerSpawnEgg; + mappings[1102] = ItemType.PolarBearSpawnEgg; + mappings[1103] = ItemType.PufferfishSpawnEgg; + mappings[1104] = ItemType.RabbitSpawnEgg; + mappings[1105] = ItemType.RavagerSpawnEgg; + mappings[1106] = ItemType.SalmonSpawnEgg; + mappings[1107] = ItemType.SheepSpawnEgg; + mappings[1108] = ItemType.ShulkerSpawnEgg; + mappings[1109] = ItemType.SilverfishSpawnEgg; + mappings[1110] = ItemType.SkeletonSpawnEgg; + mappings[1111] = ItemType.SkeletonHorseSpawnEgg; + mappings[1112] = ItemType.SlimeSpawnEgg; + mappings[1113] = ItemType.SnifferSpawnEgg; + mappings[1114] = ItemType.SnowGolemSpawnEgg; + mappings[1115] = ItemType.SpiderSpawnEgg; + mappings[1116] = ItemType.SquidSpawnEgg; + mappings[1117] = ItemType.StraySpawnEgg; + mappings[1118] = ItemType.StriderSpawnEgg; + mappings[1119] = ItemType.TadpoleSpawnEgg; + mappings[1120] = ItemType.TraderLlamaSpawnEgg; + mappings[1121] = ItemType.TropicalFishSpawnEgg; + mappings[1122] = ItemType.TurtleSpawnEgg; + mappings[1123] = ItemType.VexSpawnEgg; + mappings[1124] = ItemType.VillagerSpawnEgg; + mappings[1125] = ItemType.VindicatorSpawnEgg; + mappings[1126] = ItemType.WanderingTraderSpawnEgg; + mappings[1127] = ItemType.WardenSpawnEgg; + mappings[1128] = ItemType.WitchSpawnEgg; + mappings[1129] = ItemType.WitherSpawnEgg; + mappings[1130] = ItemType.WitherSkeletonSpawnEgg; + mappings[1131] = ItemType.WolfSpawnEgg; + mappings[1132] = ItemType.ZoglinSpawnEgg; + mappings[1133] = ItemType.CreakingSpawnEgg; + mappings[1134] = ItemType.ZombieSpawnEgg; + mappings[1135] = ItemType.ZombieHorseSpawnEgg; + mappings[1136] = ItemType.ZombieVillagerSpawnEgg; + mappings[1137] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1138] = ItemType.ExperienceBottle; + mappings[1139] = ItemType.FireCharge; + mappings[1140] = ItemType.WindCharge; + mappings[1141] = ItemType.WritableBook; + mappings[1142] = ItemType.WrittenBook; + mappings[1143] = ItemType.BreezeRod; + mappings[1144] = ItemType.Mace; + mappings[1145] = ItemType.ItemFrame; + mappings[1146] = ItemType.GlowItemFrame; + mappings[1147] = ItemType.FlowerPot; + mappings[1148] = ItemType.Carrot; + mappings[1149] = ItemType.Potato; + mappings[1150] = ItemType.BakedPotato; + mappings[1151] = ItemType.PoisonousPotato; + mappings[1152] = ItemType.Map; + mappings[1153] = ItemType.GoldenCarrot; + mappings[1154] = ItemType.SkeletonSkull; + mappings[1155] = ItemType.WitherSkeletonSkull; + mappings[1156] = ItemType.PlayerHead; + mappings[1157] = ItemType.ZombieHead; + mappings[1158] = ItemType.CreeperHead; + mappings[1159] = ItemType.DragonHead; + mappings[1160] = ItemType.PiglinHead; + mappings[1161] = ItemType.NetherStar; + mappings[1162] = ItemType.PumpkinPie; + mappings[1163] = ItemType.FireworkRocket; + mappings[1164] = ItemType.FireworkStar; + mappings[1165] = ItemType.EnchantedBook; + mappings[1166] = ItemType.NetherBrick; + mappings[1167] = ItemType.ResinBrick; + mappings[1168] = ItemType.PrismarineShard; + mappings[1169] = ItemType.PrismarineCrystals; + mappings[1170] = ItemType.Rabbit; + mappings[1171] = ItemType.CookedRabbit; + mappings[1172] = ItemType.RabbitStew; + mappings[1173] = ItemType.RabbitFoot; + mappings[1174] = ItemType.RabbitHide; + mappings[1175] = ItemType.ArmorStand; + mappings[1176] = ItemType.IronHorseArmor; + mappings[1177] = ItemType.GoldenHorseArmor; + mappings[1178] = ItemType.DiamondHorseArmor; + mappings[1179] = ItemType.LeatherHorseArmor; + mappings[1180] = ItemType.Lead; + mappings[1181] = ItemType.NameTag; + mappings[1182] = ItemType.CommandBlockMinecart; + mappings[1183] = ItemType.Mutton; + mappings[1184] = ItemType.CookedMutton; + mappings[1185] = ItemType.WhiteBanner; + mappings[1186] = ItemType.OrangeBanner; + mappings[1187] = ItemType.MagentaBanner; + mappings[1188] = ItemType.LightBlueBanner; + mappings[1189] = ItemType.YellowBanner; + mappings[1190] = ItemType.LimeBanner; + mappings[1191] = ItemType.PinkBanner; + mappings[1192] = ItemType.GrayBanner; + mappings[1193] = ItemType.LightGrayBanner; + mappings[1194] = ItemType.CyanBanner; + mappings[1195] = ItemType.PurpleBanner; + mappings[1196] = ItemType.BlueBanner; + mappings[1197] = ItemType.BrownBanner; + mappings[1198] = ItemType.GreenBanner; + mappings[1199] = ItemType.RedBanner; + mappings[1200] = ItemType.BlackBanner; + mappings[1201] = ItemType.EndCrystal; + mappings[1202] = ItemType.ChorusFruit; + mappings[1203] = ItemType.PoppedChorusFruit; + mappings[1204] = ItemType.TorchflowerSeeds; + mappings[1205] = ItemType.PitcherPod; + mappings[1206] = ItemType.Beetroot; + mappings[1207] = ItemType.BeetrootSeeds; + mappings[1208] = ItemType.BeetrootSoup; + mappings[1209] = ItemType.DragonBreath; + mappings[1210] = ItemType.SplashPotion; + mappings[1211] = ItemType.SpectralArrow; + mappings[1212] = ItemType.TippedArrow; + mappings[1213] = ItemType.LingeringPotion; + mappings[1214] = ItemType.Shield; + mappings[1215] = ItemType.TotemOfUndying; + mappings[1216] = ItemType.ShulkerShell; + mappings[1217] = ItemType.IronNugget; + mappings[1218] = ItemType.KnowledgeBook; + mappings[1219] = ItemType.DebugStick; + mappings[1220] = ItemType.MusicDisc13; + mappings[1221] = ItemType.MusicDiscCat; + mappings[1222] = ItemType.MusicDiscBlocks; + mappings[1223] = ItemType.MusicDiscChirp; + mappings[1224] = ItemType.MusicDiscCreator; + mappings[1225] = ItemType.MusicDiscCreatorMusicBox; + mappings[1226] = ItemType.MusicDiscFar; + mappings[1227] = ItemType.MusicDiscMall; + mappings[1228] = ItemType.MusicDiscMellohi; + mappings[1229] = ItemType.MusicDiscStal; + mappings[1230] = ItemType.MusicDiscStrad; + mappings[1231] = ItemType.MusicDiscWard; + mappings[1232] = ItemType.MusicDisc11; + mappings[1233] = ItemType.MusicDiscWait; + mappings[1234] = ItemType.MusicDiscOtherside; + mappings[1235] = ItemType.MusicDiscRelic; + mappings[1236] = ItemType.MusicDisc5; + mappings[1237] = ItemType.MusicDiscPigstep; + mappings[1238] = ItemType.MusicDiscPrecipice; + mappings[1239] = ItemType.DiscFragment5; + mappings[1240] = ItemType.Trident; + mappings[1241] = ItemType.NautilusShell; + mappings[1242] = ItemType.HeartOfTheSea; + mappings[1243] = ItemType.Crossbow; + mappings[1244] = ItemType.SuspiciousStew; + mappings[1245] = ItemType.Loom; + mappings[1246] = ItemType.FlowerBannerPattern; + mappings[1247] = ItemType.CreeperBannerPattern; + mappings[1248] = ItemType.SkullBannerPattern; + mappings[1249] = ItemType.MojangBannerPattern; + mappings[1250] = ItemType.GlobeBannerPattern; + mappings[1251] = ItemType.PiglinBannerPattern; + mappings[1252] = ItemType.FlowBannerPattern; + mappings[1253] = ItemType.GusterBannerPattern; + mappings[1254] = ItemType.FieldMasonedBannerPattern; + mappings[1255] = ItemType.BordureIndentedBannerPattern; + mappings[1256] = ItemType.GoatHorn; + mappings[1257] = ItemType.Composter; + mappings[1258] = ItemType.Barrel; + mappings[1259] = ItemType.Smoker; + mappings[1260] = ItemType.BlastFurnace; + mappings[1261] = ItemType.CartographyTable; + mappings[1262] = ItemType.FletchingTable; + mappings[1263] = ItemType.Grindstone; + mappings[1264] = ItemType.SmithingTable; + mappings[1265] = ItemType.Stonecutter; + mappings[1266] = ItemType.Bell; + mappings[1267] = ItemType.Lantern; + mappings[1268] = ItemType.SoulLantern; + mappings[1269] = ItemType.SweetBerries; + mappings[1270] = ItemType.GlowBerries; + mappings[1271] = ItemType.Campfire; + mappings[1272] = ItemType.SoulCampfire; + mappings[1273] = ItemType.Shroomlight; + mappings[1274] = ItemType.Honeycomb; + mappings[1275] = ItemType.BeeNest; + mappings[1276] = ItemType.Beehive; + mappings[1277] = ItemType.HoneyBottle; + mappings[1278] = ItemType.HoneycombBlock; + mappings[1279] = ItemType.Lodestone; + mappings[1280] = ItemType.CryingObsidian; + mappings[1281] = ItemType.Blackstone; + mappings[1282] = ItemType.BlackstoneSlab; + mappings[1283] = ItemType.BlackstoneStairs; + mappings[1284] = ItemType.GildedBlackstone; + mappings[1285] = ItemType.PolishedBlackstone; + mappings[1286] = ItemType.PolishedBlackstoneSlab; + mappings[1287] = ItemType.PolishedBlackstoneStairs; + mappings[1288] = ItemType.ChiseledPolishedBlackstone; + mappings[1289] = ItemType.PolishedBlackstoneBricks; + mappings[1290] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1291] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1292] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1293] = ItemType.RespawnAnchor; + mappings[1294] = ItemType.Candle; + mappings[1295] = ItemType.WhiteCandle; + mappings[1296] = ItemType.OrangeCandle; + mappings[1297] = ItemType.MagentaCandle; + mappings[1298] = ItemType.LightBlueCandle; + mappings[1299] = ItemType.YellowCandle; + mappings[1300] = ItemType.LimeCandle; + mappings[1301] = ItemType.PinkCandle; + mappings[1302] = ItemType.GrayCandle; + mappings[1303] = ItemType.LightGrayCandle; + mappings[1304] = ItemType.CyanCandle; + mappings[1305] = ItemType.PurpleCandle; + mappings[1306] = ItemType.BlueCandle; + mappings[1307] = ItemType.BrownCandle; + mappings[1308] = ItemType.GreenCandle; + mappings[1309] = ItemType.RedCandle; + mappings[1310] = ItemType.BlackCandle; + mappings[1311] = ItemType.SmallAmethystBud; + mappings[1312] = ItemType.MediumAmethystBud; + mappings[1313] = ItemType.LargeAmethystBud; + mappings[1314] = ItemType.AmethystCluster; + mappings[1315] = ItemType.PointedDripstone; + mappings[1316] = ItemType.OchreFroglight; + mappings[1317] = ItemType.VerdantFroglight; + mappings[1318] = ItemType.PearlescentFroglight; + mappings[1319] = ItemType.Frogspawn; + mappings[1320] = ItemType.EchoShard; + mappings[1321] = ItemType.Brush; + mappings[1322] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1323] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1324] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1325] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1326] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1327] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1328] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1329] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1330] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1331] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1332] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1333] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1334] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1335] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1336] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1337] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1338] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1339] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1340] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1341] = ItemType.AnglerPotterySherd; + mappings[1342] = ItemType.ArcherPotterySherd; + mappings[1343] = ItemType.ArmsUpPotterySherd; + mappings[1344] = ItemType.BladePotterySherd; + mappings[1345] = ItemType.BrewerPotterySherd; + mappings[1346] = ItemType.BurnPotterySherd; + mappings[1347] = ItemType.DangerPotterySherd; + mappings[1348] = ItemType.ExplorerPotterySherd; + mappings[1349] = ItemType.FlowPotterySherd; + mappings[1350] = ItemType.FriendPotterySherd; + mappings[1351] = ItemType.GusterPotterySherd; + mappings[1352] = ItemType.HeartPotterySherd; + mappings[1353] = ItemType.HeartbreakPotterySherd; + mappings[1354] = ItemType.HowlPotterySherd; + mappings[1355] = ItemType.MinerPotterySherd; + mappings[1356] = ItemType.MournerPotterySherd; + mappings[1357] = ItemType.PlentyPotterySherd; + mappings[1358] = ItemType.PrizePotterySherd; + mappings[1359] = ItemType.ScrapePotterySherd; + mappings[1360] = ItemType.SheafPotterySherd; + mappings[1361] = ItemType.ShelterPotterySherd; + mappings[1362] = ItemType.SkullPotterySherd; + mappings[1363] = ItemType.SnortPotterySherd; + mappings[1364] = ItemType.CopperGrate; + mappings[1365] = ItemType.ExposedCopperGrate; + mappings[1366] = ItemType.WeatheredCopperGrate; + mappings[1367] = ItemType.OxidizedCopperGrate; + mappings[1368] = ItemType.WaxedCopperGrate; + mappings[1369] = ItemType.WaxedExposedCopperGrate; + mappings[1370] = ItemType.WaxedWeatheredCopperGrate; + mappings[1371] = ItemType.WaxedOxidizedCopperGrate; + mappings[1372] = ItemType.CopperBulb; + mappings[1373] = ItemType.ExposedCopperBulb; + mappings[1374] = ItemType.WeatheredCopperBulb; + mappings[1375] = ItemType.OxidizedCopperBulb; + mappings[1376] = ItemType.WaxedCopperBulb; + mappings[1377] = ItemType.WaxedExposedCopperBulb; + mappings[1378] = ItemType.WaxedWeatheredCopperBulb; + mappings[1379] = ItemType.WaxedOxidizedCopperBulb; + mappings[1380] = ItemType.TrialSpawner; + mappings[1381] = ItemType.TrialKey; + mappings[1382] = ItemType.OminousTrialKey; + mappings[1383] = ItemType.Vault; + mappings[1384] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1215.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1215.cs new file mode 100644 index 00000000..19795e50 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1215.cs @@ -0,0 +1,1414 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1215 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1215() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.DryShortGrass; + mappings[210] = ItemType.DryTallGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.OpenEyeblossom; + mappings[231] = ItemType.ClosedEyeblossom; + mappings[232] = ItemType.Poppy; + mappings[233] = ItemType.BlueOrchid; + mappings[234] = ItemType.Allium; + mappings[235] = ItemType.AzureBluet; + mappings[236] = ItemType.RedTulip; + mappings[237] = ItemType.OrangeTulip; + mappings[238] = ItemType.WhiteTulip; + mappings[239] = ItemType.PinkTulip; + mappings[240] = ItemType.OxeyeDaisy; + mappings[241] = ItemType.Cornflower; + mappings[242] = ItemType.LilyOfTheValley; + mappings[243] = ItemType.WitherRose; + mappings[244] = ItemType.Torchflower; + mappings[245] = ItemType.PitcherPlant; + mappings[246] = ItemType.SporeBlossom; + mappings[247] = ItemType.BrownMushroom; + mappings[248] = ItemType.RedMushroom; + mappings[249] = ItemType.CrimsonFungus; + mappings[250] = ItemType.WarpedFungus; + mappings[251] = ItemType.CrimsonRoots; + mappings[252] = ItemType.WarpedRoots; + mappings[253] = ItemType.NetherSprouts; + mappings[254] = ItemType.WeepingVines; + mappings[255] = ItemType.TwistingVines; + mappings[256] = ItemType.SugarCane; + mappings[257] = ItemType.Kelp; + mappings[258] = ItemType.PinkPetals; + mappings[259] = ItemType.Wildflowers; + mappings[260] = ItemType.LeafLitter; + mappings[261] = ItemType.MossCarpet; + mappings[262] = ItemType.MossBlock; + mappings[263] = ItemType.PaleMossCarpet; + mappings[264] = ItemType.PaleHangingMoss; + mappings[265] = ItemType.PaleMossBlock; + mappings[266] = ItemType.HangingRoots; + mappings[267] = ItemType.BigDripleaf; + mappings[268] = ItemType.SmallDripleaf; + mappings[269] = ItemType.Bamboo; + mappings[270] = ItemType.OakSlab; + mappings[271] = ItemType.SpruceSlab; + mappings[272] = ItemType.BirchSlab; + mappings[273] = ItemType.JungleSlab; + mappings[274] = ItemType.AcaciaSlab; + mappings[275] = ItemType.CherrySlab; + mappings[276] = ItemType.DarkOakSlab; + mappings[277] = ItemType.PaleOakSlab; + mappings[278] = ItemType.MangroveSlab; + mappings[279] = ItemType.BambooSlab; + mappings[280] = ItemType.BambooMosaicSlab; + mappings[281] = ItemType.CrimsonSlab; + mappings[282] = ItemType.WarpedSlab; + mappings[283] = ItemType.StoneSlab; + mappings[284] = ItemType.SmoothStoneSlab; + mappings[285] = ItemType.SandstoneSlab; + mappings[286] = ItemType.CutSandstoneSlab; + mappings[287] = ItemType.PetrifiedOakSlab; + mappings[288] = ItemType.CobblestoneSlab; + mappings[289] = ItemType.BrickSlab; + mappings[290] = ItemType.StoneBrickSlab; + mappings[291] = ItemType.MudBrickSlab; + mappings[292] = ItemType.NetherBrickSlab; + mappings[293] = ItemType.QuartzSlab; + mappings[294] = ItemType.RedSandstoneSlab; + mappings[295] = ItemType.CutRedSandstoneSlab; + mappings[296] = ItemType.PurpurSlab; + mappings[297] = ItemType.PrismarineSlab; + mappings[298] = ItemType.PrismarineBrickSlab; + mappings[299] = ItemType.DarkPrismarineSlab; + mappings[300] = ItemType.SmoothQuartz; + mappings[301] = ItemType.SmoothRedSandstone; + mappings[302] = ItemType.SmoothSandstone; + mappings[303] = ItemType.SmoothStone; + mappings[304] = ItemType.Bricks; + mappings[305] = ItemType.Bookshelf; + mappings[306] = ItemType.ChiseledBookshelf; + mappings[307] = ItemType.DecoratedPot; + mappings[308] = ItemType.MossyCobblestone; + mappings[309] = ItemType.Obsidian; + mappings[310] = ItemType.Torch; + mappings[311] = ItemType.EndRod; + mappings[312] = ItemType.ChorusPlant; + mappings[313] = ItemType.ChorusFlower; + mappings[314] = ItemType.PurpurBlock; + mappings[315] = ItemType.PurpurPillar; + mappings[316] = ItemType.PurpurStairs; + mappings[317] = ItemType.Spawner; + mappings[318] = ItemType.CreakingHeart; + mappings[319] = ItemType.Chest; + mappings[320] = ItemType.CraftingTable; + mappings[321] = ItemType.Farmland; + mappings[322] = ItemType.Furnace; + mappings[323] = ItemType.Ladder; + mappings[324] = ItemType.CobblestoneStairs; + mappings[325] = ItemType.Snow; + mappings[326] = ItemType.Ice; + mappings[327] = ItemType.SnowBlock; + mappings[328] = ItemType.Cactus; + mappings[329] = ItemType.CactusFlower; + mappings[330] = ItemType.Clay; + mappings[331] = ItemType.Jukebox; + mappings[332] = ItemType.OakFence; + mappings[333] = ItemType.SpruceFence; + mappings[334] = ItemType.BirchFence; + mappings[335] = ItemType.JungleFence; + mappings[336] = ItemType.AcaciaFence; + mappings[337] = ItemType.CherryFence; + mappings[338] = ItemType.DarkOakFence; + mappings[339] = ItemType.PaleOakFence; + mappings[340] = ItemType.MangroveFence; + mappings[341] = ItemType.BambooFence; + mappings[342] = ItemType.CrimsonFence; + mappings[343] = ItemType.WarpedFence; + mappings[344] = ItemType.Pumpkin; + mappings[345] = ItemType.CarvedPumpkin; + mappings[346] = ItemType.JackOLantern; + mappings[347] = ItemType.Netherrack; + mappings[348] = ItemType.SoulSand; + mappings[349] = ItemType.SoulSoil; + mappings[350] = ItemType.Basalt; + mappings[351] = ItemType.PolishedBasalt; + mappings[352] = ItemType.SmoothBasalt; + mappings[353] = ItemType.SoulTorch; + mappings[354] = ItemType.Glowstone; + mappings[355] = ItemType.InfestedStone; + mappings[356] = ItemType.InfestedCobblestone; + mappings[357] = ItemType.InfestedStoneBricks; + mappings[358] = ItemType.InfestedMossyStoneBricks; + mappings[359] = ItemType.InfestedCrackedStoneBricks; + mappings[360] = ItemType.InfestedChiseledStoneBricks; + mappings[361] = ItemType.InfestedDeepslate; + mappings[362] = ItemType.StoneBricks; + mappings[363] = ItemType.MossyStoneBricks; + mappings[364] = ItemType.CrackedStoneBricks; + mappings[365] = ItemType.ChiseledStoneBricks; + mappings[366] = ItemType.PackedMud; + mappings[367] = ItemType.MudBricks; + mappings[368] = ItemType.DeepslateBricks; + mappings[369] = ItemType.CrackedDeepslateBricks; + mappings[370] = ItemType.DeepslateTiles; + mappings[371] = ItemType.CrackedDeepslateTiles; + mappings[372] = ItemType.ChiseledDeepslate; + mappings[373] = ItemType.ReinforcedDeepslate; + mappings[374] = ItemType.BrownMushroomBlock; + mappings[375] = ItemType.RedMushroomBlock; + mappings[376] = ItemType.MushroomStem; + mappings[377] = ItemType.IronBars; + mappings[378] = ItemType.Chain; + mappings[379] = ItemType.GlassPane; + mappings[380] = ItemType.Melon; + mappings[381] = ItemType.Vine; + mappings[382] = ItemType.GlowLichen; + mappings[383] = ItemType.ResinClump; + mappings[384] = ItemType.ResinBlock; + mappings[385] = ItemType.ResinBricks; + mappings[386] = ItemType.ResinBrickStairs; + mappings[387] = ItemType.ResinBrickSlab; + mappings[388] = ItemType.ResinBrickWall; + mappings[389] = ItemType.ChiseledResinBricks; + mappings[390] = ItemType.BrickStairs; + mappings[391] = ItemType.StoneBrickStairs; + mappings[392] = ItemType.MudBrickStairs; + mappings[393] = ItemType.Mycelium; + mappings[394] = ItemType.LilyPad; + mappings[395] = ItemType.NetherBricks; + mappings[396] = ItemType.CrackedNetherBricks; + mappings[397] = ItemType.ChiseledNetherBricks; + mappings[398] = ItemType.NetherBrickFence; + mappings[399] = ItemType.NetherBrickStairs; + mappings[400] = ItemType.Sculk; + mappings[401] = ItemType.SculkVein; + mappings[402] = ItemType.SculkCatalyst; + mappings[403] = ItemType.SculkShrieker; + mappings[404] = ItemType.EnchantingTable; + mappings[405] = ItemType.EndPortalFrame; + mappings[406] = ItemType.EndStone; + mappings[407] = ItemType.EndStoneBricks; + mappings[408] = ItemType.DragonEgg; + mappings[409] = ItemType.SandstoneStairs; + mappings[410] = ItemType.EnderChest; + mappings[411] = ItemType.EmeraldBlock; + mappings[412] = ItemType.OakStairs; + mappings[413] = ItemType.SpruceStairs; + mappings[414] = ItemType.BirchStairs; + mappings[415] = ItemType.JungleStairs; + mappings[416] = ItemType.AcaciaStairs; + mappings[417] = ItemType.CherryStairs; + mappings[418] = ItemType.DarkOakStairs; + mappings[419] = ItemType.PaleOakStairs; + mappings[420] = ItemType.MangroveStairs; + mappings[421] = ItemType.BambooStairs; + mappings[422] = ItemType.BambooMosaicStairs; + mappings[423] = ItemType.CrimsonStairs; + mappings[424] = ItemType.WarpedStairs; + mappings[425] = ItemType.CommandBlock; + mappings[426] = ItemType.Beacon; + mappings[427] = ItemType.CobblestoneWall; + mappings[428] = ItemType.MossyCobblestoneWall; + mappings[429] = ItemType.BrickWall; + mappings[430] = ItemType.PrismarineWall; + mappings[431] = ItemType.RedSandstoneWall; + mappings[432] = ItemType.MossyStoneBrickWall; + mappings[433] = ItemType.GraniteWall; + mappings[434] = ItemType.StoneBrickWall; + mappings[435] = ItemType.MudBrickWall; + mappings[436] = ItemType.NetherBrickWall; + mappings[437] = ItemType.AndesiteWall; + mappings[438] = ItemType.RedNetherBrickWall; + mappings[439] = ItemType.SandstoneWall; + mappings[440] = ItemType.EndStoneBrickWall; + mappings[441] = ItemType.DioriteWall; + mappings[442] = ItemType.BlackstoneWall; + mappings[443] = ItemType.PolishedBlackstoneWall; + mappings[444] = ItemType.PolishedBlackstoneBrickWall; + mappings[445] = ItemType.CobbledDeepslateWall; + mappings[446] = ItemType.PolishedDeepslateWall; + mappings[447] = ItemType.DeepslateBrickWall; + mappings[448] = ItemType.DeepslateTileWall; + mappings[449] = ItemType.Anvil; + mappings[450] = ItemType.ChippedAnvil; + mappings[451] = ItemType.DamagedAnvil; + mappings[452] = ItemType.ChiseledQuartzBlock; + mappings[453] = ItemType.QuartzBlock; + mappings[454] = ItemType.QuartzBricks; + mappings[455] = ItemType.QuartzPillar; + mappings[456] = ItemType.QuartzStairs; + mappings[457] = ItemType.WhiteTerracotta; + mappings[458] = ItemType.OrangeTerracotta; + mappings[459] = ItemType.MagentaTerracotta; + mappings[460] = ItemType.LightBlueTerracotta; + mappings[461] = ItemType.YellowTerracotta; + mappings[462] = ItemType.LimeTerracotta; + mappings[463] = ItemType.PinkTerracotta; + mappings[464] = ItemType.GrayTerracotta; + mappings[465] = ItemType.LightGrayTerracotta; + mappings[466] = ItemType.CyanTerracotta; + mappings[467] = ItemType.PurpleTerracotta; + mappings[468] = ItemType.BlueTerracotta; + mappings[469] = ItemType.BrownTerracotta; + mappings[470] = ItemType.GreenTerracotta; + mappings[471] = ItemType.RedTerracotta; + mappings[472] = ItemType.BlackTerracotta; + mappings[473] = ItemType.Barrier; + mappings[474] = ItemType.Light; + mappings[475] = ItemType.HayBlock; + mappings[476] = ItemType.WhiteCarpet; + mappings[477] = ItemType.OrangeCarpet; + mappings[478] = ItemType.MagentaCarpet; + mappings[479] = ItemType.LightBlueCarpet; + mappings[480] = ItemType.YellowCarpet; + mappings[481] = ItemType.LimeCarpet; + mappings[482] = ItemType.PinkCarpet; + mappings[483] = ItemType.GrayCarpet; + mappings[484] = ItemType.LightGrayCarpet; + mappings[485] = ItemType.CyanCarpet; + mappings[486] = ItemType.PurpleCarpet; + mappings[487] = ItemType.BlueCarpet; + mappings[488] = ItemType.BrownCarpet; + mappings[489] = ItemType.GreenCarpet; + mappings[490] = ItemType.RedCarpet; + mappings[491] = ItemType.BlackCarpet; + mappings[492] = ItemType.Terracotta; + mappings[493] = ItemType.PackedIce; + mappings[494] = ItemType.DirtPath; + mappings[495] = ItemType.Sunflower; + mappings[496] = ItemType.Lilac; + mappings[497] = ItemType.RoseBush; + mappings[498] = ItemType.Peony; + mappings[499] = ItemType.TallGrass; + mappings[500] = ItemType.LargeFern; + mappings[501] = ItemType.WhiteStainedGlass; + mappings[502] = ItemType.OrangeStainedGlass; + mappings[503] = ItemType.MagentaStainedGlass; + mappings[504] = ItemType.LightBlueStainedGlass; + mappings[505] = ItemType.YellowStainedGlass; + mappings[506] = ItemType.LimeStainedGlass; + mappings[507] = ItemType.PinkStainedGlass; + mappings[508] = ItemType.GrayStainedGlass; + mappings[509] = ItemType.LightGrayStainedGlass; + mappings[510] = ItemType.CyanStainedGlass; + mappings[511] = ItemType.PurpleStainedGlass; + mappings[512] = ItemType.BlueStainedGlass; + mappings[513] = ItemType.BrownStainedGlass; + mappings[514] = ItemType.GreenStainedGlass; + mappings[515] = ItemType.RedStainedGlass; + mappings[516] = ItemType.BlackStainedGlass; + mappings[517] = ItemType.WhiteStainedGlassPane; + mappings[518] = ItemType.OrangeStainedGlassPane; + mappings[519] = ItemType.MagentaStainedGlassPane; + mappings[520] = ItemType.LightBlueStainedGlassPane; + mappings[521] = ItemType.YellowStainedGlassPane; + mappings[522] = ItemType.LimeStainedGlassPane; + mappings[523] = ItemType.PinkStainedGlassPane; + mappings[524] = ItemType.GrayStainedGlassPane; + mappings[525] = ItemType.LightGrayStainedGlassPane; + mappings[526] = ItemType.CyanStainedGlassPane; + mappings[527] = ItemType.PurpleStainedGlassPane; + mappings[528] = ItemType.BlueStainedGlassPane; + mappings[529] = ItemType.BrownStainedGlassPane; + mappings[530] = ItemType.GreenStainedGlassPane; + mappings[531] = ItemType.RedStainedGlassPane; + mappings[532] = ItemType.BlackStainedGlassPane; + mappings[533] = ItemType.Prismarine; + mappings[534] = ItemType.PrismarineBricks; + mappings[535] = ItemType.DarkPrismarine; + mappings[536] = ItemType.PrismarineStairs; + mappings[537] = ItemType.PrismarineBrickStairs; + mappings[538] = ItemType.DarkPrismarineStairs; + mappings[539] = ItemType.SeaLantern; + mappings[540] = ItemType.RedSandstone; + mappings[541] = ItemType.ChiseledRedSandstone; + mappings[542] = ItemType.CutRedSandstone; + mappings[543] = ItemType.RedSandstoneStairs; + mappings[544] = ItemType.RepeatingCommandBlock; + mappings[545] = ItemType.ChainCommandBlock; + mappings[546] = ItemType.MagmaBlock; + mappings[547] = ItemType.NetherWartBlock; + mappings[548] = ItemType.WarpedWartBlock; + mappings[549] = ItemType.RedNetherBricks; + mappings[550] = ItemType.BoneBlock; + mappings[551] = ItemType.StructureVoid; + mappings[552] = ItemType.ShulkerBox; + mappings[553] = ItemType.WhiteShulkerBox; + mappings[554] = ItemType.OrangeShulkerBox; + mappings[555] = ItemType.MagentaShulkerBox; + mappings[556] = ItemType.LightBlueShulkerBox; + mappings[557] = ItemType.YellowShulkerBox; + mappings[558] = ItemType.LimeShulkerBox; + mappings[559] = ItemType.PinkShulkerBox; + mappings[560] = ItemType.GrayShulkerBox; + mappings[561] = ItemType.LightGrayShulkerBox; + mappings[562] = ItemType.CyanShulkerBox; + mappings[563] = ItemType.PurpleShulkerBox; + mappings[564] = ItemType.BlueShulkerBox; + mappings[565] = ItemType.BrownShulkerBox; + mappings[566] = ItemType.GreenShulkerBox; + mappings[567] = ItemType.RedShulkerBox; + mappings[568] = ItemType.BlackShulkerBox; + mappings[569] = ItemType.WhiteGlazedTerracotta; + mappings[570] = ItemType.OrangeGlazedTerracotta; + mappings[571] = ItemType.MagentaGlazedTerracotta; + mappings[572] = ItemType.LightBlueGlazedTerracotta; + mappings[573] = ItemType.YellowGlazedTerracotta; + mappings[574] = ItemType.LimeGlazedTerracotta; + mappings[575] = ItemType.PinkGlazedTerracotta; + mappings[576] = ItemType.GrayGlazedTerracotta; + mappings[577] = ItemType.LightGrayGlazedTerracotta; + mappings[578] = ItemType.CyanGlazedTerracotta; + mappings[579] = ItemType.PurpleGlazedTerracotta; + mappings[580] = ItemType.BlueGlazedTerracotta; + mappings[581] = ItemType.BrownGlazedTerracotta; + mappings[582] = ItemType.GreenGlazedTerracotta; + mappings[583] = ItemType.RedGlazedTerracotta; + mappings[584] = ItemType.BlackGlazedTerracotta; + mappings[585] = ItemType.WhiteConcrete; + mappings[586] = ItemType.OrangeConcrete; + mappings[587] = ItemType.MagentaConcrete; + mappings[588] = ItemType.LightBlueConcrete; + mappings[589] = ItemType.YellowConcrete; + mappings[590] = ItemType.LimeConcrete; + mappings[591] = ItemType.PinkConcrete; + mappings[592] = ItemType.GrayConcrete; + mappings[593] = ItemType.LightGrayConcrete; + mappings[594] = ItemType.CyanConcrete; + mappings[595] = ItemType.PurpleConcrete; + mappings[596] = ItemType.BlueConcrete; + mappings[597] = ItemType.BrownConcrete; + mappings[598] = ItemType.GreenConcrete; + mappings[599] = ItemType.RedConcrete; + mappings[600] = ItemType.BlackConcrete; + mappings[601] = ItemType.WhiteConcretePowder; + mappings[602] = ItemType.OrangeConcretePowder; + mappings[603] = ItemType.MagentaConcretePowder; + mappings[604] = ItemType.LightBlueConcretePowder; + mappings[605] = ItemType.YellowConcretePowder; + mappings[606] = ItemType.LimeConcretePowder; + mappings[607] = ItemType.PinkConcretePowder; + mappings[608] = ItemType.GrayConcretePowder; + mappings[609] = ItemType.LightGrayConcretePowder; + mappings[610] = ItemType.CyanConcretePowder; + mappings[611] = ItemType.PurpleConcretePowder; + mappings[612] = ItemType.BlueConcretePowder; + mappings[613] = ItemType.BrownConcretePowder; + mappings[614] = ItemType.GreenConcretePowder; + mappings[615] = ItemType.RedConcretePowder; + mappings[616] = ItemType.BlackConcretePowder; + mappings[617] = ItemType.TurtleEgg; + mappings[618] = ItemType.SnifferEgg; + mappings[619] = ItemType.DeadTubeCoralBlock; + mappings[620] = ItemType.DeadBrainCoralBlock; + mappings[621] = ItemType.DeadBubbleCoralBlock; + mappings[622] = ItemType.DeadFireCoralBlock; + mappings[623] = ItemType.DeadHornCoralBlock; + mappings[624] = ItemType.TubeCoralBlock; + mappings[625] = ItemType.BrainCoralBlock; + mappings[626] = ItemType.BubbleCoralBlock; + mappings[627] = ItemType.FireCoralBlock; + mappings[628] = ItemType.HornCoralBlock; + mappings[629] = ItemType.TubeCoral; + mappings[630] = ItemType.BrainCoral; + mappings[631] = ItemType.BubbleCoral; + mappings[632] = ItemType.FireCoral; + mappings[633] = ItemType.HornCoral; + mappings[634] = ItemType.DeadBrainCoral; + mappings[635] = ItemType.DeadBubbleCoral; + mappings[636] = ItemType.DeadFireCoral; + mappings[637] = ItemType.DeadHornCoral; + mappings[638] = ItemType.DeadTubeCoral; + mappings[639] = ItemType.TubeCoralFan; + mappings[640] = ItemType.BrainCoralFan; + mappings[641] = ItemType.BubbleCoralFan; + mappings[642] = ItemType.FireCoralFan; + mappings[643] = ItemType.HornCoralFan; + mappings[644] = ItemType.DeadTubeCoralFan; + mappings[645] = ItemType.DeadBrainCoralFan; + mappings[646] = ItemType.DeadBubbleCoralFan; + mappings[647] = ItemType.DeadFireCoralFan; + mappings[648] = ItemType.DeadHornCoralFan; + mappings[649] = ItemType.BlueIce; + mappings[650] = ItemType.Conduit; + mappings[651] = ItemType.PolishedGraniteStairs; + mappings[652] = ItemType.SmoothRedSandstoneStairs; + mappings[653] = ItemType.MossyStoneBrickStairs; + mappings[654] = ItemType.PolishedDioriteStairs; + mappings[655] = ItemType.MossyCobblestoneStairs; + mappings[656] = ItemType.EndStoneBrickStairs; + mappings[657] = ItemType.StoneStairs; + mappings[658] = ItemType.SmoothSandstoneStairs; + mappings[659] = ItemType.SmoothQuartzStairs; + mappings[660] = ItemType.GraniteStairs; + mappings[661] = ItemType.AndesiteStairs; + mappings[662] = ItemType.RedNetherBrickStairs; + mappings[663] = ItemType.PolishedAndesiteStairs; + mappings[664] = ItemType.DioriteStairs; + mappings[665] = ItemType.CobbledDeepslateStairs; + mappings[666] = ItemType.PolishedDeepslateStairs; + mappings[667] = ItemType.DeepslateBrickStairs; + mappings[668] = ItemType.DeepslateTileStairs; + mappings[669] = ItemType.PolishedGraniteSlab; + mappings[670] = ItemType.SmoothRedSandstoneSlab; + mappings[671] = ItemType.MossyStoneBrickSlab; + mappings[672] = ItemType.PolishedDioriteSlab; + mappings[673] = ItemType.MossyCobblestoneSlab; + mappings[674] = ItemType.EndStoneBrickSlab; + mappings[675] = ItemType.SmoothSandstoneSlab; + mappings[676] = ItemType.SmoothQuartzSlab; + mappings[677] = ItemType.GraniteSlab; + mappings[678] = ItemType.AndesiteSlab; + mappings[679] = ItemType.RedNetherBrickSlab; + mappings[680] = ItemType.PolishedAndesiteSlab; + mappings[681] = ItemType.DioriteSlab; + mappings[682] = ItemType.CobbledDeepslateSlab; + mappings[683] = ItemType.PolishedDeepslateSlab; + mappings[684] = ItemType.DeepslateBrickSlab; + mappings[685] = ItemType.DeepslateTileSlab; + mappings[686] = ItemType.Scaffolding; + mappings[687] = ItemType.Redstone; + mappings[688] = ItemType.RedstoneTorch; + mappings[689] = ItemType.RedstoneBlock; + mappings[690] = ItemType.Repeater; + mappings[691] = ItemType.Comparator; + mappings[692] = ItemType.Piston; + mappings[693] = ItemType.StickyPiston; + mappings[694] = ItemType.SlimeBlock; + mappings[695] = ItemType.HoneyBlock; + mappings[696] = ItemType.Observer; + mappings[697] = ItemType.Hopper; + mappings[698] = ItemType.Dispenser; + mappings[699] = ItemType.Dropper; + mappings[700] = ItemType.Lectern; + mappings[701] = ItemType.Target; + mappings[702] = ItemType.Lever; + mappings[703] = ItemType.LightningRod; + mappings[704] = ItemType.DaylightDetector; + mappings[705] = ItemType.SculkSensor; + mappings[706] = ItemType.CalibratedSculkSensor; + mappings[707] = ItemType.TripwireHook; + mappings[708] = ItemType.TrappedChest; + mappings[709] = ItemType.Tnt; + mappings[710] = ItemType.RedstoneLamp; + mappings[711] = ItemType.NoteBlock; + mappings[712] = ItemType.StoneButton; + mappings[713] = ItemType.PolishedBlackstoneButton; + mappings[714] = ItemType.OakButton; + mappings[715] = ItemType.SpruceButton; + mappings[716] = ItemType.BirchButton; + mappings[717] = ItemType.JungleButton; + mappings[718] = ItemType.AcaciaButton; + mappings[719] = ItemType.CherryButton; + mappings[720] = ItemType.DarkOakButton; + mappings[721] = ItemType.PaleOakButton; + mappings[722] = ItemType.MangroveButton; + mappings[723] = ItemType.BambooButton; + mappings[724] = ItemType.CrimsonButton; + mappings[725] = ItemType.WarpedButton; + mappings[726] = ItemType.StonePressurePlate; + mappings[727] = ItemType.PolishedBlackstonePressurePlate; + mappings[728] = ItemType.LightWeightedPressurePlate; + mappings[729] = ItemType.HeavyWeightedPressurePlate; + mappings[730] = ItemType.OakPressurePlate; + mappings[731] = ItemType.SprucePressurePlate; + mappings[732] = ItemType.BirchPressurePlate; + mappings[733] = ItemType.JunglePressurePlate; + mappings[734] = ItemType.AcaciaPressurePlate; + mappings[735] = ItemType.CherryPressurePlate; + mappings[736] = ItemType.DarkOakPressurePlate; + mappings[737] = ItemType.PaleOakPressurePlate; + mappings[738] = ItemType.MangrovePressurePlate; + mappings[739] = ItemType.BambooPressurePlate; + mappings[740] = ItemType.CrimsonPressurePlate; + mappings[741] = ItemType.WarpedPressurePlate; + mappings[742] = ItemType.IronDoor; + mappings[743] = ItemType.OakDoor; + mappings[744] = ItemType.SpruceDoor; + mappings[745] = ItemType.BirchDoor; + mappings[746] = ItemType.JungleDoor; + mappings[747] = ItemType.AcaciaDoor; + mappings[748] = ItemType.CherryDoor; + mappings[749] = ItemType.DarkOakDoor; + mappings[750] = ItemType.PaleOakDoor; + mappings[751] = ItemType.MangroveDoor; + mappings[752] = ItemType.BambooDoor; + mappings[753] = ItemType.CrimsonDoor; + mappings[754] = ItemType.WarpedDoor; + mappings[755] = ItemType.CopperDoor; + mappings[756] = ItemType.ExposedCopperDoor; + mappings[757] = ItemType.WeatheredCopperDoor; + mappings[758] = ItemType.OxidizedCopperDoor; + mappings[759] = ItemType.WaxedCopperDoor; + mappings[760] = ItemType.WaxedExposedCopperDoor; + mappings[761] = ItemType.WaxedWeatheredCopperDoor; + mappings[762] = ItemType.WaxedOxidizedCopperDoor; + mappings[763] = ItemType.IronTrapdoor; + mappings[764] = ItemType.OakTrapdoor; + mappings[765] = ItemType.SpruceTrapdoor; + mappings[766] = ItemType.BirchTrapdoor; + mappings[767] = ItemType.JungleTrapdoor; + mappings[768] = ItemType.AcaciaTrapdoor; + mappings[769] = ItemType.CherryTrapdoor; + mappings[770] = ItemType.DarkOakTrapdoor; + mappings[771] = ItemType.PaleOakTrapdoor; + mappings[772] = ItemType.MangroveTrapdoor; + mappings[773] = ItemType.BambooTrapdoor; + mappings[774] = ItemType.CrimsonTrapdoor; + mappings[775] = ItemType.WarpedTrapdoor; + mappings[776] = ItemType.CopperTrapdoor; + mappings[777] = ItemType.ExposedCopperTrapdoor; + mappings[778] = ItemType.WeatheredCopperTrapdoor; + mappings[779] = ItemType.OxidizedCopperTrapdoor; + mappings[780] = ItemType.WaxedCopperTrapdoor; + mappings[781] = ItemType.WaxedExposedCopperTrapdoor; + mappings[782] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[783] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[784] = ItemType.OakFenceGate; + mappings[785] = ItemType.SpruceFenceGate; + mappings[786] = ItemType.BirchFenceGate; + mappings[787] = ItemType.JungleFenceGate; + mappings[788] = ItemType.AcaciaFenceGate; + mappings[789] = ItemType.CherryFenceGate; + mappings[790] = ItemType.DarkOakFenceGate; + mappings[791] = ItemType.PaleOakFenceGate; + mappings[792] = ItemType.MangroveFenceGate; + mappings[793] = ItemType.BambooFenceGate; + mappings[794] = ItemType.CrimsonFenceGate; + mappings[795] = ItemType.WarpedFenceGate; + mappings[796] = ItemType.PoweredRail; + mappings[797] = ItemType.DetectorRail; + mappings[798] = ItemType.Rail; + mappings[799] = ItemType.ActivatorRail; + mappings[800] = ItemType.Saddle; + mappings[801] = ItemType.Minecart; + mappings[802] = ItemType.ChestMinecart; + mappings[803] = ItemType.FurnaceMinecart; + mappings[804] = ItemType.TntMinecart; + mappings[805] = ItemType.HopperMinecart; + mappings[806] = ItemType.CarrotOnAStick; + mappings[807] = ItemType.WarpedFungusOnAStick; + mappings[808] = ItemType.PhantomMembrane; + mappings[809] = ItemType.Elytra; + mappings[810] = ItemType.OakBoat; + mappings[811] = ItemType.OakChestBoat; + mappings[812] = ItemType.SpruceBoat; + mappings[813] = ItemType.SpruceChestBoat; + mappings[814] = ItemType.BirchBoat; + mappings[815] = ItemType.BirchChestBoat; + mappings[816] = ItemType.JungleBoat; + mappings[817] = ItemType.JungleChestBoat; + mappings[818] = ItemType.AcaciaBoat; + mappings[819] = ItemType.AcaciaChestBoat; + mappings[820] = ItemType.CherryBoat; + mappings[821] = ItemType.CherryChestBoat; + mappings[822] = ItemType.DarkOakBoat; + mappings[823] = ItemType.DarkOakChestBoat; + mappings[824] = ItemType.PaleOakBoat; + mappings[825] = ItemType.PaleOakChestBoat; + mappings[826] = ItemType.MangroveBoat; + mappings[827] = ItemType.MangroveChestBoat; + mappings[828] = ItemType.BambooRaft; + mappings[829] = ItemType.BambooChestRaft; + mappings[830] = ItemType.StructureBlock; + mappings[831] = ItemType.Jigsaw; + mappings[832] = ItemType.TestBlock; + mappings[833] = ItemType.TestInstanceBlock; + mappings[834] = ItemType.TurtleHelmet; + mappings[835] = ItemType.TurtleScute; + mappings[836] = ItemType.ArmadilloScute; + mappings[837] = ItemType.WolfArmor; + mappings[838] = ItemType.FlintAndSteel; + mappings[839] = ItemType.Bowl; + mappings[840] = ItemType.Apple; + mappings[841] = ItemType.Bow; + mappings[842] = ItemType.Arrow; + mappings[843] = ItemType.Coal; + mappings[844] = ItemType.Charcoal; + mappings[845] = ItemType.Diamond; + mappings[846] = ItemType.Emerald; + mappings[847] = ItemType.LapisLazuli; + mappings[848] = ItemType.Quartz; + mappings[849] = ItemType.AmethystShard; + mappings[850] = ItemType.RawIron; + mappings[851] = ItemType.IronIngot; + mappings[852] = ItemType.RawCopper; + mappings[853] = ItemType.CopperIngot; + mappings[854] = ItemType.RawGold; + mappings[855] = ItemType.GoldIngot; + mappings[856] = ItemType.NetheriteIngot; + mappings[857] = ItemType.NetheriteScrap; + mappings[858] = ItemType.WoodenSword; + mappings[859] = ItemType.WoodenShovel; + mappings[860] = ItemType.WoodenPickaxe; + mappings[861] = ItemType.WoodenAxe; + mappings[862] = ItemType.WoodenHoe; + mappings[863] = ItemType.StoneSword; + mappings[864] = ItemType.StoneShovel; + mappings[865] = ItemType.StonePickaxe; + mappings[866] = ItemType.StoneAxe; + mappings[867] = ItemType.StoneHoe; + mappings[868] = ItemType.GoldenSword; + mappings[869] = ItemType.GoldenShovel; + mappings[870] = ItemType.GoldenPickaxe; + mappings[871] = ItemType.GoldenAxe; + mappings[872] = ItemType.GoldenHoe; + mappings[873] = ItemType.IronSword; + mappings[874] = ItemType.IronShovel; + mappings[875] = ItemType.IronPickaxe; + mappings[876] = ItemType.IronAxe; + mappings[877] = ItemType.IronHoe; + mappings[878] = ItemType.DiamondSword; + mappings[879] = ItemType.DiamondShovel; + mappings[880] = ItemType.DiamondPickaxe; + mappings[881] = ItemType.DiamondAxe; + mappings[882] = ItemType.DiamondHoe; + mappings[883] = ItemType.NetheriteSword; + mappings[884] = ItemType.NetheriteShovel; + mappings[885] = ItemType.NetheritePickaxe; + mappings[886] = ItemType.NetheriteAxe; + mappings[887] = ItemType.NetheriteHoe; + mappings[888] = ItemType.Stick; + mappings[889] = ItemType.MushroomStew; + mappings[890] = ItemType.String; + mappings[891] = ItemType.Feather; + mappings[892] = ItemType.Gunpowder; + mappings[893] = ItemType.WheatSeeds; + mappings[894] = ItemType.Wheat; + mappings[895] = ItemType.Bread; + mappings[896] = ItemType.LeatherHelmet; + mappings[897] = ItemType.LeatherChestplate; + mappings[898] = ItemType.LeatherLeggings; + mappings[899] = ItemType.LeatherBoots; + mappings[900] = ItemType.ChainmailHelmet; + mappings[901] = ItemType.ChainmailChestplate; + mappings[902] = ItemType.ChainmailLeggings; + mappings[903] = ItemType.ChainmailBoots; + mappings[904] = ItemType.IronHelmet; + mappings[905] = ItemType.IronChestplate; + mappings[906] = ItemType.IronLeggings; + mappings[907] = ItemType.IronBoots; + mappings[908] = ItemType.DiamondHelmet; + mappings[909] = ItemType.DiamondChestplate; + mappings[910] = ItemType.DiamondLeggings; + mappings[911] = ItemType.DiamondBoots; + mappings[912] = ItemType.GoldenHelmet; + mappings[913] = ItemType.GoldenChestplate; + mappings[914] = ItemType.GoldenLeggings; + mappings[915] = ItemType.GoldenBoots; + mappings[916] = ItemType.NetheriteHelmet; + mappings[917] = ItemType.NetheriteChestplate; + mappings[918] = ItemType.NetheriteLeggings; + mappings[919] = ItemType.NetheriteBoots; + mappings[920] = ItemType.Flint; + mappings[921] = ItemType.Porkchop; + mappings[922] = ItemType.CookedPorkchop; + mappings[923] = ItemType.Painting; + mappings[924] = ItemType.GoldenApple; + mappings[925] = ItemType.EnchantedGoldenApple; + mappings[926] = ItemType.OakSign; + mappings[927] = ItemType.SpruceSign; + mappings[928] = ItemType.BirchSign; + mappings[929] = ItemType.JungleSign; + mappings[930] = ItemType.AcaciaSign; + mappings[931] = ItemType.CherrySign; + mappings[932] = ItemType.DarkOakSign; + mappings[933] = ItemType.PaleOakSign; + mappings[934] = ItemType.MangroveSign; + mappings[935] = ItemType.BambooSign; + mappings[936] = ItemType.CrimsonSign; + mappings[937] = ItemType.WarpedSign; + mappings[938] = ItemType.OakHangingSign; + mappings[939] = ItemType.SpruceHangingSign; + mappings[940] = ItemType.BirchHangingSign; + mappings[941] = ItemType.JungleHangingSign; + mappings[942] = ItemType.AcaciaHangingSign; + mappings[943] = ItemType.CherryHangingSign; + mappings[944] = ItemType.DarkOakHangingSign; + mappings[945] = ItemType.PaleOakHangingSign; + mappings[946] = ItemType.MangroveHangingSign; + mappings[947] = ItemType.BambooHangingSign; + mappings[948] = ItemType.CrimsonHangingSign; + mappings[949] = ItemType.WarpedHangingSign; + mappings[950] = ItemType.Bucket; + mappings[951] = ItemType.WaterBucket; + mappings[952] = ItemType.LavaBucket; + mappings[953] = ItemType.PowderSnowBucket; + mappings[954] = ItemType.Snowball; + mappings[955] = ItemType.Leather; + mappings[956] = ItemType.MilkBucket; + mappings[957] = ItemType.PufferfishBucket; + mappings[958] = ItemType.SalmonBucket; + mappings[959] = ItemType.CodBucket; + mappings[960] = ItemType.TropicalFishBucket; + mappings[961] = ItemType.AxolotlBucket; + mappings[962] = ItemType.TadpoleBucket; + mappings[963] = ItemType.Brick; + mappings[964] = ItemType.ClayBall; + mappings[965] = ItemType.DriedKelpBlock; + mappings[966] = ItemType.Paper; + mappings[967] = ItemType.Book; + mappings[968] = ItemType.SlimeBall; + mappings[969] = ItemType.Egg; + mappings[970] = ItemType.BlueEgg; + mappings[971] = ItemType.BrownEgg; + mappings[972] = ItemType.Compass; + mappings[973] = ItemType.RecoveryCompass; + mappings[974] = ItemType.Bundle; + mappings[975] = ItemType.WhiteBundle; + mappings[976] = ItemType.OrangeBundle; + mappings[977] = ItemType.MagentaBundle; + mappings[978] = ItemType.LightBlueBundle; + mappings[979] = ItemType.YellowBundle; + mappings[980] = ItemType.LimeBundle; + mappings[981] = ItemType.PinkBundle; + mappings[982] = ItemType.GrayBundle; + mappings[983] = ItemType.LightGrayBundle; + mappings[984] = ItemType.CyanBundle; + mappings[985] = ItemType.PurpleBundle; + mappings[986] = ItemType.BlueBundle; + mappings[987] = ItemType.BrownBundle; + mappings[988] = ItemType.GreenBundle; + mappings[989] = ItemType.RedBundle; + mappings[990] = ItemType.BlackBundle; + mappings[991] = ItemType.FishingRod; + mappings[992] = ItemType.Clock; + mappings[993] = ItemType.Spyglass; + mappings[994] = ItemType.GlowstoneDust; + mappings[995] = ItemType.Cod; + mappings[996] = ItemType.Salmon; + mappings[997] = ItemType.TropicalFish; + mappings[998] = ItemType.Pufferfish; + mappings[999] = ItemType.CookedCod; + mappings[1000] = ItemType.CookedSalmon; + mappings[1001] = ItemType.InkSac; + mappings[1002] = ItemType.GlowInkSac; + mappings[1003] = ItemType.CocoaBeans; + mappings[1004] = ItemType.WhiteDye; + mappings[1005] = ItemType.OrangeDye; + mappings[1006] = ItemType.MagentaDye; + mappings[1007] = ItemType.LightBlueDye; + mappings[1008] = ItemType.YellowDye; + mappings[1009] = ItemType.LimeDye; + mappings[1010] = ItemType.PinkDye; + mappings[1011] = ItemType.GrayDye; + mappings[1012] = ItemType.LightGrayDye; + mappings[1013] = ItemType.CyanDye; + mappings[1014] = ItemType.PurpleDye; + mappings[1015] = ItemType.BlueDye; + mappings[1016] = ItemType.BrownDye; + mappings[1017] = ItemType.GreenDye; + mappings[1018] = ItemType.RedDye; + mappings[1019] = ItemType.BlackDye; + mappings[1020] = ItemType.BoneMeal; + mappings[1021] = ItemType.Bone; + mappings[1022] = ItemType.Sugar; + mappings[1023] = ItemType.Cake; + mappings[1024] = ItemType.WhiteBed; + mappings[1025] = ItemType.OrangeBed; + mappings[1026] = ItemType.MagentaBed; + mappings[1027] = ItemType.LightBlueBed; + mappings[1028] = ItemType.YellowBed; + mappings[1029] = ItemType.LimeBed; + mappings[1030] = ItemType.PinkBed; + mappings[1031] = ItemType.GrayBed; + mappings[1032] = ItemType.LightGrayBed; + mappings[1033] = ItemType.CyanBed; + mappings[1034] = ItemType.PurpleBed; + mappings[1035] = ItemType.BlueBed; + mappings[1036] = ItemType.BrownBed; + mappings[1037] = ItemType.GreenBed; + mappings[1038] = ItemType.RedBed; + mappings[1039] = ItemType.BlackBed; + mappings[1040] = ItemType.Cookie; + mappings[1041] = ItemType.Crafter; + mappings[1042] = ItemType.FilledMap; + mappings[1043] = ItemType.Shears; + mappings[1044] = ItemType.MelonSlice; + mappings[1045] = ItemType.DriedKelp; + mappings[1046] = ItemType.PumpkinSeeds; + mappings[1047] = ItemType.MelonSeeds; + mappings[1048] = ItemType.Beef; + mappings[1049] = ItemType.CookedBeef; + mappings[1050] = ItemType.Chicken; + mappings[1051] = ItemType.CookedChicken; + mappings[1052] = ItemType.RottenFlesh; + mappings[1053] = ItemType.EnderPearl; + mappings[1054] = ItemType.BlazeRod; + mappings[1055] = ItemType.GhastTear; + mappings[1056] = ItemType.GoldNugget; + mappings[1057] = ItemType.NetherWart; + mappings[1058] = ItemType.GlassBottle; + mappings[1059] = ItemType.Potion; + mappings[1060] = ItemType.SpiderEye; + mappings[1061] = ItemType.FermentedSpiderEye; + mappings[1062] = ItemType.BlazePowder; + mappings[1063] = ItemType.MagmaCream; + mappings[1064] = ItemType.BrewingStand; + mappings[1065] = ItemType.Cauldron; + mappings[1066] = ItemType.EnderEye; + mappings[1067] = ItemType.GlisteringMelonSlice; + mappings[1068] = ItemType.ArmadilloSpawnEgg; + mappings[1069] = ItemType.AllaySpawnEgg; + mappings[1070] = ItemType.AxolotlSpawnEgg; + mappings[1071] = ItemType.BatSpawnEgg; + mappings[1072] = ItemType.BeeSpawnEgg; + mappings[1073] = ItemType.BlazeSpawnEgg; + mappings[1074] = ItemType.BoggedSpawnEgg; + mappings[1075] = ItemType.BreezeSpawnEgg; + mappings[1076] = ItemType.CatSpawnEgg; + mappings[1077] = ItemType.CamelSpawnEgg; + mappings[1078] = ItemType.CaveSpiderSpawnEgg; + mappings[1079] = ItemType.ChickenSpawnEgg; + mappings[1080] = ItemType.CodSpawnEgg; + mappings[1081] = ItemType.CowSpawnEgg; + mappings[1082] = ItemType.CreeperSpawnEgg; + mappings[1083] = ItemType.DolphinSpawnEgg; + mappings[1084] = ItemType.DonkeySpawnEgg; + mappings[1085] = ItemType.DrownedSpawnEgg; + mappings[1086] = ItemType.ElderGuardianSpawnEgg; + mappings[1087] = ItemType.EnderDragonSpawnEgg; + mappings[1088] = ItemType.EndermanSpawnEgg; + mappings[1089] = ItemType.EndermiteSpawnEgg; + mappings[1090] = ItemType.EvokerSpawnEgg; + mappings[1091] = ItemType.FoxSpawnEgg; + mappings[1092] = ItemType.FrogSpawnEgg; + mappings[1093] = ItemType.GhastSpawnEgg; + mappings[1094] = ItemType.GlowSquidSpawnEgg; + mappings[1095] = ItemType.GoatSpawnEgg; + mappings[1096] = ItemType.GuardianSpawnEgg; + mappings[1097] = ItemType.HoglinSpawnEgg; + mappings[1098] = ItemType.HorseSpawnEgg; + mappings[1099] = ItemType.HuskSpawnEgg; + mappings[1100] = ItemType.IronGolemSpawnEgg; + mappings[1101] = ItemType.LlamaSpawnEgg; + mappings[1102] = ItemType.MagmaCubeSpawnEgg; + mappings[1103] = ItemType.MooshroomSpawnEgg; + mappings[1104] = ItemType.MuleSpawnEgg; + mappings[1105] = ItemType.OcelotSpawnEgg; + mappings[1106] = ItemType.PandaSpawnEgg; + mappings[1107] = ItemType.ParrotSpawnEgg; + mappings[1108] = ItemType.PhantomSpawnEgg; + mappings[1109] = ItemType.PigSpawnEgg; + mappings[1110] = ItemType.PiglinSpawnEgg; + mappings[1111] = ItemType.PiglinBruteSpawnEgg; + mappings[1112] = ItemType.PillagerSpawnEgg; + mappings[1113] = ItemType.PolarBearSpawnEgg; + mappings[1114] = ItemType.PufferfishSpawnEgg; + mappings[1115] = ItemType.RabbitSpawnEgg; + mappings[1116] = ItemType.RavagerSpawnEgg; + mappings[1117] = ItemType.SalmonSpawnEgg; + mappings[1118] = ItemType.SheepSpawnEgg; + mappings[1119] = ItemType.ShulkerSpawnEgg; + mappings[1120] = ItemType.SilverfishSpawnEgg; + mappings[1121] = ItemType.SkeletonSpawnEgg; + mappings[1122] = ItemType.SkeletonHorseSpawnEgg; + mappings[1123] = ItemType.SlimeSpawnEgg; + mappings[1124] = ItemType.SnifferSpawnEgg; + mappings[1125] = ItemType.SnowGolemSpawnEgg; + mappings[1126] = ItemType.SpiderSpawnEgg; + mappings[1127] = ItemType.SquidSpawnEgg; + mappings[1128] = ItemType.StraySpawnEgg; + mappings[1129] = ItemType.StriderSpawnEgg; + mappings[1130] = ItemType.TadpoleSpawnEgg; + mappings[1131] = ItemType.TraderLlamaSpawnEgg; + mappings[1132] = ItemType.TropicalFishSpawnEgg; + mappings[1133] = ItemType.TurtleSpawnEgg; + mappings[1134] = ItemType.VexSpawnEgg; + mappings[1135] = ItemType.VillagerSpawnEgg; + mappings[1136] = ItemType.VindicatorSpawnEgg; + mappings[1137] = ItemType.WanderingTraderSpawnEgg; + mappings[1138] = ItemType.WardenSpawnEgg; + mappings[1139] = ItemType.WitchSpawnEgg; + mappings[1140] = ItemType.WitherSpawnEgg; + mappings[1141] = ItemType.WitherSkeletonSpawnEgg; + mappings[1142] = ItemType.WolfSpawnEgg; + mappings[1143] = ItemType.ZoglinSpawnEgg; + mappings[1144] = ItemType.CreakingSpawnEgg; + mappings[1145] = ItemType.ZombieSpawnEgg; + mappings[1146] = ItemType.ZombieHorseSpawnEgg; + mappings[1147] = ItemType.ZombieVillagerSpawnEgg; + mappings[1148] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1149] = ItemType.ExperienceBottle; + mappings[1150] = ItemType.FireCharge; + mappings[1151] = ItemType.WindCharge; + mappings[1152] = ItemType.WritableBook; + mappings[1153] = ItemType.WrittenBook; + mappings[1154] = ItemType.BreezeRod; + mappings[1155] = ItemType.Mace; + mappings[1156] = ItemType.ItemFrame; + mappings[1157] = ItemType.GlowItemFrame; + mappings[1158] = ItemType.FlowerPot; + mappings[1159] = ItemType.Carrot; + mappings[1160] = ItemType.Potato; + mappings[1161] = ItemType.BakedPotato; + mappings[1162] = ItemType.PoisonousPotato; + mappings[1163] = ItemType.Map; + mappings[1164] = ItemType.GoldenCarrot; + mappings[1165] = ItemType.SkeletonSkull; + mappings[1166] = ItemType.WitherSkeletonSkull; + mappings[1167] = ItemType.PlayerHead; + mappings[1168] = ItemType.ZombieHead; + mappings[1169] = ItemType.CreeperHead; + mappings[1170] = ItemType.DragonHead; + mappings[1171] = ItemType.PiglinHead; + mappings[1172] = ItemType.NetherStar; + mappings[1173] = ItemType.PumpkinPie; + mappings[1174] = ItemType.FireworkRocket; + mappings[1175] = ItemType.FireworkStar; + mappings[1176] = ItemType.EnchantedBook; + mappings[1177] = ItemType.NetherBrick; + mappings[1178] = ItemType.ResinBrick; + mappings[1179] = ItemType.PrismarineShard; + mappings[1180] = ItemType.PrismarineCrystals; + mappings[1181] = ItemType.Rabbit; + mappings[1182] = ItemType.CookedRabbit; + mappings[1183] = ItemType.RabbitStew; + mappings[1184] = ItemType.RabbitFoot; + mappings[1185] = ItemType.RabbitHide; + mappings[1186] = ItemType.ArmorStand; + mappings[1187] = ItemType.IronHorseArmor; + mappings[1188] = ItemType.GoldenHorseArmor; + mappings[1189] = ItemType.DiamondHorseArmor; + mappings[1190] = ItemType.LeatherHorseArmor; + mappings[1191] = ItemType.Lead; + mappings[1192] = ItemType.NameTag; + mappings[1193] = ItemType.CommandBlockMinecart; + mappings[1194] = ItemType.Mutton; + mappings[1195] = ItemType.CookedMutton; + mappings[1196] = ItemType.WhiteBanner; + mappings[1197] = ItemType.OrangeBanner; + mappings[1198] = ItemType.MagentaBanner; + mappings[1199] = ItemType.LightBlueBanner; + mappings[1200] = ItemType.YellowBanner; + mappings[1201] = ItemType.LimeBanner; + mappings[1202] = ItemType.PinkBanner; + mappings[1203] = ItemType.GrayBanner; + mappings[1204] = ItemType.LightGrayBanner; + mappings[1205] = ItemType.CyanBanner; + mappings[1206] = ItemType.PurpleBanner; + mappings[1207] = ItemType.BlueBanner; + mappings[1208] = ItemType.BrownBanner; + mappings[1209] = ItemType.GreenBanner; + mappings[1210] = ItemType.RedBanner; + mappings[1211] = ItemType.BlackBanner; + mappings[1212] = ItemType.EndCrystal; + mappings[1213] = ItemType.ChorusFruit; + mappings[1214] = ItemType.PoppedChorusFruit; + mappings[1215] = ItemType.TorchflowerSeeds; + mappings[1216] = ItemType.PitcherPod; + mappings[1217] = ItemType.Beetroot; + mappings[1218] = ItemType.BeetrootSeeds; + mappings[1219] = ItemType.BeetrootSoup; + mappings[1220] = ItemType.DragonBreath; + mappings[1221] = ItemType.SplashPotion; + mappings[1222] = ItemType.SpectralArrow; + mappings[1223] = ItemType.TippedArrow; + mappings[1224] = ItemType.LingeringPotion; + mappings[1225] = ItemType.Shield; + mappings[1226] = ItemType.TotemOfUndying; + mappings[1227] = ItemType.ShulkerShell; + mappings[1228] = ItemType.IronNugget; + mappings[1229] = ItemType.KnowledgeBook; + mappings[1230] = ItemType.DebugStick; + mappings[1231] = ItemType.MusicDisc13; + mappings[1232] = ItemType.MusicDiscCat; + mappings[1233] = ItemType.MusicDiscBlocks; + mappings[1234] = ItemType.MusicDiscChirp; + mappings[1235] = ItemType.MusicDiscCreator; + mappings[1236] = ItemType.MusicDiscCreatorMusicBox; + mappings[1237] = ItemType.MusicDiscFar; + mappings[1238] = ItemType.MusicDiscMall; + mappings[1239] = ItemType.MusicDiscMellohi; + mappings[1240] = ItemType.MusicDiscStal; + mappings[1241] = ItemType.MusicDiscStrad; + mappings[1242] = ItemType.MusicDiscWard; + mappings[1243] = ItemType.MusicDisc11; + mappings[1244] = ItemType.MusicDiscWait; + mappings[1245] = ItemType.MusicDiscOtherside; + mappings[1246] = ItemType.MusicDiscRelic; + mappings[1247] = ItemType.MusicDisc5; + mappings[1248] = ItemType.MusicDiscPigstep; + mappings[1249] = ItemType.MusicDiscPrecipice; + mappings[1250] = ItemType.DiscFragment5; + mappings[1251] = ItemType.Trident; + mappings[1252] = ItemType.NautilusShell; + mappings[1253] = ItemType.HeartOfTheSea; + mappings[1254] = ItemType.Crossbow; + mappings[1255] = ItemType.SuspiciousStew; + mappings[1256] = ItemType.Loom; + mappings[1257] = ItemType.FlowerBannerPattern; + mappings[1258] = ItemType.CreeperBannerPattern; + mappings[1259] = ItemType.SkullBannerPattern; + mappings[1260] = ItemType.MojangBannerPattern; + mappings[1261] = ItemType.GlobeBannerPattern; + mappings[1262] = ItemType.PiglinBannerPattern; + mappings[1263] = ItemType.FlowBannerPattern; + mappings[1264] = ItemType.GusterBannerPattern; + mappings[1265] = ItemType.FieldMasonedBannerPattern; + mappings[1266] = ItemType.BordureIndentedBannerPattern; + mappings[1267] = ItemType.GoatHorn; + mappings[1268] = ItemType.Composter; + mappings[1269] = ItemType.Barrel; + mappings[1270] = ItemType.Smoker; + mappings[1271] = ItemType.BlastFurnace; + mappings[1272] = ItemType.CartographyTable; + mappings[1273] = ItemType.FletchingTable; + mappings[1274] = ItemType.Grindstone; + mappings[1275] = ItemType.SmithingTable; + mappings[1276] = ItemType.Stonecutter; + mappings[1277] = ItemType.Bell; + mappings[1278] = ItemType.Lantern; + mappings[1279] = ItemType.SoulLantern; + mappings[1280] = ItemType.SweetBerries; + mappings[1281] = ItemType.GlowBerries; + mappings[1282] = ItemType.Campfire; + mappings[1283] = ItemType.SoulCampfire; + mappings[1284] = ItemType.Shroomlight; + mappings[1285] = ItemType.Honeycomb; + mappings[1286] = ItemType.BeeNest; + mappings[1287] = ItemType.Beehive; + mappings[1288] = ItemType.HoneyBottle; + mappings[1289] = ItemType.HoneycombBlock; + mappings[1290] = ItemType.Lodestone; + mappings[1291] = ItemType.CryingObsidian; + mappings[1292] = ItemType.Blackstone; + mappings[1293] = ItemType.BlackstoneSlab; + mappings[1294] = ItemType.BlackstoneStairs; + mappings[1295] = ItemType.GildedBlackstone; + mappings[1296] = ItemType.PolishedBlackstone; + mappings[1297] = ItemType.PolishedBlackstoneSlab; + mappings[1298] = ItemType.PolishedBlackstoneStairs; + mappings[1299] = ItemType.ChiseledPolishedBlackstone; + mappings[1300] = ItemType.PolishedBlackstoneBricks; + mappings[1301] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1302] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1303] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1304] = ItemType.RespawnAnchor; + mappings[1305] = ItemType.Candle; + mappings[1306] = ItemType.WhiteCandle; + mappings[1307] = ItemType.OrangeCandle; + mappings[1308] = ItemType.MagentaCandle; + mappings[1309] = ItemType.LightBlueCandle; + mappings[1310] = ItemType.YellowCandle; + mappings[1311] = ItemType.LimeCandle; + mappings[1312] = ItemType.PinkCandle; + mappings[1313] = ItemType.GrayCandle; + mappings[1314] = ItemType.LightGrayCandle; + mappings[1315] = ItemType.CyanCandle; + mappings[1316] = ItemType.PurpleCandle; + mappings[1317] = ItemType.BlueCandle; + mappings[1318] = ItemType.BrownCandle; + mappings[1319] = ItemType.GreenCandle; + mappings[1320] = ItemType.RedCandle; + mappings[1321] = ItemType.BlackCandle; + mappings[1322] = ItemType.SmallAmethystBud; + mappings[1323] = ItemType.MediumAmethystBud; + mappings[1324] = ItemType.LargeAmethystBud; + mappings[1325] = ItemType.AmethystCluster; + mappings[1326] = ItemType.PointedDripstone; + mappings[1327] = ItemType.OchreFroglight; + mappings[1328] = ItemType.VerdantFroglight; + mappings[1329] = ItemType.PearlescentFroglight; + mappings[1330] = ItemType.Frogspawn; + mappings[1331] = ItemType.EchoShard; + mappings[1332] = ItemType.Brush; + mappings[1333] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1334] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1335] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1336] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1337] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1338] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1339] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1340] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1341] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1342] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1343] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1344] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1345] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1346] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1347] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1348] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1349] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1350] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1351] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1352] = ItemType.AnglerPotterySherd; + mappings[1353] = ItemType.ArcherPotterySherd; + mappings[1354] = ItemType.ArmsUpPotterySherd; + mappings[1355] = ItemType.BladePotterySherd; + mappings[1356] = ItemType.BrewerPotterySherd; + mappings[1357] = ItemType.BurnPotterySherd; + mappings[1358] = ItemType.DangerPotterySherd; + mappings[1359] = ItemType.ExplorerPotterySherd; + mappings[1360] = ItemType.FlowPotterySherd; + mappings[1361] = ItemType.FriendPotterySherd; + mappings[1362] = ItemType.GusterPotterySherd; + mappings[1363] = ItemType.HeartPotterySherd; + mappings[1364] = ItemType.HeartbreakPotterySherd; + mappings[1365] = ItemType.HowlPotterySherd; + mappings[1366] = ItemType.MinerPotterySherd; + mappings[1367] = ItemType.MournerPotterySherd; + mappings[1368] = ItemType.PlentyPotterySherd; + mappings[1369] = ItemType.PrizePotterySherd; + mappings[1370] = ItemType.ScrapePotterySherd; + mappings[1371] = ItemType.SheafPotterySherd; + mappings[1372] = ItemType.ShelterPotterySherd; + mappings[1373] = ItemType.SkullPotterySherd; + mappings[1374] = ItemType.SnortPotterySherd; + mappings[1375] = ItemType.CopperGrate; + mappings[1376] = ItemType.ExposedCopperGrate; + mappings[1377] = ItemType.WeatheredCopperGrate; + mappings[1378] = ItemType.OxidizedCopperGrate; + mappings[1379] = ItemType.WaxedCopperGrate; + mappings[1380] = ItemType.WaxedExposedCopperGrate; + mappings[1381] = ItemType.WaxedWeatheredCopperGrate; + mappings[1382] = ItemType.WaxedOxidizedCopperGrate; + mappings[1383] = ItemType.CopperBulb; + mappings[1384] = ItemType.ExposedCopperBulb; + mappings[1385] = ItemType.WeatheredCopperBulb; + mappings[1386] = ItemType.OxidizedCopperBulb; + mappings[1387] = ItemType.WaxedCopperBulb; + mappings[1388] = ItemType.WaxedExposedCopperBulb; + mappings[1389] = ItemType.WaxedWeatheredCopperBulb; + mappings[1390] = ItemType.WaxedOxidizedCopperBulb; + mappings[1391] = ItemType.TrialSpawner; + mappings[1392] = ItemType.TrialKey; + mappings[1393] = ItemType.OminousTrialKey; + mappings[1394] = ItemType.Vault; + mappings[1395] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1216.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1216.cs new file mode 100644 index 00000000..91472a53 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1216.cs @@ -0,0 +1,1433 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1216 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1216() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.DryShortGrass; + mappings[210] = ItemType.DryTallGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.OpenEyeblossom; + mappings[231] = ItemType.ClosedEyeblossom; + mappings[232] = ItemType.Poppy; + mappings[233] = ItemType.BlueOrchid; + mappings[234] = ItemType.Allium; + mappings[235] = ItemType.AzureBluet; + mappings[236] = ItemType.RedTulip; + mappings[237] = ItemType.OrangeTulip; + mappings[238] = ItemType.WhiteTulip; + mappings[239] = ItemType.PinkTulip; + mappings[240] = ItemType.OxeyeDaisy; + mappings[241] = ItemType.Cornflower; + mappings[242] = ItemType.LilyOfTheValley; + mappings[243] = ItemType.WitherRose; + mappings[244] = ItemType.Torchflower; + mappings[245] = ItemType.PitcherPlant; + mappings[246] = ItemType.SporeBlossom; + mappings[247] = ItemType.BrownMushroom; + mappings[248] = ItemType.RedMushroom; + mappings[249] = ItemType.CrimsonFungus; + mappings[250] = ItemType.WarpedFungus; + mappings[251] = ItemType.CrimsonRoots; + mappings[252] = ItemType.WarpedRoots; + mappings[253] = ItemType.NetherSprouts; + mappings[254] = ItemType.WeepingVines; + mappings[255] = ItemType.TwistingVines; + mappings[256] = ItemType.SugarCane; + mappings[257] = ItemType.Kelp; + mappings[258] = ItemType.PinkPetals; + mappings[259] = ItemType.Wildflowers; + mappings[260] = ItemType.LeafLitter; + mappings[261] = ItemType.MossCarpet; + mappings[262] = ItemType.MossBlock; + mappings[263] = ItemType.PaleMossCarpet; + mappings[264] = ItemType.PaleHangingMoss; + mappings[265] = ItemType.PaleMossBlock; + mappings[266] = ItemType.HangingRoots; + mappings[267] = ItemType.BigDripleaf; + mappings[268] = ItemType.SmallDripleaf; + mappings[269] = ItemType.Bamboo; + mappings[270] = ItemType.OakSlab; + mappings[271] = ItemType.SpruceSlab; + mappings[272] = ItemType.BirchSlab; + mappings[273] = ItemType.JungleSlab; + mappings[274] = ItemType.AcaciaSlab; + mappings[275] = ItemType.CherrySlab; + mappings[276] = ItemType.DarkOakSlab; + mappings[277] = ItemType.PaleOakSlab; + mappings[278] = ItemType.MangroveSlab; + mappings[279] = ItemType.BambooSlab; + mappings[280] = ItemType.BambooMosaicSlab; + mappings[281] = ItemType.CrimsonSlab; + mappings[282] = ItemType.WarpedSlab; + mappings[283] = ItemType.StoneSlab; + mappings[284] = ItemType.SmoothStoneSlab; + mappings[285] = ItemType.SandstoneSlab; + mappings[286] = ItemType.CutSandstoneSlab; + mappings[287] = ItemType.PetrifiedOakSlab; + mappings[288] = ItemType.CobblestoneSlab; + mappings[289] = ItemType.BrickSlab; + mappings[290] = ItemType.StoneBrickSlab; + mappings[291] = ItemType.MudBrickSlab; + mappings[292] = ItemType.NetherBrickSlab; + mappings[293] = ItemType.QuartzSlab; + mappings[294] = ItemType.RedSandstoneSlab; + mappings[295] = ItemType.CutRedSandstoneSlab; + mappings[296] = ItemType.PurpurSlab; + mappings[297] = ItemType.PrismarineSlab; + mappings[298] = ItemType.PrismarineBrickSlab; + mappings[299] = ItemType.DarkPrismarineSlab; + mappings[300] = ItemType.SmoothQuartz; + mappings[301] = ItemType.SmoothRedSandstone; + mappings[302] = ItemType.SmoothSandstone; + mappings[303] = ItemType.SmoothStone; + mappings[304] = ItemType.Bricks; + mappings[305] = ItemType.Bookshelf; + mappings[306] = ItemType.ChiseledBookshelf; + mappings[307] = ItemType.DecoratedPot; + mappings[308] = ItemType.MossyCobblestone; + mappings[309] = ItemType.Obsidian; + mappings[310] = ItemType.Torch; + mappings[311] = ItemType.EndRod; + mappings[312] = ItemType.ChorusPlant; + mappings[313] = ItemType.ChorusFlower; + mappings[314] = ItemType.PurpurBlock; + mappings[315] = ItemType.PurpurPillar; + mappings[316] = ItemType.PurpurStairs; + mappings[317] = ItemType.Spawner; + mappings[318] = ItemType.CreakingHeart; + mappings[319] = ItemType.Chest; + mappings[320] = ItemType.CraftingTable; + mappings[321] = ItemType.Farmland; + mappings[322] = ItemType.Furnace; + mappings[323] = ItemType.Ladder; + mappings[324] = ItemType.CobblestoneStairs; + mappings[325] = ItemType.Snow; + mappings[326] = ItemType.Ice; + mappings[327] = ItemType.SnowBlock; + mappings[328] = ItemType.Cactus; + mappings[329] = ItemType.CactusFlower; + mappings[330] = ItemType.Clay; + mappings[331] = ItemType.Jukebox; + mappings[332] = ItemType.OakFence; + mappings[333] = ItemType.SpruceFence; + mappings[334] = ItemType.BirchFence; + mappings[335] = ItemType.JungleFence; + mappings[336] = ItemType.AcaciaFence; + mappings[337] = ItemType.CherryFence; + mappings[338] = ItemType.DarkOakFence; + mappings[339] = ItemType.PaleOakFence; + mappings[340] = ItemType.MangroveFence; + mappings[341] = ItemType.BambooFence; + mappings[342] = ItemType.CrimsonFence; + mappings[343] = ItemType.WarpedFence; + mappings[344] = ItemType.Pumpkin; + mappings[345] = ItemType.CarvedPumpkin; + mappings[346] = ItemType.JackOLantern; + mappings[347] = ItemType.Netherrack; + mappings[348] = ItemType.SoulSand; + mappings[349] = ItemType.SoulSoil; + mappings[350] = ItemType.Basalt; + mappings[351] = ItemType.PolishedBasalt; + mappings[352] = ItemType.SmoothBasalt; + mappings[353] = ItemType.SoulTorch; + mappings[354] = ItemType.Glowstone; + mappings[355] = ItemType.InfestedStone; + mappings[356] = ItemType.InfestedCobblestone; + mappings[357] = ItemType.InfestedStoneBricks; + mappings[358] = ItemType.InfestedMossyStoneBricks; + mappings[359] = ItemType.InfestedCrackedStoneBricks; + mappings[360] = ItemType.InfestedChiseledStoneBricks; + mappings[361] = ItemType.InfestedDeepslate; + mappings[362] = ItemType.StoneBricks; + mappings[363] = ItemType.MossyStoneBricks; + mappings[364] = ItemType.CrackedStoneBricks; + mappings[365] = ItemType.ChiseledStoneBricks; + mappings[366] = ItemType.PackedMud; + mappings[367] = ItemType.MudBricks; + mappings[368] = ItemType.DeepslateBricks; + mappings[369] = ItemType.CrackedDeepslateBricks; + mappings[370] = ItemType.DeepslateTiles; + mappings[371] = ItemType.CrackedDeepslateTiles; + mappings[372] = ItemType.ChiseledDeepslate; + mappings[373] = ItemType.ReinforcedDeepslate; + mappings[374] = ItemType.BrownMushroomBlock; + mappings[375] = ItemType.RedMushroomBlock; + mappings[376] = ItemType.MushroomStem; + mappings[377] = ItemType.IronBars; + mappings[378] = ItemType.Chain; + mappings[379] = ItemType.GlassPane; + mappings[380] = ItemType.Melon; + mappings[381] = ItemType.Vine; + mappings[382] = ItemType.GlowLichen; + mappings[383] = ItemType.ResinClump; + mappings[384] = ItemType.ResinBlock; + mappings[385] = ItemType.ResinBricks; + mappings[386] = ItemType.ResinBrickStairs; + mappings[387] = ItemType.ResinBrickSlab; + mappings[388] = ItemType.ResinBrickWall; + mappings[389] = ItemType.ChiseledResinBricks; + mappings[390] = ItemType.BrickStairs; + mappings[391] = ItemType.StoneBrickStairs; + mappings[392] = ItemType.MudBrickStairs; + mappings[393] = ItemType.Mycelium; + mappings[394] = ItemType.LilyPad; + mappings[395] = ItemType.NetherBricks; + mappings[396] = ItemType.CrackedNetherBricks; + mappings[397] = ItemType.ChiseledNetherBricks; + mappings[398] = ItemType.NetherBrickFence; + mappings[399] = ItemType.NetherBrickStairs; + mappings[400] = ItemType.Sculk; + mappings[401] = ItemType.SculkVein; + mappings[402] = ItemType.SculkCatalyst; + mappings[403] = ItemType.SculkShrieker; + mappings[404] = ItemType.EnchantingTable; + mappings[405] = ItemType.EndPortalFrame; + mappings[406] = ItemType.EndStone; + mappings[407] = ItemType.EndStoneBricks; + mappings[408] = ItemType.DragonEgg; + mappings[409] = ItemType.SandstoneStairs; + mappings[410] = ItemType.EnderChest; + mappings[411] = ItemType.EmeraldBlock; + mappings[412] = ItemType.OakStairs; + mappings[413] = ItemType.SpruceStairs; + mappings[414] = ItemType.BirchStairs; + mappings[415] = ItemType.JungleStairs; + mappings[416] = ItemType.AcaciaStairs; + mappings[417] = ItemType.CherryStairs; + mappings[418] = ItemType.DarkOakStairs; + mappings[419] = ItemType.PaleOakStairs; + mappings[420] = ItemType.MangroveStairs; + mappings[421] = ItemType.BambooStairs; + mappings[422] = ItemType.BambooMosaicStairs; + mappings[423] = ItemType.CrimsonStairs; + mappings[424] = ItemType.WarpedStairs; + mappings[425] = ItemType.CommandBlock; + mappings[426] = ItemType.Beacon; + mappings[427] = ItemType.CobblestoneWall; + mappings[428] = ItemType.MossyCobblestoneWall; + mappings[429] = ItemType.BrickWall; + mappings[430] = ItemType.PrismarineWall; + mappings[431] = ItemType.RedSandstoneWall; + mappings[432] = ItemType.MossyStoneBrickWall; + mappings[433] = ItemType.GraniteWall; + mappings[434] = ItemType.StoneBrickWall; + mappings[435] = ItemType.MudBrickWall; + mappings[436] = ItemType.NetherBrickWall; + mappings[437] = ItemType.AndesiteWall; + mappings[438] = ItemType.RedNetherBrickWall; + mappings[439] = ItemType.SandstoneWall; + mappings[440] = ItemType.EndStoneBrickWall; + mappings[441] = ItemType.DioriteWall; + mappings[442] = ItemType.BlackstoneWall; + mappings[443] = ItemType.PolishedBlackstoneWall; + mappings[444] = ItemType.PolishedBlackstoneBrickWall; + mappings[445] = ItemType.CobbledDeepslateWall; + mappings[446] = ItemType.PolishedDeepslateWall; + mappings[447] = ItemType.DeepslateBrickWall; + mappings[448] = ItemType.DeepslateTileWall; + mappings[449] = ItemType.Anvil; + mappings[450] = ItemType.ChippedAnvil; + mappings[451] = ItemType.DamagedAnvil; + mappings[452] = ItemType.ChiseledQuartzBlock; + mappings[453] = ItemType.QuartzBlock; + mappings[454] = ItemType.QuartzBricks; + mappings[455] = ItemType.QuartzPillar; + mappings[456] = ItemType.QuartzStairs; + mappings[457] = ItemType.WhiteTerracotta; + mappings[458] = ItemType.OrangeTerracotta; + mappings[459] = ItemType.MagentaTerracotta; + mappings[460] = ItemType.LightBlueTerracotta; + mappings[461] = ItemType.YellowTerracotta; + mappings[462] = ItemType.LimeTerracotta; + mappings[463] = ItemType.PinkTerracotta; + mappings[464] = ItemType.GrayTerracotta; + mappings[465] = ItemType.LightGrayTerracotta; + mappings[466] = ItemType.CyanTerracotta; + mappings[467] = ItemType.PurpleTerracotta; + mappings[468] = ItemType.BlueTerracotta; + mappings[469] = ItemType.BrownTerracotta; + mappings[470] = ItemType.GreenTerracotta; + mappings[471] = ItemType.RedTerracotta; + mappings[472] = ItemType.BlackTerracotta; + mappings[473] = ItemType.Barrier; + mappings[474] = ItemType.Light; + mappings[475] = ItemType.HayBlock; + mappings[476] = ItemType.WhiteCarpet; + mappings[477] = ItemType.OrangeCarpet; + mappings[478] = ItemType.MagentaCarpet; + mappings[479] = ItemType.LightBlueCarpet; + mappings[480] = ItemType.YellowCarpet; + mappings[481] = ItemType.LimeCarpet; + mappings[482] = ItemType.PinkCarpet; + mappings[483] = ItemType.GrayCarpet; + mappings[484] = ItemType.LightGrayCarpet; + mappings[485] = ItemType.CyanCarpet; + mappings[486] = ItemType.PurpleCarpet; + mappings[487] = ItemType.BlueCarpet; + mappings[488] = ItemType.BrownCarpet; + mappings[489] = ItemType.GreenCarpet; + mappings[490] = ItemType.RedCarpet; + mappings[491] = ItemType.BlackCarpet; + mappings[492] = ItemType.Terracotta; + mappings[493] = ItemType.PackedIce; + mappings[494] = ItemType.DirtPath; + mappings[495] = ItemType.Sunflower; + mappings[496] = ItemType.Lilac; + mappings[497] = ItemType.RoseBush; + mappings[498] = ItemType.Peony; + mappings[499] = ItemType.TallGrass; + mappings[500] = ItemType.LargeFern; + mappings[501] = ItemType.WhiteStainedGlass; + mappings[502] = ItemType.OrangeStainedGlass; + mappings[503] = ItemType.MagentaStainedGlass; + mappings[504] = ItemType.LightBlueStainedGlass; + mappings[505] = ItemType.YellowStainedGlass; + mappings[506] = ItemType.LimeStainedGlass; + mappings[507] = ItemType.PinkStainedGlass; + mappings[508] = ItemType.GrayStainedGlass; + mappings[509] = ItemType.LightGrayStainedGlass; + mappings[510] = ItemType.CyanStainedGlass; + mappings[511] = ItemType.PurpleStainedGlass; + mappings[512] = ItemType.BlueStainedGlass; + mappings[513] = ItemType.BrownStainedGlass; + mappings[514] = ItemType.GreenStainedGlass; + mappings[515] = ItemType.RedStainedGlass; + mappings[516] = ItemType.BlackStainedGlass; + mappings[517] = ItemType.WhiteStainedGlassPane; + mappings[518] = ItemType.OrangeStainedGlassPane; + mappings[519] = ItemType.MagentaStainedGlassPane; + mappings[520] = ItemType.LightBlueStainedGlassPane; + mappings[521] = ItemType.YellowStainedGlassPane; + mappings[522] = ItemType.LimeStainedGlassPane; + mappings[523] = ItemType.PinkStainedGlassPane; + mappings[524] = ItemType.GrayStainedGlassPane; + mappings[525] = ItemType.LightGrayStainedGlassPane; + mappings[526] = ItemType.CyanStainedGlassPane; + mappings[527] = ItemType.PurpleStainedGlassPane; + mappings[528] = ItemType.BlueStainedGlassPane; + mappings[529] = ItemType.BrownStainedGlassPane; + mappings[530] = ItemType.GreenStainedGlassPane; + mappings[531] = ItemType.RedStainedGlassPane; + mappings[532] = ItemType.BlackStainedGlassPane; + mappings[533] = ItemType.Prismarine; + mappings[534] = ItemType.PrismarineBricks; + mappings[535] = ItemType.DarkPrismarine; + mappings[536] = ItemType.PrismarineStairs; + mappings[537] = ItemType.PrismarineBrickStairs; + mappings[538] = ItemType.DarkPrismarineStairs; + mappings[539] = ItemType.SeaLantern; + mappings[540] = ItemType.RedSandstone; + mappings[541] = ItemType.ChiseledRedSandstone; + mappings[542] = ItemType.CutRedSandstone; + mappings[543] = ItemType.RedSandstoneStairs; + mappings[544] = ItemType.RepeatingCommandBlock; + mappings[545] = ItemType.ChainCommandBlock; + mappings[546] = ItemType.MagmaBlock; + mappings[547] = ItemType.NetherWartBlock; + mappings[548] = ItemType.WarpedWartBlock; + mappings[549] = ItemType.RedNetherBricks; + mappings[550] = ItemType.BoneBlock; + mappings[551] = ItemType.StructureVoid; + mappings[552] = ItemType.ShulkerBox; + mappings[553] = ItemType.WhiteShulkerBox; + mappings[554] = ItemType.OrangeShulkerBox; + mappings[555] = ItemType.MagentaShulkerBox; + mappings[556] = ItemType.LightBlueShulkerBox; + mappings[557] = ItemType.YellowShulkerBox; + mappings[558] = ItemType.LimeShulkerBox; + mappings[559] = ItemType.PinkShulkerBox; + mappings[560] = ItemType.GrayShulkerBox; + mappings[561] = ItemType.LightGrayShulkerBox; + mappings[562] = ItemType.CyanShulkerBox; + mappings[563] = ItemType.PurpleShulkerBox; + mappings[564] = ItemType.BlueShulkerBox; + mappings[565] = ItemType.BrownShulkerBox; + mappings[566] = ItemType.GreenShulkerBox; + mappings[567] = ItemType.RedShulkerBox; + mappings[568] = ItemType.BlackShulkerBox; + mappings[569] = ItemType.WhiteGlazedTerracotta; + mappings[570] = ItemType.OrangeGlazedTerracotta; + mappings[571] = ItemType.MagentaGlazedTerracotta; + mappings[572] = ItemType.LightBlueGlazedTerracotta; + mappings[573] = ItemType.YellowGlazedTerracotta; + mappings[574] = ItemType.LimeGlazedTerracotta; + mappings[575] = ItemType.PinkGlazedTerracotta; + mappings[576] = ItemType.GrayGlazedTerracotta; + mappings[577] = ItemType.LightGrayGlazedTerracotta; + mappings[578] = ItemType.CyanGlazedTerracotta; + mappings[579] = ItemType.PurpleGlazedTerracotta; + mappings[580] = ItemType.BlueGlazedTerracotta; + mappings[581] = ItemType.BrownGlazedTerracotta; + mappings[582] = ItemType.GreenGlazedTerracotta; + mappings[583] = ItemType.RedGlazedTerracotta; + mappings[584] = ItemType.BlackGlazedTerracotta; + mappings[585] = ItemType.WhiteConcrete; + mappings[586] = ItemType.OrangeConcrete; + mappings[587] = ItemType.MagentaConcrete; + mappings[588] = ItemType.LightBlueConcrete; + mappings[589] = ItemType.YellowConcrete; + mappings[590] = ItemType.LimeConcrete; + mappings[591] = ItemType.PinkConcrete; + mappings[592] = ItemType.GrayConcrete; + mappings[593] = ItemType.LightGrayConcrete; + mappings[594] = ItemType.CyanConcrete; + mappings[595] = ItemType.PurpleConcrete; + mappings[596] = ItemType.BlueConcrete; + mappings[597] = ItemType.BrownConcrete; + mappings[598] = ItemType.GreenConcrete; + mappings[599] = ItemType.RedConcrete; + mappings[600] = ItemType.BlackConcrete; + mappings[601] = ItemType.WhiteConcretePowder; + mappings[602] = ItemType.OrangeConcretePowder; + mappings[603] = ItemType.MagentaConcretePowder; + mappings[604] = ItemType.LightBlueConcretePowder; + mappings[605] = ItemType.YellowConcretePowder; + mappings[606] = ItemType.LimeConcretePowder; + mappings[607] = ItemType.PinkConcretePowder; + mappings[608] = ItemType.GrayConcretePowder; + mappings[609] = ItemType.LightGrayConcretePowder; + mappings[610] = ItemType.CyanConcretePowder; + mappings[611] = ItemType.PurpleConcretePowder; + mappings[612] = ItemType.BlueConcretePowder; + mappings[613] = ItemType.BrownConcretePowder; + mappings[614] = ItemType.GreenConcretePowder; + mappings[615] = ItemType.RedConcretePowder; + mappings[616] = ItemType.BlackConcretePowder; + mappings[617] = ItemType.TurtleEgg; + mappings[618] = ItemType.SnifferEgg; + mappings[619] = ItemType.DriedGhast; + mappings[620] = ItemType.DeadTubeCoralBlock; + mappings[621] = ItemType.DeadBrainCoralBlock; + mappings[622] = ItemType.DeadBubbleCoralBlock; + mappings[623] = ItemType.DeadFireCoralBlock; + mappings[624] = ItemType.DeadHornCoralBlock; + mappings[625] = ItemType.TubeCoralBlock; + mappings[626] = ItemType.BrainCoralBlock; + mappings[627] = ItemType.BubbleCoralBlock; + mappings[628] = ItemType.FireCoralBlock; + mappings[629] = ItemType.HornCoralBlock; + mappings[630] = ItemType.TubeCoral; + mappings[631] = ItemType.BrainCoral; + mappings[632] = ItemType.BubbleCoral; + mappings[633] = ItemType.FireCoral; + mappings[634] = ItemType.HornCoral; + mappings[635] = ItemType.DeadBrainCoral; + mappings[636] = ItemType.DeadBubbleCoral; + mappings[637] = ItemType.DeadFireCoral; + mappings[638] = ItemType.DeadHornCoral; + mappings[639] = ItemType.DeadTubeCoral; + mappings[640] = ItemType.TubeCoralFan; + mappings[641] = ItemType.BrainCoralFan; + mappings[642] = ItemType.BubbleCoralFan; + mappings[643] = ItemType.FireCoralFan; + mappings[644] = ItemType.HornCoralFan; + mappings[645] = ItemType.DeadTubeCoralFan; + mappings[646] = ItemType.DeadBrainCoralFan; + mappings[647] = ItemType.DeadBubbleCoralFan; + mappings[648] = ItemType.DeadFireCoralFan; + mappings[649] = ItemType.DeadHornCoralFan; + mappings[650] = ItemType.BlueIce; + mappings[651] = ItemType.Conduit; + mappings[652] = ItemType.PolishedGraniteStairs; + mappings[653] = ItemType.SmoothRedSandstoneStairs; + mappings[654] = ItemType.MossyStoneBrickStairs; + mappings[655] = ItemType.PolishedDioriteStairs; + mappings[656] = ItemType.MossyCobblestoneStairs; + mappings[657] = ItemType.EndStoneBrickStairs; + mappings[658] = ItemType.StoneStairs; + mappings[659] = ItemType.SmoothSandstoneStairs; + mappings[660] = ItemType.SmoothQuartzStairs; + mappings[661] = ItemType.GraniteStairs; + mappings[662] = ItemType.AndesiteStairs; + mappings[663] = ItemType.RedNetherBrickStairs; + mappings[664] = ItemType.PolishedAndesiteStairs; + mappings[665] = ItemType.DioriteStairs; + mappings[666] = ItemType.CobbledDeepslateStairs; + mappings[667] = ItemType.PolishedDeepslateStairs; + mappings[668] = ItemType.DeepslateBrickStairs; + mappings[669] = ItemType.DeepslateTileStairs; + mappings[670] = ItemType.PolishedGraniteSlab; + mappings[671] = ItemType.SmoothRedSandstoneSlab; + mappings[672] = ItemType.MossyStoneBrickSlab; + mappings[673] = ItemType.PolishedDioriteSlab; + mappings[674] = ItemType.MossyCobblestoneSlab; + mappings[675] = ItemType.EndStoneBrickSlab; + mappings[676] = ItemType.SmoothSandstoneSlab; + mappings[677] = ItemType.SmoothQuartzSlab; + mappings[678] = ItemType.GraniteSlab; + mappings[679] = ItemType.AndesiteSlab; + mappings[680] = ItemType.RedNetherBrickSlab; + mappings[681] = ItemType.PolishedAndesiteSlab; + mappings[682] = ItemType.DioriteSlab; + mappings[683] = ItemType.CobbledDeepslateSlab; + mappings[684] = ItemType.PolishedDeepslateSlab; + mappings[685] = ItemType.DeepslateBrickSlab; + mappings[686] = ItemType.DeepslateTileSlab; + mappings[687] = ItemType.Scaffolding; + mappings[688] = ItemType.Redstone; + mappings[689] = ItemType.RedstoneTorch; + mappings[690] = ItemType.RedstoneBlock; + mappings[691] = ItemType.Repeater; + mappings[692] = ItemType.Comparator; + mappings[693] = ItemType.Piston; + mappings[694] = ItemType.StickyPiston; + mappings[695] = ItemType.SlimeBlock; + mappings[696] = ItemType.HoneyBlock; + mappings[697] = ItemType.Observer; + mappings[698] = ItemType.Hopper; + mappings[699] = ItemType.Dispenser; + mappings[700] = ItemType.Dropper; + mappings[701] = ItemType.Lectern; + mappings[702] = ItemType.Target; + mappings[703] = ItemType.Lever; + mappings[704] = ItemType.LightningRod; + mappings[705] = ItemType.DaylightDetector; + mappings[706] = ItemType.SculkSensor; + mappings[707] = ItemType.CalibratedSculkSensor; + mappings[708] = ItemType.TripwireHook; + mappings[709] = ItemType.TrappedChest; + mappings[710] = ItemType.Tnt; + mappings[711] = ItemType.RedstoneLamp; + mappings[712] = ItemType.NoteBlock; + mappings[713] = ItemType.StoneButton; + mappings[714] = ItemType.PolishedBlackstoneButton; + mappings[715] = ItemType.OakButton; + mappings[716] = ItemType.SpruceButton; + mappings[717] = ItemType.BirchButton; + mappings[718] = ItemType.JungleButton; + mappings[719] = ItemType.AcaciaButton; + mappings[720] = ItemType.CherryButton; + mappings[721] = ItemType.DarkOakButton; + mappings[722] = ItemType.PaleOakButton; + mappings[723] = ItemType.MangroveButton; + mappings[724] = ItemType.BambooButton; + mappings[725] = ItemType.CrimsonButton; + mappings[726] = ItemType.WarpedButton; + mappings[727] = ItemType.StonePressurePlate; + mappings[728] = ItemType.PolishedBlackstonePressurePlate; + mappings[729] = ItemType.LightWeightedPressurePlate; + mappings[730] = ItemType.HeavyWeightedPressurePlate; + mappings[731] = ItemType.OakPressurePlate; + mappings[732] = ItemType.SprucePressurePlate; + mappings[733] = ItemType.BirchPressurePlate; + mappings[734] = ItemType.JunglePressurePlate; + mappings[735] = ItemType.AcaciaPressurePlate; + mappings[736] = ItemType.CherryPressurePlate; + mappings[737] = ItemType.DarkOakPressurePlate; + mappings[738] = ItemType.PaleOakPressurePlate; + mappings[739] = ItemType.MangrovePressurePlate; + mappings[740] = ItemType.BambooPressurePlate; + mappings[741] = ItemType.CrimsonPressurePlate; + mappings[742] = ItemType.WarpedPressurePlate; + mappings[743] = ItemType.IronDoor; + mappings[744] = ItemType.OakDoor; + mappings[745] = ItemType.SpruceDoor; + mappings[746] = ItemType.BirchDoor; + mappings[747] = ItemType.JungleDoor; + mappings[748] = ItemType.AcaciaDoor; + mappings[749] = ItemType.CherryDoor; + mappings[750] = ItemType.DarkOakDoor; + mappings[751] = ItemType.PaleOakDoor; + mappings[752] = ItemType.MangroveDoor; + mappings[753] = ItemType.BambooDoor; + mappings[754] = ItemType.CrimsonDoor; + mappings[755] = ItemType.WarpedDoor; + mappings[756] = ItemType.CopperDoor; + mappings[757] = ItemType.ExposedCopperDoor; + mappings[758] = ItemType.WeatheredCopperDoor; + mappings[759] = ItemType.OxidizedCopperDoor; + mappings[760] = ItemType.WaxedCopperDoor; + mappings[761] = ItemType.WaxedExposedCopperDoor; + mappings[762] = ItemType.WaxedWeatheredCopperDoor; + mappings[763] = ItemType.WaxedOxidizedCopperDoor; + mappings[764] = ItemType.IronTrapdoor; + mappings[765] = ItemType.OakTrapdoor; + mappings[766] = ItemType.SpruceTrapdoor; + mappings[767] = ItemType.BirchTrapdoor; + mappings[768] = ItemType.JungleTrapdoor; + mappings[769] = ItemType.AcaciaTrapdoor; + mappings[770] = ItemType.CherryTrapdoor; + mappings[771] = ItemType.DarkOakTrapdoor; + mappings[772] = ItemType.PaleOakTrapdoor; + mappings[773] = ItemType.MangroveTrapdoor; + mappings[774] = ItemType.BambooTrapdoor; + mappings[775] = ItemType.CrimsonTrapdoor; + mappings[776] = ItemType.WarpedTrapdoor; + mappings[777] = ItemType.CopperTrapdoor; + mappings[778] = ItemType.ExposedCopperTrapdoor; + mappings[779] = ItemType.WeatheredCopperTrapdoor; + mappings[780] = ItemType.OxidizedCopperTrapdoor; + mappings[781] = ItemType.WaxedCopperTrapdoor; + mappings[782] = ItemType.WaxedExposedCopperTrapdoor; + mappings[783] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[784] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[785] = ItemType.OakFenceGate; + mappings[786] = ItemType.SpruceFenceGate; + mappings[787] = ItemType.BirchFenceGate; + mappings[788] = ItemType.JungleFenceGate; + mappings[789] = ItemType.AcaciaFenceGate; + mappings[790] = ItemType.CherryFenceGate; + mappings[791] = ItemType.DarkOakFenceGate; + mappings[792] = ItemType.PaleOakFenceGate; + mappings[793] = ItemType.MangroveFenceGate; + mappings[794] = ItemType.BambooFenceGate; + mappings[795] = ItemType.CrimsonFenceGate; + mappings[796] = ItemType.WarpedFenceGate; + mappings[797] = ItemType.PoweredRail; + mappings[798] = ItemType.DetectorRail; + mappings[799] = ItemType.Rail; + mappings[800] = ItemType.ActivatorRail; + mappings[801] = ItemType.Saddle; + mappings[802] = ItemType.WhiteHarness; + mappings[803] = ItemType.OrangeHarness; + mappings[804] = ItemType.MagentaHarness; + mappings[805] = ItemType.LightBlueHarness; + mappings[806] = ItemType.YellowHarness; + mappings[807] = ItemType.LimeHarness; + mappings[808] = ItemType.PinkHarness; + mappings[809] = ItemType.GrayHarness; + mappings[810] = ItemType.LightGrayHarness; + mappings[811] = ItemType.CyanHarness; + mappings[812] = ItemType.PurpleHarness; + mappings[813] = ItemType.BlueHarness; + mappings[814] = ItemType.BrownHarness; + mappings[815] = ItemType.GreenHarness; + mappings[816] = ItemType.RedHarness; + mappings[817] = ItemType.BlackHarness; + mappings[818] = ItemType.Minecart; + mappings[819] = ItemType.ChestMinecart; + mappings[820] = ItemType.FurnaceMinecart; + mappings[821] = ItemType.TntMinecart; + mappings[822] = ItemType.HopperMinecart; + mappings[823] = ItemType.CarrotOnAStick; + mappings[824] = ItemType.WarpedFungusOnAStick; + mappings[825] = ItemType.PhantomMembrane; + mappings[826] = ItemType.Elytra; + mappings[827] = ItemType.OakBoat; + mappings[828] = ItemType.OakChestBoat; + mappings[829] = ItemType.SpruceBoat; + mappings[830] = ItemType.SpruceChestBoat; + mappings[831] = ItemType.BirchBoat; + mappings[832] = ItemType.BirchChestBoat; + mappings[833] = ItemType.JungleBoat; + mappings[834] = ItemType.JungleChestBoat; + mappings[835] = ItemType.AcaciaBoat; + mappings[836] = ItemType.AcaciaChestBoat; + mappings[837] = ItemType.CherryBoat; + mappings[838] = ItemType.CherryChestBoat; + mappings[839] = ItemType.DarkOakBoat; + mappings[840] = ItemType.DarkOakChestBoat; + mappings[841] = ItemType.PaleOakBoat; + mappings[842] = ItemType.PaleOakChestBoat; + mappings[843] = ItemType.MangroveBoat; + mappings[844] = ItemType.MangroveChestBoat; + mappings[845] = ItemType.BambooRaft; + mappings[846] = ItemType.BambooChestRaft; + mappings[847] = ItemType.StructureBlock; + mappings[848] = ItemType.Jigsaw; + mappings[849] = ItemType.TestBlock; + mappings[850] = ItemType.TestInstanceBlock; + mappings[851] = ItemType.TurtleHelmet; + mappings[852] = ItemType.TurtleScute; + mappings[853] = ItemType.ArmadilloScute; + mappings[854] = ItemType.WolfArmor; + mappings[855] = ItemType.FlintAndSteel; + mappings[856] = ItemType.Bowl; + mappings[857] = ItemType.Apple; + mappings[858] = ItemType.Bow; + mappings[859] = ItemType.Arrow; + mappings[860] = ItemType.Coal; + mappings[861] = ItemType.Charcoal; + mappings[862] = ItemType.Diamond; + mappings[863] = ItemType.Emerald; + mappings[864] = ItemType.LapisLazuli; + mappings[865] = ItemType.Quartz; + mappings[866] = ItemType.AmethystShard; + mappings[867] = ItemType.RawIron; + mappings[868] = ItemType.IronIngot; + mappings[869] = ItemType.RawCopper; + mappings[870] = ItemType.CopperIngot; + mappings[871] = ItemType.RawGold; + mappings[872] = ItemType.GoldIngot; + mappings[873] = ItemType.NetheriteIngot; + mappings[874] = ItemType.NetheriteScrap; + mappings[875] = ItemType.WoodenSword; + mappings[876] = ItemType.WoodenShovel; + mappings[877] = ItemType.WoodenPickaxe; + mappings[878] = ItemType.WoodenAxe; + mappings[879] = ItemType.WoodenHoe; + mappings[880] = ItemType.StoneSword; + mappings[881] = ItemType.StoneShovel; + mappings[882] = ItemType.StonePickaxe; + mappings[883] = ItemType.StoneAxe; + mappings[884] = ItemType.StoneHoe; + mappings[885] = ItemType.GoldenSword; + mappings[886] = ItemType.GoldenShovel; + mappings[887] = ItemType.GoldenPickaxe; + mappings[888] = ItemType.GoldenAxe; + mappings[889] = ItemType.GoldenHoe; + mappings[890] = ItemType.IronSword; + mappings[891] = ItemType.IronShovel; + mappings[892] = ItemType.IronPickaxe; + mappings[893] = ItemType.IronAxe; + mappings[894] = ItemType.IronHoe; + mappings[895] = ItemType.DiamondSword; + mappings[896] = ItemType.DiamondShovel; + mappings[897] = ItemType.DiamondPickaxe; + mappings[898] = ItemType.DiamondAxe; + mappings[899] = ItemType.DiamondHoe; + mappings[900] = ItemType.NetheriteSword; + mappings[901] = ItemType.NetheriteShovel; + mappings[902] = ItemType.NetheritePickaxe; + mappings[903] = ItemType.NetheriteAxe; + mappings[904] = ItemType.NetheriteHoe; + mappings[905] = ItemType.Stick; + mappings[906] = ItemType.MushroomStew; + mappings[907] = ItemType.String; + mappings[908] = ItemType.Feather; + mappings[909] = ItemType.Gunpowder; + mappings[910] = ItemType.WheatSeeds; + mappings[911] = ItemType.Wheat; + mappings[912] = ItemType.Bread; + mappings[913] = ItemType.LeatherHelmet; + mappings[914] = ItemType.LeatherChestplate; + mappings[915] = ItemType.LeatherLeggings; + mappings[916] = ItemType.LeatherBoots; + mappings[917] = ItemType.ChainmailHelmet; + mappings[918] = ItemType.ChainmailChestplate; + mappings[919] = ItemType.ChainmailLeggings; + mappings[920] = ItemType.ChainmailBoots; + mappings[921] = ItemType.IronHelmet; + mappings[922] = ItemType.IronChestplate; + mappings[923] = ItemType.IronLeggings; + mappings[924] = ItemType.IronBoots; + mappings[925] = ItemType.DiamondHelmet; + mappings[926] = ItemType.DiamondChestplate; + mappings[927] = ItemType.DiamondLeggings; + mappings[928] = ItemType.DiamondBoots; + mappings[929] = ItemType.GoldenHelmet; + mappings[930] = ItemType.GoldenChestplate; + mappings[931] = ItemType.GoldenLeggings; + mappings[932] = ItemType.GoldenBoots; + mappings[933] = ItemType.NetheriteHelmet; + mappings[934] = ItemType.NetheriteChestplate; + mappings[935] = ItemType.NetheriteLeggings; + mappings[936] = ItemType.NetheriteBoots; + mappings[937] = ItemType.Flint; + mappings[938] = ItemType.Porkchop; + mappings[939] = ItemType.CookedPorkchop; + mappings[940] = ItemType.Painting; + mappings[941] = ItemType.GoldenApple; + mappings[942] = ItemType.EnchantedGoldenApple; + mappings[943] = ItemType.OakSign; + mappings[944] = ItemType.SpruceSign; + mappings[945] = ItemType.BirchSign; + mappings[946] = ItemType.JungleSign; + mappings[947] = ItemType.AcaciaSign; + mappings[948] = ItemType.CherrySign; + mappings[949] = ItemType.DarkOakSign; + mappings[950] = ItemType.PaleOakSign; + mappings[951] = ItemType.MangroveSign; + mappings[952] = ItemType.BambooSign; + mappings[953] = ItemType.CrimsonSign; + mappings[954] = ItemType.WarpedSign; + mappings[955] = ItemType.OakHangingSign; + mappings[956] = ItemType.SpruceHangingSign; + mappings[957] = ItemType.BirchHangingSign; + mappings[958] = ItemType.JungleHangingSign; + mappings[959] = ItemType.AcaciaHangingSign; + mappings[960] = ItemType.CherryHangingSign; + mappings[961] = ItemType.DarkOakHangingSign; + mappings[962] = ItemType.PaleOakHangingSign; + mappings[963] = ItemType.MangroveHangingSign; + mappings[964] = ItemType.BambooHangingSign; + mappings[965] = ItemType.CrimsonHangingSign; + mappings[966] = ItemType.WarpedHangingSign; + mappings[967] = ItemType.Bucket; + mappings[968] = ItemType.WaterBucket; + mappings[969] = ItemType.LavaBucket; + mappings[970] = ItemType.PowderSnowBucket; + mappings[971] = ItemType.Snowball; + mappings[972] = ItemType.Leather; + mappings[973] = ItemType.MilkBucket; + mappings[974] = ItemType.PufferfishBucket; + mappings[975] = ItemType.SalmonBucket; + mappings[976] = ItemType.CodBucket; + mappings[977] = ItemType.TropicalFishBucket; + mappings[978] = ItemType.AxolotlBucket; + mappings[979] = ItemType.TadpoleBucket; + mappings[980] = ItemType.Brick; + mappings[981] = ItemType.ClayBall; + mappings[982] = ItemType.DriedKelpBlock; + mappings[983] = ItemType.Paper; + mappings[984] = ItemType.Book; + mappings[985] = ItemType.SlimeBall; + mappings[986] = ItemType.Egg; + mappings[987] = ItemType.BlueEgg; + mappings[988] = ItemType.BrownEgg; + mappings[989] = ItemType.Compass; + mappings[990] = ItemType.RecoveryCompass; + mappings[991] = ItemType.Bundle; + mappings[992] = ItemType.WhiteBundle; + mappings[993] = ItemType.OrangeBundle; + mappings[994] = ItemType.MagentaBundle; + mappings[995] = ItemType.LightBlueBundle; + mappings[996] = ItemType.YellowBundle; + mappings[997] = ItemType.LimeBundle; + mappings[998] = ItemType.PinkBundle; + mappings[999] = ItemType.GrayBundle; + mappings[1000] = ItemType.LightGrayBundle; + mappings[1001] = ItemType.CyanBundle; + mappings[1002] = ItemType.PurpleBundle; + mappings[1003] = ItemType.BlueBundle; + mappings[1004] = ItemType.BrownBundle; + mappings[1005] = ItemType.GreenBundle; + mappings[1006] = ItemType.RedBundle; + mappings[1007] = ItemType.BlackBundle; + mappings[1008] = ItemType.FishingRod; + mappings[1009] = ItemType.Clock; + mappings[1010] = ItemType.Spyglass; + mappings[1011] = ItemType.GlowstoneDust; + mappings[1012] = ItemType.Cod; + mappings[1013] = ItemType.Salmon; + mappings[1014] = ItemType.TropicalFish; + mappings[1015] = ItemType.Pufferfish; + mappings[1016] = ItemType.CookedCod; + mappings[1017] = ItemType.CookedSalmon; + mappings[1018] = ItemType.InkSac; + mappings[1019] = ItemType.GlowInkSac; + mappings[1020] = ItemType.CocoaBeans; + mappings[1021] = ItemType.WhiteDye; + mappings[1022] = ItemType.OrangeDye; + mappings[1023] = ItemType.MagentaDye; + mappings[1024] = ItemType.LightBlueDye; + mappings[1025] = ItemType.YellowDye; + mappings[1026] = ItemType.LimeDye; + mappings[1027] = ItemType.PinkDye; + mappings[1028] = ItemType.GrayDye; + mappings[1029] = ItemType.LightGrayDye; + mappings[1030] = ItemType.CyanDye; + mappings[1031] = ItemType.PurpleDye; + mappings[1032] = ItemType.BlueDye; + mappings[1033] = ItemType.BrownDye; + mappings[1034] = ItemType.GreenDye; + mappings[1035] = ItemType.RedDye; + mappings[1036] = ItemType.BlackDye; + mappings[1037] = ItemType.BoneMeal; + mappings[1038] = ItemType.Bone; + mappings[1039] = ItemType.Sugar; + mappings[1040] = ItemType.Cake; + mappings[1041] = ItemType.WhiteBed; + mappings[1042] = ItemType.OrangeBed; + mappings[1043] = ItemType.MagentaBed; + mappings[1044] = ItemType.LightBlueBed; + mappings[1045] = ItemType.YellowBed; + mappings[1046] = ItemType.LimeBed; + mappings[1047] = ItemType.PinkBed; + mappings[1048] = ItemType.GrayBed; + mappings[1049] = ItemType.LightGrayBed; + mappings[1050] = ItemType.CyanBed; + mappings[1051] = ItemType.PurpleBed; + mappings[1052] = ItemType.BlueBed; + mappings[1053] = ItemType.BrownBed; + mappings[1054] = ItemType.GreenBed; + mappings[1055] = ItemType.RedBed; + mappings[1056] = ItemType.BlackBed; + mappings[1057] = ItemType.Cookie; + mappings[1058] = ItemType.Crafter; + mappings[1059] = ItemType.FilledMap; + mappings[1060] = ItemType.Shears; + mappings[1061] = ItemType.MelonSlice; + mappings[1062] = ItemType.DriedKelp; + mappings[1063] = ItemType.PumpkinSeeds; + mappings[1064] = ItemType.MelonSeeds; + mappings[1065] = ItemType.Beef; + mappings[1066] = ItemType.CookedBeef; + mappings[1067] = ItemType.Chicken; + mappings[1068] = ItemType.CookedChicken; + mappings[1069] = ItemType.RottenFlesh; + mappings[1070] = ItemType.EnderPearl; + mappings[1071] = ItemType.BlazeRod; + mappings[1072] = ItemType.GhastTear; + mappings[1073] = ItemType.GoldNugget; + mappings[1074] = ItemType.NetherWart; + mappings[1075] = ItemType.GlassBottle; + mappings[1076] = ItemType.Potion; + mappings[1077] = ItemType.SpiderEye; + mappings[1078] = ItemType.FermentedSpiderEye; + mappings[1079] = ItemType.BlazePowder; + mappings[1080] = ItemType.MagmaCream; + mappings[1081] = ItemType.BrewingStand; + mappings[1082] = ItemType.Cauldron; + mappings[1083] = ItemType.EnderEye; + mappings[1084] = ItemType.GlisteringMelonSlice; + mappings[1085] = ItemType.ArmadilloSpawnEgg; + mappings[1086] = ItemType.AllaySpawnEgg; + mappings[1087] = ItemType.AxolotlSpawnEgg; + mappings[1088] = ItemType.BatSpawnEgg; + mappings[1089] = ItemType.BeeSpawnEgg; + mappings[1090] = ItemType.BlazeSpawnEgg; + mappings[1091] = ItemType.BoggedSpawnEgg; + mappings[1092] = ItemType.BreezeSpawnEgg; + mappings[1093] = ItemType.CatSpawnEgg; + mappings[1094] = ItemType.CamelSpawnEgg; + mappings[1095] = ItemType.CaveSpiderSpawnEgg; + mappings[1096] = ItemType.ChickenSpawnEgg; + mappings[1097] = ItemType.CodSpawnEgg; + mappings[1098] = ItemType.CowSpawnEgg; + mappings[1099] = ItemType.CreeperSpawnEgg; + mappings[1100] = ItemType.DolphinSpawnEgg; + mappings[1101] = ItemType.DonkeySpawnEgg; + mappings[1102] = ItemType.DrownedSpawnEgg; + mappings[1103] = ItemType.ElderGuardianSpawnEgg; + mappings[1104] = ItemType.EnderDragonSpawnEgg; + mappings[1105] = ItemType.EndermanSpawnEgg; + mappings[1106] = ItemType.EndermiteSpawnEgg; + mappings[1107] = ItemType.EvokerSpawnEgg; + mappings[1108] = ItemType.FoxSpawnEgg; + mappings[1109] = ItemType.FrogSpawnEgg; + mappings[1110] = ItemType.GhastSpawnEgg; + mappings[1111] = ItemType.HappyGhastSpawnEgg; + mappings[1112] = ItemType.GlowSquidSpawnEgg; + mappings[1113] = ItemType.GoatSpawnEgg; + mappings[1114] = ItemType.GuardianSpawnEgg; + mappings[1115] = ItemType.HoglinSpawnEgg; + mappings[1116] = ItemType.HorseSpawnEgg; + mappings[1117] = ItemType.HuskSpawnEgg; + mappings[1118] = ItemType.IronGolemSpawnEgg; + mappings[1119] = ItemType.LlamaSpawnEgg; + mappings[1120] = ItemType.MagmaCubeSpawnEgg; + mappings[1121] = ItemType.MooshroomSpawnEgg; + mappings[1122] = ItemType.MuleSpawnEgg; + mappings[1123] = ItemType.OcelotSpawnEgg; + mappings[1124] = ItemType.PandaSpawnEgg; + mappings[1125] = ItemType.ParrotSpawnEgg; + mappings[1126] = ItemType.PhantomSpawnEgg; + mappings[1127] = ItemType.PigSpawnEgg; + mappings[1128] = ItemType.PiglinSpawnEgg; + mappings[1129] = ItemType.PiglinBruteSpawnEgg; + mappings[1130] = ItemType.PillagerSpawnEgg; + mappings[1131] = ItemType.PolarBearSpawnEgg; + mappings[1132] = ItemType.PufferfishSpawnEgg; + mappings[1133] = ItemType.RabbitSpawnEgg; + mappings[1134] = ItemType.RavagerSpawnEgg; + mappings[1135] = ItemType.SalmonSpawnEgg; + mappings[1136] = ItemType.SheepSpawnEgg; + mappings[1137] = ItemType.ShulkerSpawnEgg; + mappings[1138] = ItemType.SilverfishSpawnEgg; + mappings[1139] = ItemType.SkeletonSpawnEgg; + mappings[1140] = ItemType.SkeletonHorseSpawnEgg; + mappings[1141] = ItemType.SlimeSpawnEgg; + mappings[1142] = ItemType.SnifferSpawnEgg; + mappings[1143] = ItemType.SnowGolemSpawnEgg; + mappings[1144] = ItemType.SpiderSpawnEgg; + mappings[1145] = ItemType.SquidSpawnEgg; + mappings[1146] = ItemType.StraySpawnEgg; + mappings[1147] = ItemType.StriderSpawnEgg; + mappings[1148] = ItemType.TadpoleSpawnEgg; + mappings[1149] = ItemType.TraderLlamaSpawnEgg; + mappings[1150] = ItemType.TropicalFishSpawnEgg; + mappings[1151] = ItemType.TurtleSpawnEgg; + mappings[1152] = ItemType.VexSpawnEgg; + mappings[1153] = ItemType.VillagerSpawnEgg; + mappings[1154] = ItemType.VindicatorSpawnEgg; + mappings[1155] = ItemType.WanderingTraderSpawnEgg; + mappings[1156] = ItemType.WardenSpawnEgg; + mappings[1157] = ItemType.WitchSpawnEgg; + mappings[1158] = ItemType.WitherSpawnEgg; + mappings[1159] = ItemType.WitherSkeletonSpawnEgg; + mappings[1160] = ItemType.WolfSpawnEgg; + mappings[1161] = ItemType.ZoglinSpawnEgg; + mappings[1162] = ItemType.CreakingSpawnEgg; + mappings[1163] = ItemType.ZombieSpawnEgg; + mappings[1164] = ItemType.ZombieHorseSpawnEgg; + mappings[1165] = ItemType.ZombieVillagerSpawnEgg; + mappings[1166] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1167] = ItemType.ExperienceBottle; + mappings[1168] = ItemType.FireCharge; + mappings[1169] = ItemType.WindCharge; + mappings[1170] = ItemType.WritableBook; + mappings[1171] = ItemType.WrittenBook; + mappings[1172] = ItemType.BreezeRod; + mappings[1173] = ItemType.Mace; + mappings[1174] = ItemType.ItemFrame; + mappings[1175] = ItemType.GlowItemFrame; + mappings[1176] = ItemType.FlowerPot; + mappings[1177] = ItemType.Carrot; + mappings[1178] = ItemType.Potato; + mappings[1179] = ItemType.BakedPotato; + mappings[1180] = ItemType.PoisonousPotato; + mappings[1181] = ItemType.Map; + mappings[1182] = ItemType.GoldenCarrot; + mappings[1183] = ItemType.SkeletonSkull; + mappings[1184] = ItemType.WitherSkeletonSkull; + mappings[1185] = ItemType.PlayerHead; + mappings[1186] = ItemType.ZombieHead; + mappings[1187] = ItemType.CreeperHead; + mappings[1188] = ItemType.DragonHead; + mappings[1189] = ItemType.PiglinHead; + mappings[1190] = ItemType.NetherStar; + mappings[1191] = ItemType.PumpkinPie; + mappings[1192] = ItemType.FireworkRocket; + mappings[1193] = ItemType.FireworkStar; + mappings[1194] = ItemType.EnchantedBook; + mappings[1195] = ItemType.NetherBrick; + mappings[1196] = ItemType.ResinBrick; + mappings[1197] = ItemType.PrismarineShard; + mappings[1198] = ItemType.PrismarineCrystals; + mappings[1199] = ItemType.Rabbit; + mappings[1200] = ItemType.CookedRabbit; + mappings[1201] = ItemType.RabbitStew; + mappings[1202] = ItemType.RabbitFoot; + mappings[1203] = ItemType.RabbitHide; + mappings[1204] = ItemType.ArmorStand; + mappings[1205] = ItemType.IronHorseArmor; + mappings[1206] = ItemType.GoldenHorseArmor; + mappings[1207] = ItemType.DiamondHorseArmor; + mappings[1208] = ItemType.LeatherHorseArmor; + mappings[1209] = ItemType.Lead; + mappings[1210] = ItemType.NameTag; + mappings[1211] = ItemType.CommandBlockMinecart; + mappings[1212] = ItemType.Mutton; + mappings[1213] = ItemType.CookedMutton; + mappings[1214] = ItemType.WhiteBanner; + mappings[1215] = ItemType.OrangeBanner; + mappings[1216] = ItemType.MagentaBanner; + mappings[1217] = ItemType.LightBlueBanner; + mappings[1218] = ItemType.YellowBanner; + mappings[1219] = ItemType.LimeBanner; + mappings[1220] = ItemType.PinkBanner; + mappings[1221] = ItemType.GrayBanner; + mappings[1222] = ItemType.LightGrayBanner; + mappings[1223] = ItemType.CyanBanner; + mappings[1224] = ItemType.PurpleBanner; + mappings[1225] = ItemType.BlueBanner; + mappings[1226] = ItemType.BrownBanner; + mappings[1227] = ItemType.GreenBanner; + mappings[1228] = ItemType.RedBanner; + mappings[1229] = ItemType.BlackBanner; + mappings[1230] = ItemType.EndCrystal; + mappings[1231] = ItemType.ChorusFruit; + mappings[1232] = ItemType.PoppedChorusFruit; + mappings[1233] = ItemType.TorchflowerSeeds; + mappings[1234] = ItemType.PitcherPod; + mappings[1235] = ItemType.Beetroot; + mappings[1236] = ItemType.BeetrootSeeds; + mappings[1237] = ItemType.BeetrootSoup; + mappings[1238] = ItemType.DragonBreath; + mappings[1239] = ItemType.SplashPotion; + mappings[1240] = ItemType.SpectralArrow; + mappings[1241] = ItemType.TippedArrow; + mappings[1242] = ItemType.LingeringPotion; + mappings[1243] = ItemType.Shield; + mappings[1244] = ItemType.TotemOfUndying; + mappings[1245] = ItemType.ShulkerShell; + mappings[1246] = ItemType.IronNugget; + mappings[1247] = ItemType.KnowledgeBook; + mappings[1248] = ItemType.DebugStick; + mappings[1249] = ItemType.MusicDisc13; + mappings[1250] = ItemType.MusicDiscCat; + mappings[1251] = ItemType.MusicDiscBlocks; + mappings[1252] = ItemType.MusicDiscChirp; + mappings[1253] = ItemType.MusicDiscCreator; + mappings[1254] = ItemType.MusicDiscCreatorMusicBox; + mappings[1255] = ItemType.MusicDiscFar; + mappings[1256] = ItemType.MusicDiscMall; + mappings[1257] = ItemType.MusicDiscMellohi; + mappings[1258] = ItemType.MusicDiscStal; + mappings[1259] = ItemType.MusicDiscStrad; + mappings[1260] = ItemType.MusicDiscWard; + mappings[1261] = ItemType.MusicDisc11; + mappings[1262] = ItemType.MusicDiscWait; + mappings[1263] = ItemType.MusicDiscOtherside; + mappings[1264] = ItemType.MusicDiscRelic; + mappings[1265] = ItemType.MusicDisc5; + mappings[1266] = ItemType.MusicDiscPigstep; + mappings[1267] = ItemType.MusicDiscPrecipice; + mappings[1268] = ItemType.MusicDiscTears; + mappings[1269] = ItemType.DiscFragment5; + mappings[1270] = ItemType.Trident; + mappings[1271] = ItemType.NautilusShell; + mappings[1272] = ItemType.HeartOfTheSea; + mappings[1273] = ItemType.Crossbow; + mappings[1274] = ItemType.SuspiciousStew; + mappings[1275] = ItemType.Loom; + mappings[1276] = ItemType.FlowerBannerPattern; + mappings[1277] = ItemType.CreeperBannerPattern; + mappings[1278] = ItemType.SkullBannerPattern; + mappings[1279] = ItemType.MojangBannerPattern; + mappings[1280] = ItemType.GlobeBannerPattern; + mappings[1281] = ItemType.PiglinBannerPattern; + mappings[1282] = ItemType.FlowBannerPattern; + mappings[1283] = ItemType.GusterBannerPattern; + mappings[1284] = ItemType.FieldMasonedBannerPattern; + mappings[1285] = ItemType.BordureIndentedBannerPattern; + mappings[1286] = ItemType.GoatHorn; + mappings[1287] = ItemType.Composter; + mappings[1288] = ItemType.Barrel; + mappings[1289] = ItemType.Smoker; + mappings[1290] = ItemType.BlastFurnace; + mappings[1291] = ItemType.CartographyTable; + mappings[1292] = ItemType.FletchingTable; + mappings[1293] = ItemType.Grindstone; + mappings[1294] = ItemType.SmithingTable; + mappings[1295] = ItemType.Stonecutter; + mappings[1296] = ItemType.Bell; + mappings[1297] = ItemType.Lantern; + mappings[1298] = ItemType.SoulLantern; + mappings[1299] = ItemType.SweetBerries; + mappings[1300] = ItemType.GlowBerries; + mappings[1301] = ItemType.Campfire; + mappings[1302] = ItemType.SoulCampfire; + mappings[1303] = ItemType.Shroomlight; + mappings[1304] = ItemType.Honeycomb; + mappings[1305] = ItemType.BeeNest; + mappings[1306] = ItemType.Beehive; + mappings[1307] = ItemType.HoneyBottle; + mappings[1308] = ItemType.HoneycombBlock; + mappings[1309] = ItemType.Lodestone; + mappings[1310] = ItemType.CryingObsidian; + mappings[1311] = ItemType.Blackstone; + mappings[1312] = ItemType.BlackstoneSlab; + mappings[1313] = ItemType.BlackstoneStairs; + mappings[1314] = ItemType.GildedBlackstone; + mappings[1315] = ItemType.PolishedBlackstone; + mappings[1316] = ItemType.PolishedBlackstoneSlab; + mappings[1317] = ItemType.PolishedBlackstoneStairs; + mappings[1318] = ItemType.ChiseledPolishedBlackstone; + mappings[1319] = ItemType.PolishedBlackstoneBricks; + mappings[1320] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1321] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1322] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1323] = ItemType.RespawnAnchor; + mappings[1324] = ItemType.Candle; + mappings[1325] = ItemType.WhiteCandle; + mappings[1326] = ItemType.OrangeCandle; + mappings[1327] = ItemType.MagentaCandle; + mappings[1328] = ItemType.LightBlueCandle; + mappings[1329] = ItemType.YellowCandle; + mappings[1330] = ItemType.LimeCandle; + mappings[1331] = ItemType.PinkCandle; + mappings[1332] = ItemType.GrayCandle; + mappings[1333] = ItemType.LightGrayCandle; + mappings[1334] = ItemType.CyanCandle; + mappings[1335] = ItemType.PurpleCandle; + mappings[1336] = ItemType.BlueCandle; + mappings[1337] = ItemType.BrownCandle; + mappings[1338] = ItemType.GreenCandle; + mappings[1339] = ItemType.RedCandle; + mappings[1340] = ItemType.BlackCandle; + mappings[1341] = ItemType.SmallAmethystBud; + mappings[1342] = ItemType.MediumAmethystBud; + mappings[1343] = ItemType.LargeAmethystBud; + mappings[1344] = ItemType.AmethystCluster; + mappings[1345] = ItemType.PointedDripstone; + mappings[1346] = ItemType.OchreFroglight; + mappings[1347] = ItemType.VerdantFroglight; + mappings[1348] = ItemType.PearlescentFroglight; + mappings[1349] = ItemType.Frogspawn; + mappings[1350] = ItemType.EchoShard; + mappings[1351] = ItemType.Brush; + mappings[1352] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1353] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1354] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1355] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1356] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1357] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1358] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1359] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1360] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1361] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1362] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1363] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1364] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1365] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1366] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1367] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1368] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1369] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1370] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1371] = ItemType.AnglerPotterySherd; + mappings[1372] = ItemType.ArcherPotterySherd; + mappings[1373] = ItemType.ArmsUpPotterySherd; + mappings[1374] = ItemType.BladePotterySherd; + mappings[1375] = ItemType.BrewerPotterySherd; + mappings[1376] = ItemType.BurnPotterySherd; + mappings[1377] = ItemType.DangerPotterySherd; + mappings[1378] = ItemType.ExplorerPotterySherd; + mappings[1379] = ItemType.FlowPotterySherd; + mappings[1380] = ItemType.FriendPotterySherd; + mappings[1381] = ItemType.GusterPotterySherd; + mappings[1382] = ItemType.HeartPotterySherd; + mappings[1383] = ItemType.HeartbreakPotterySherd; + mappings[1384] = ItemType.HowlPotterySherd; + mappings[1385] = ItemType.MinerPotterySherd; + mappings[1386] = ItemType.MournerPotterySherd; + mappings[1387] = ItemType.PlentyPotterySherd; + mappings[1388] = ItemType.PrizePotterySherd; + mappings[1389] = ItemType.ScrapePotterySherd; + mappings[1390] = ItemType.SheafPotterySherd; + mappings[1391] = ItemType.ShelterPotterySherd; + mappings[1392] = ItemType.SkullPotterySherd; + mappings[1393] = ItemType.SnortPotterySherd; + mappings[1394] = ItemType.CopperGrate; + mappings[1395] = ItemType.ExposedCopperGrate; + mappings[1396] = ItemType.WeatheredCopperGrate; + mappings[1397] = ItemType.OxidizedCopperGrate; + mappings[1398] = ItemType.WaxedCopperGrate; + mappings[1399] = ItemType.WaxedExposedCopperGrate; + mappings[1400] = ItemType.WaxedWeatheredCopperGrate; + mappings[1401] = ItemType.WaxedOxidizedCopperGrate; + mappings[1402] = ItemType.CopperBulb; + mappings[1403] = ItemType.ExposedCopperBulb; + mappings[1404] = ItemType.WeatheredCopperBulb; + mappings[1405] = ItemType.OxidizedCopperBulb; + mappings[1406] = ItemType.WaxedCopperBulb; + mappings[1407] = ItemType.WaxedExposedCopperBulb; + mappings[1408] = ItemType.WaxedWeatheredCopperBulb; + mappings[1409] = ItemType.WaxedOxidizedCopperBulb; + mappings[1410] = ItemType.TrialSpawner; + mappings[1411] = ItemType.TrialKey; + mappings[1412] = ItemType.OminousTrialKey; + mappings[1413] = ItemType.Vault; + mappings[1414] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1217.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1217.cs new file mode 100644 index 00000000..a7026b92 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1217.cs @@ -0,0 +1,1434 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1217 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1217() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.DryShortGrass; + mappings[210] = ItemType.DryTallGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.OpenEyeblossom; + mappings[231] = ItemType.ClosedEyeblossom; + mappings[232] = ItemType.Poppy; + mappings[233] = ItemType.BlueOrchid; + mappings[234] = ItemType.Allium; + mappings[235] = ItemType.AzureBluet; + mappings[236] = ItemType.RedTulip; + mappings[237] = ItemType.OrangeTulip; + mappings[238] = ItemType.WhiteTulip; + mappings[239] = ItemType.PinkTulip; + mappings[240] = ItemType.OxeyeDaisy; + mappings[241] = ItemType.Cornflower; + mappings[242] = ItemType.LilyOfTheValley; + mappings[243] = ItemType.WitherRose; + mappings[244] = ItemType.Torchflower; + mappings[245] = ItemType.PitcherPlant; + mappings[246] = ItemType.SporeBlossom; + mappings[247] = ItemType.BrownMushroom; + mappings[248] = ItemType.RedMushroom; + mappings[249] = ItemType.CrimsonFungus; + mappings[250] = ItemType.WarpedFungus; + mappings[251] = ItemType.CrimsonRoots; + mappings[252] = ItemType.WarpedRoots; + mappings[253] = ItemType.NetherSprouts; + mappings[254] = ItemType.WeepingVines; + mappings[255] = ItemType.TwistingVines; + mappings[256] = ItemType.SugarCane; + mappings[257] = ItemType.Kelp; + mappings[258] = ItemType.PinkPetals; + mappings[259] = ItemType.Wildflowers; + mappings[260] = ItemType.LeafLitter; + mappings[261] = ItemType.MossCarpet; + mappings[262] = ItemType.MossBlock; + mappings[263] = ItemType.PaleMossCarpet; + mappings[264] = ItemType.PaleHangingMoss; + mappings[265] = ItemType.PaleMossBlock; + mappings[266] = ItemType.HangingRoots; + mappings[267] = ItemType.BigDripleaf; + mappings[268] = ItemType.SmallDripleaf; + mappings[269] = ItemType.Bamboo; + mappings[270] = ItemType.OakSlab; + mappings[271] = ItemType.SpruceSlab; + mappings[272] = ItemType.BirchSlab; + mappings[273] = ItemType.JungleSlab; + mappings[274] = ItemType.AcaciaSlab; + mappings[275] = ItemType.CherrySlab; + mappings[276] = ItemType.DarkOakSlab; + mappings[277] = ItemType.PaleOakSlab; + mappings[278] = ItemType.MangroveSlab; + mappings[279] = ItemType.BambooSlab; + mappings[280] = ItemType.BambooMosaicSlab; + mappings[281] = ItemType.CrimsonSlab; + mappings[282] = ItemType.WarpedSlab; + mappings[283] = ItemType.StoneSlab; + mappings[284] = ItemType.SmoothStoneSlab; + mappings[285] = ItemType.SandstoneSlab; + mappings[286] = ItemType.CutSandstoneSlab; + mappings[287] = ItemType.PetrifiedOakSlab; + mappings[288] = ItemType.CobblestoneSlab; + mappings[289] = ItemType.BrickSlab; + mappings[290] = ItemType.StoneBrickSlab; + mappings[291] = ItemType.MudBrickSlab; + mappings[292] = ItemType.NetherBrickSlab; + mappings[293] = ItemType.QuartzSlab; + mappings[294] = ItemType.RedSandstoneSlab; + mappings[295] = ItemType.CutRedSandstoneSlab; + mappings[296] = ItemType.PurpurSlab; + mappings[297] = ItemType.PrismarineSlab; + mappings[298] = ItemType.PrismarineBrickSlab; + mappings[299] = ItemType.DarkPrismarineSlab; + mappings[300] = ItemType.SmoothQuartz; + mappings[301] = ItemType.SmoothRedSandstone; + mappings[302] = ItemType.SmoothSandstone; + mappings[303] = ItemType.SmoothStone; + mappings[304] = ItemType.Bricks; + mappings[305] = ItemType.Bookshelf; + mappings[306] = ItemType.ChiseledBookshelf; + mappings[307] = ItemType.DecoratedPot; + mappings[308] = ItemType.MossyCobblestone; + mappings[309] = ItemType.Obsidian; + mappings[310] = ItemType.Torch; + mappings[311] = ItemType.EndRod; + mappings[312] = ItemType.ChorusPlant; + mappings[313] = ItemType.ChorusFlower; + mappings[314] = ItemType.PurpurBlock; + mappings[315] = ItemType.PurpurPillar; + mappings[316] = ItemType.PurpurStairs; + mappings[317] = ItemType.Spawner; + mappings[318] = ItemType.CreakingHeart; + mappings[319] = ItemType.Chest; + mappings[320] = ItemType.CraftingTable; + mappings[321] = ItemType.Farmland; + mappings[322] = ItemType.Furnace; + mappings[323] = ItemType.Ladder; + mappings[324] = ItemType.CobblestoneStairs; + mappings[325] = ItemType.Snow; + mappings[326] = ItemType.Ice; + mappings[327] = ItemType.SnowBlock; + mappings[328] = ItemType.Cactus; + mappings[329] = ItemType.CactusFlower; + mappings[330] = ItemType.Clay; + mappings[331] = ItemType.Jukebox; + mappings[332] = ItemType.OakFence; + mappings[333] = ItemType.SpruceFence; + mappings[334] = ItemType.BirchFence; + mappings[335] = ItemType.JungleFence; + mappings[336] = ItemType.AcaciaFence; + mappings[337] = ItemType.CherryFence; + mappings[338] = ItemType.DarkOakFence; + mappings[339] = ItemType.PaleOakFence; + mappings[340] = ItemType.MangroveFence; + mappings[341] = ItemType.BambooFence; + mappings[342] = ItemType.CrimsonFence; + mappings[343] = ItemType.WarpedFence; + mappings[344] = ItemType.Pumpkin; + mappings[345] = ItemType.CarvedPumpkin; + mappings[346] = ItemType.JackOLantern; + mappings[347] = ItemType.Netherrack; + mappings[348] = ItemType.SoulSand; + mappings[349] = ItemType.SoulSoil; + mappings[350] = ItemType.Basalt; + mappings[351] = ItemType.PolishedBasalt; + mappings[352] = ItemType.SmoothBasalt; + mappings[353] = ItemType.SoulTorch; + mappings[354] = ItemType.Glowstone; + mappings[355] = ItemType.InfestedStone; + mappings[356] = ItemType.InfestedCobblestone; + mappings[357] = ItemType.InfestedStoneBricks; + mappings[358] = ItemType.InfestedMossyStoneBricks; + mappings[359] = ItemType.InfestedCrackedStoneBricks; + mappings[360] = ItemType.InfestedChiseledStoneBricks; + mappings[361] = ItemType.InfestedDeepslate; + mappings[362] = ItemType.StoneBricks; + mappings[363] = ItemType.MossyStoneBricks; + mappings[364] = ItemType.CrackedStoneBricks; + mappings[365] = ItemType.ChiseledStoneBricks; + mappings[366] = ItemType.PackedMud; + mappings[367] = ItemType.MudBricks; + mappings[368] = ItemType.DeepslateBricks; + mappings[369] = ItemType.CrackedDeepslateBricks; + mappings[370] = ItemType.DeepslateTiles; + mappings[371] = ItemType.CrackedDeepslateTiles; + mappings[372] = ItemType.ChiseledDeepslate; + mappings[373] = ItemType.ReinforcedDeepslate; + mappings[374] = ItemType.BrownMushroomBlock; + mappings[375] = ItemType.RedMushroomBlock; + mappings[376] = ItemType.MushroomStem; + mappings[377] = ItemType.IronBars; + mappings[378] = ItemType.Chain; + mappings[379] = ItemType.GlassPane; + mappings[380] = ItemType.Melon; + mappings[381] = ItemType.Vine; + mappings[382] = ItemType.GlowLichen; + mappings[383] = ItemType.ResinClump; + mappings[384] = ItemType.ResinBlock; + mappings[385] = ItemType.ResinBricks; + mappings[386] = ItemType.ResinBrickStairs; + mappings[387] = ItemType.ResinBrickSlab; + mappings[388] = ItemType.ResinBrickWall; + mappings[389] = ItemType.ChiseledResinBricks; + mappings[390] = ItemType.BrickStairs; + mappings[391] = ItemType.StoneBrickStairs; + mappings[392] = ItemType.MudBrickStairs; + mappings[393] = ItemType.Mycelium; + mappings[394] = ItemType.LilyPad; + mappings[395] = ItemType.NetherBricks; + mappings[396] = ItemType.CrackedNetherBricks; + mappings[397] = ItemType.ChiseledNetherBricks; + mappings[398] = ItemType.NetherBrickFence; + mappings[399] = ItemType.NetherBrickStairs; + mappings[400] = ItemType.Sculk; + mappings[401] = ItemType.SculkVein; + mappings[402] = ItemType.SculkCatalyst; + mappings[403] = ItemType.SculkShrieker; + mappings[404] = ItemType.EnchantingTable; + mappings[405] = ItemType.EndPortalFrame; + mappings[406] = ItemType.EndStone; + mappings[407] = ItemType.EndStoneBricks; + mappings[408] = ItemType.DragonEgg; + mappings[409] = ItemType.SandstoneStairs; + mappings[410] = ItemType.EnderChest; + mappings[411] = ItemType.EmeraldBlock; + mappings[412] = ItemType.OakStairs; + mappings[413] = ItemType.SpruceStairs; + mappings[414] = ItemType.BirchStairs; + mappings[415] = ItemType.JungleStairs; + mappings[416] = ItemType.AcaciaStairs; + mappings[417] = ItemType.CherryStairs; + mappings[418] = ItemType.DarkOakStairs; + mappings[419] = ItemType.PaleOakStairs; + mappings[420] = ItemType.MangroveStairs; + mappings[421] = ItemType.BambooStairs; + mappings[422] = ItemType.BambooMosaicStairs; + mappings[423] = ItemType.CrimsonStairs; + mappings[424] = ItemType.WarpedStairs; + mappings[425] = ItemType.CommandBlock; + mappings[426] = ItemType.Beacon; + mappings[427] = ItemType.CobblestoneWall; + mappings[428] = ItemType.MossyCobblestoneWall; + mappings[429] = ItemType.BrickWall; + mappings[430] = ItemType.PrismarineWall; + mappings[431] = ItemType.RedSandstoneWall; + mappings[432] = ItemType.MossyStoneBrickWall; + mappings[433] = ItemType.GraniteWall; + mappings[434] = ItemType.StoneBrickWall; + mappings[435] = ItemType.MudBrickWall; + mappings[436] = ItemType.NetherBrickWall; + mappings[437] = ItemType.AndesiteWall; + mappings[438] = ItemType.RedNetherBrickWall; + mappings[439] = ItemType.SandstoneWall; + mappings[440] = ItemType.EndStoneBrickWall; + mappings[441] = ItemType.DioriteWall; + mappings[442] = ItemType.BlackstoneWall; + mappings[443] = ItemType.PolishedBlackstoneWall; + mappings[444] = ItemType.PolishedBlackstoneBrickWall; + mappings[445] = ItemType.CobbledDeepslateWall; + mappings[446] = ItemType.PolishedDeepslateWall; + mappings[447] = ItemType.DeepslateBrickWall; + mappings[448] = ItemType.DeepslateTileWall; + mappings[449] = ItemType.Anvil; + mappings[450] = ItemType.ChippedAnvil; + mappings[451] = ItemType.DamagedAnvil; + mappings[452] = ItemType.ChiseledQuartzBlock; + mappings[453] = ItemType.QuartzBlock; + mappings[454] = ItemType.QuartzBricks; + mappings[455] = ItemType.QuartzPillar; + mappings[456] = ItemType.QuartzStairs; + mappings[457] = ItemType.WhiteTerracotta; + mappings[458] = ItemType.OrangeTerracotta; + mappings[459] = ItemType.MagentaTerracotta; + mappings[460] = ItemType.LightBlueTerracotta; + mappings[461] = ItemType.YellowTerracotta; + mappings[462] = ItemType.LimeTerracotta; + mappings[463] = ItemType.PinkTerracotta; + mappings[464] = ItemType.GrayTerracotta; + mappings[465] = ItemType.LightGrayTerracotta; + mappings[466] = ItemType.CyanTerracotta; + mappings[467] = ItemType.PurpleTerracotta; + mappings[468] = ItemType.BlueTerracotta; + mappings[469] = ItemType.BrownTerracotta; + mappings[470] = ItemType.GreenTerracotta; + mappings[471] = ItemType.RedTerracotta; + mappings[472] = ItemType.BlackTerracotta; + mappings[473] = ItemType.Barrier; + mappings[474] = ItemType.Light; + mappings[475] = ItemType.HayBlock; + mappings[476] = ItemType.WhiteCarpet; + mappings[477] = ItemType.OrangeCarpet; + mappings[478] = ItemType.MagentaCarpet; + mappings[479] = ItemType.LightBlueCarpet; + mappings[480] = ItemType.YellowCarpet; + mappings[481] = ItemType.LimeCarpet; + mappings[482] = ItemType.PinkCarpet; + mappings[483] = ItemType.GrayCarpet; + mappings[484] = ItemType.LightGrayCarpet; + mappings[485] = ItemType.CyanCarpet; + mappings[486] = ItemType.PurpleCarpet; + mappings[487] = ItemType.BlueCarpet; + mappings[488] = ItemType.BrownCarpet; + mappings[489] = ItemType.GreenCarpet; + mappings[490] = ItemType.RedCarpet; + mappings[491] = ItemType.BlackCarpet; + mappings[492] = ItemType.Terracotta; + mappings[493] = ItemType.PackedIce; + mappings[494] = ItemType.DirtPath; + mappings[495] = ItemType.Sunflower; + mappings[496] = ItemType.Lilac; + mappings[497] = ItemType.RoseBush; + mappings[498] = ItemType.Peony; + mappings[499] = ItemType.TallGrass; + mappings[500] = ItemType.LargeFern; + mappings[501] = ItemType.WhiteStainedGlass; + mappings[502] = ItemType.OrangeStainedGlass; + mappings[503] = ItemType.MagentaStainedGlass; + mappings[504] = ItemType.LightBlueStainedGlass; + mappings[505] = ItemType.YellowStainedGlass; + mappings[506] = ItemType.LimeStainedGlass; + mappings[507] = ItemType.PinkStainedGlass; + mappings[508] = ItemType.GrayStainedGlass; + mappings[509] = ItemType.LightGrayStainedGlass; + mappings[510] = ItemType.CyanStainedGlass; + mappings[511] = ItemType.PurpleStainedGlass; + mappings[512] = ItemType.BlueStainedGlass; + mappings[513] = ItemType.BrownStainedGlass; + mappings[514] = ItemType.GreenStainedGlass; + mappings[515] = ItemType.RedStainedGlass; + mappings[516] = ItemType.BlackStainedGlass; + mappings[517] = ItemType.WhiteStainedGlassPane; + mappings[518] = ItemType.OrangeStainedGlassPane; + mappings[519] = ItemType.MagentaStainedGlassPane; + mappings[520] = ItemType.LightBlueStainedGlassPane; + mappings[521] = ItemType.YellowStainedGlassPane; + mappings[522] = ItemType.LimeStainedGlassPane; + mappings[523] = ItemType.PinkStainedGlassPane; + mappings[524] = ItemType.GrayStainedGlassPane; + mappings[525] = ItemType.LightGrayStainedGlassPane; + mappings[526] = ItemType.CyanStainedGlassPane; + mappings[527] = ItemType.PurpleStainedGlassPane; + mappings[528] = ItemType.BlueStainedGlassPane; + mappings[529] = ItemType.BrownStainedGlassPane; + mappings[530] = ItemType.GreenStainedGlassPane; + mappings[531] = ItemType.RedStainedGlassPane; + mappings[532] = ItemType.BlackStainedGlassPane; + mappings[533] = ItemType.Prismarine; + mappings[534] = ItemType.PrismarineBricks; + mappings[535] = ItemType.DarkPrismarine; + mappings[536] = ItemType.PrismarineStairs; + mappings[537] = ItemType.PrismarineBrickStairs; + mappings[538] = ItemType.DarkPrismarineStairs; + mappings[539] = ItemType.SeaLantern; + mappings[540] = ItemType.RedSandstone; + mappings[541] = ItemType.ChiseledRedSandstone; + mappings[542] = ItemType.CutRedSandstone; + mappings[543] = ItemType.RedSandstoneStairs; + mappings[544] = ItemType.RepeatingCommandBlock; + mappings[545] = ItemType.ChainCommandBlock; + mappings[546] = ItemType.MagmaBlock; + mappings[547] = ItemType.NetherWartBlock; + mappings[548] = ItemType.WarpedWartBlock; + mappings[549] = ItemType.RedNetherBricks; + mappings[550] = ItemType.BoneBlock; + mappings[551] = ItemType.StructureVoid; + mappings[552] = ItemType.ShulkerBox; + mappings[553] = ItemType.WhiteShulkerBox; + mappings[554] = ItemType.OrangeShulkerBox; + mappings[555] = ItemType.MagentaShulkerBox; + mappings[556] = ItemType.LightBlueShulkerBox; + mappings[557] = ItemType.YellowShulkerBox; + mappings[558] = ItemType.LimeShulkerBox; + mappings[559] = ItemType.PinkShulkerBox; + mappings[560] = ItemType.GrayShulkerBox; + mappings[561] = ItemType.LightGrayShulkerBox; + mappings[562] = ItemType.CyanShulkerBox; + mappings[563] = ItemType.PurpleShulkerBox; + mappings[564] = ItemType.BlueShulkerBox; + mappings[565] = ItemType.BrownShulkerBox; + mappings[566] = ItemType.GreenShulkerBox; + mappings[567] = ItemType.RedShulkerBox; + mappings[568] = ItemType.BlackShulkerBox; + mappings[569] = ItemType.WhiteGlazedTerracotta; + mappings[570] = ItemType.OrangeGlazedTerracotta; + mappings[571] = ItemType.MagentaGlazedTerracotta; + mappings[572] = ItemType.LightBlueGlazedTerracotta; + mappings[573] = ItemType.YellowGlazedTerracotta; + mappings[574] = ItemType.LimeGlazedTerracotta; + mappings[575] = ItemType.PinkGlazedTerracotta; + mappings[576] = ItemType.GrayGlazedTerracotta; + mappings[577] = ItemType.LightGrayGlazedTerracotta; + mappings[578] = ItemType.CyanGlazedTerracotta; + mappings[579] = ItemType.PurpleGlazedTerracotta; + mappings[580] = ItemType.BlueGlazedTerracotta; + mappings[581] = ItemType.BrownGlazedTerracotta; + mappings[582] = ItemType.GreenGlazedTerracotta; + mappings[583] = ItemType.RedGlazedTerracotta; + mappings[584] = ItemType.BlackGlazedTerracotta; + mappings[585] = ItemType.WhiteConcrete; + mappings[586] = ItemType.OrangeConcrete; + mappings[587] = ItemType.MagentaConcrete; + mappings[588] = ItemType.LightBlueConcrete; + mappings[589] = ItemType.YellowConcrete; + mappings[590] = ItemType.LimeConcrete; + mappings[591] = ItemType.PinkConcrete; + mappings[592] = ItemType.GrayConcrete; + mappings[593] = ItemType.LightGrayConcrete; + mappings[594] = ItemType.CyanConcrete; + mappings[595] = ItemType.PurpleConcrete; + mappings[596] = ItemType.BlueConcrete; + mappings[597] = ItemType.BrownConcrete; + mappings[598] = ItemType.GreenConcrete; + mappings[599] = ItemType.RedConcrete; + mappings[600] = ItemType.BlackConcrete; + mappings[601] = ItemType.WhiteConcretePowder; + mappings[602] = ItemType.OrangeConcretePowder; + mappings[603] = ItemType.MagentaConcretePowder; + mappings[604] = ItemType.LightBlueConcretePowder; + mappings[605] = ItemType.YellowConcretePowder; + mappings[606] = ItemType.LimeConcretePowder; + mappings[607] = ItemType.PinkConcretePowder; + mappings[608] = ItemType.GrayConcretePowder; + mappings[609] = ItemType.LightGrayConcretePowder; + mappings[610] = ItemType.CyanConcretePowder; + mappings[611] = ItemType.PurpleConcretePowder; + mappings[612] = ItemType.BlueConcretePowder; + mappings[613] = ItemType.BrownConcretePowder; + mappings[614] = ItemType.GreenConcretePowder; + mappings[615] = ItemType.RedConcretePowder; + mappings[616] = ItemType.BlackConcretePowder; + mappings[617] = ItemType.TurtleEgg; + mappings[618] = ItemType.SnifferEgg; + mappings[619] = ItemType.DriedGhast; + mappings[620] = ItemType.DeadTubeCoralBlock; + mappings[621] = ItemType.DeadBrainCoralBlock; + mappings[622] = ItemType.DeadBubbleCoralBlock; + mappings[623] = ItemType.DeadFireCoralBlock; + mappings[624] = ItemType.DeadHornCoralBlock; + mappings[625] = ItemType.TubeCoralBlock; + mappings[626] = ItemType.BrainCoralBlock; + mappings[627] = ItemType.BubbleCoralBlock; + mappings[628] = ItemType.FireCoralBlock; + mappings[629] = ItemType.HornCoralBlock; + mappings[630] = ItemType.TubeCoral; + mappings[631] = ItemType.BrainCoral; + mappings[632] = ItemType.BubbleCoral; + mappings[633] = ItemType.FireCoral; + mappings[634] = ItemType.HornCoral; + mappings[635] = ItemType.DeadBrainCoral; + mappings[636] = ItemType.DeadBubbleCoral; + mappings[637] = ItemType.DeadFireCoral; + mappings[638] = ItemType.DeadHornCoral; + mappings[639] = ItemType.DeadTubeCoral; + mappings[640] = ItemType.TubeCoralFan; + mappings[641] = ItemType.BrainCoralFan; + mappings[642] = ItemType.BubbleCoralFan; + mappings[643] = ItemType.FireCoralFan; + mappings[644] = ItemType.HornCoralFan; + mappings[645] = ItemType.DeadTubeCoralFan; + mappings[646] = ItemType.DeadBrainCoralFan; + mappings[647] = ItemType.DeadBubbleCoralFan; + mappings[648] = ItemType.DeadFireCoralFan; + mappings[649] = ItemType.DeadHornCoralFan; + mappings[650] = ItemType.BlueIce; + mappings[651] = ItemType.Conduit; + mappings[652] = ItemType.PolishedGraniteStairs; + mappings[653] = ItemType.SmoothRedSandstoneStairs; + mappings[654] = ItemType.MossyStoneBrickStairs; + mappings[655] = ItemType.PolishedDioriteStairs; + mappings[656] = ItemType.MossyCobblestoneStairs; + mappings[657] = ItemType.EndStoneBrickStairs; + mappings[658] = ItemType.StoneStairs; + mappings[659] = ItemType.SmoothSandstoneStairs; + mappings[660] = ItemType.SmoothQuartzStairs; + mappings[661] = ItemType.GraniteStairs; + mappings[662] = ItemType.AndesiteStairs; + mappings[663] = ItemType.RedNetherBrickStairs; + mappings[664] = ItemType.PolishedAndesiteStairs; + mappings[665] = ItemType.DioriteStairs; + mappings[666] = ItemType.CobbledDeepslateStairs; + mappings[667] = ItemType.PolishedDeepslateStairs; + mappings[668] = ItemType.DeepslateBrickStairs; + mappings[669] = ItemType.DeepslateTileStairs; + mappings[670] = ItemType.PolishedGraniteSlab; + mappings[671] = ItemType.SmoothRedSandstoneSlab; + mappings[672] = ItemType.MossyStoneBrickSlab; + mappings[673] = ItemType.PolishedDioriteSlab; + mappings[674] = ItemType.MossyCobblestoneSlab; + mappings[675] = ItemType.EndStoneBrickSlab; + mappings[676] = ItemType.SmoothSandstoneSlab; + mappings[677] = ItemType.SmoothQuartzSlab; + mappings[678] = ItemType.GraniteSlab; + mappings[679] = ItemType.AndesiteSlab; + mappings[680] = ItemType.RedNetherBrickSlab; + mappings[681] = ItemType.PolishedAndesiteSlab; + mappings[682] = ItemType.DioriteSlab; + mappings[683] = ItemType.CobbledDeepslateSlab; + mappings[684] = ItemType.PolishedDeepslateSlab; + mappings[685] = ItemType.DeepslateBrickSlab; + mappings[686] = ItemType.DeepslateTileSlab; + mappings[687] = ItemType.Scaffolding; + mappings[688] = ItemType.Redstone; + mappings[689] = ItemType.RedstoneTorch; + mappings[690] = ItemType.RedstoneBlock; + mappings[691] = ItemType.Repeater; + mappings[692] = ItemType.Comparator; + mappings[693] = ItemType.Piston; + mappings[694] = ItemType.StickyPiston; + mappings[695] = ItemType.SlimeBlock; + mappings[696] = ItemType.HoneyBlock; + mappings[697] = ItemType.Observer; + mappings[698] = ItemType.Hopper; + mappings[699] = ItemType.Dispenser; + mappings[700] = ItemType.Dropper; + mappings[701] = ItemType.Lectern; + mappings[702] = ItemType.Target; + mappings[703] = ItemType.Lever; + mappings[704] = ItemType.LightningRod; + mappings[705] = ItemType.DaylightDetector; + mappings[706] = ItemType.SculkSensor; + mappings[707] = ItemType.CalibratedSculkSensor; + mappings[708] = ItemType.TripwireHook; + mappings[709] = ItemType.TrappedChest; + mappings[710] = ItemType.Tnt; + mappings[711] = ItemType.RedstoneLamp; + mappings[712] = ItemType.NoteBlock; + mappings[713] = ItemType.StoneButton; + mappings[714] = ItemType.PolishedBlackstoneButton; + mappings[715] = ItemType.OakButton; + mappings[716] = ItemType.SpruceButton; + mappings[717] = ItemType.BirchButton; + mappings[718] = ItemType.JungleButton; + mappings[719] = ItemType.AcaciaButton; + mappings[720] = ItemType.CherryButton; + mappings[721] = ItemType.DarkOakButton; + mappings[722] = ItemType.PaleOakButton; + mappings[723] = ItemType.MangroveButton; + mappings[724] = ItemType.BambooButton; + mappings[725] = ItemType.CrimsonButton; + mappings[726] = ItemType.WarpedButton; + mappings[727] = ItemType.StonePressurePlate; + mappings[728] = ItemType.PolishedBlackstonePressurePlate; + mappings[729] = ItemType.LightWeightedPressurePlate; + mappings[730] = ItemType.HeavyWeightedPressurePlate; + mappings[731] = ItemType.OakPressurePlate; + mappings[732] = ItemType.SprucePressurePlate; + mappings[733] = ItemType.BirchPressurePlate; + mappings[734] = ItemType.JunglePressurePlate; + mappings[735] = ItemType.AcaciaPressurePlate; + mappings[736] = ItemType.CherryPressurePlate; + mappings[737] = ItemType.DarkOakPressurePlate; + mappings[738] = ItemType.PaleOakPressurePlate; + mappings[739] = ItemType.MangrovePressurePlate; + mappings[740] = ItemType.BambooPressurePlate; + mappings[741] = ItemType.CrimsonPressurePlate; + mappings[742] = ItemType.WarpedPressurePlate; + mappings[743] = ItemType.IronDoor; + mappings[744] = ItemType.OakDoor; + mappings[745] = ItemType.SpruceDoor; + mappings[746] = ItemType.BirchDoor; + mappings[747] = ItemType.JungleDoor; + mappings[748] = ItemType.AcaciaDoor; + mappings[749] = ItemType.CherryDoor; + mappings[750] = ItemType.DarkOakDoor; + mappings[751] = ItemType.PaleOakDoor; + mappings[752] = ItemType.MangroveDoor; + mappings[753] = ItemType.BambooDoor; + mappings[754] = ItemType.CrimsonDoor; + mappings[755] = ItemType.WarpedDoor; + mappings[756] = ItemType.CopperDoor; + mappings[757] = ItemType.ExposedCopperDoor; + mappings[758] = ItemType.WeatheredCopperDoor; + mappings[759] = ItemType.OxidizedCopperDoor; + mappings[760] = ItemType.WaxedCopperDoor; + mappings[761] = ItemType.WaxedExposedCopperDoor; + mappings[762] = ItemType.WaxedWeatheredCopperDoor; + mappings[763] = ItemType.WaxedOxidizedCopperDoor; + mappings[764] = ItemType.IronTrapdoor; + mappings[765] = ItemType.OakTrapdoor; + mappings[766] = ItemType.SpruceTrapdoor; + mappings[767] = ItemType.BirchTrapdoor; + mappings[768] = ItemType.JungleTrapdoor; + mappings[769] = ItemType.AcaciaTrapdoor; + mappings[770] = ItemType.CherryTrapdoor; + mappings[771] = ItemType.DarkOakTrapdoor; + mappings[772] = ItemType.PaleOakTrapdoor; + mappings[773] = ItemType.MangroveTrapdoor; + mappings[774] = ItemType.BambooTrapdoor; + mappings[775] = ItemType.CrimsonTrapdoor; + mappings[776] = ItemType.WarpedTrapdoor; + mappings[777] = ItemType.CopperTrapdoor; + mappings[778] = ItemType.ExposedCopperTrapdoor; + mappings[779] = ItemType.WeatheredCopperTrapdoor; + mappings[780] = ItemType.OxidizedCopperTrapdoor; + mappings[781] = ItemType.WaxedCopperTrapdoor; + mappings[782] = ItemType.WaxedExposedCopperTrapdoor; + mappings[783] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[784] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[785] = ItemType.OakFenceGate; + mappings[786] = ItemType.SpruceFenceGate; + mappings[787] = ItemType.BirchFenceGate; + mappings[788] = ItemType.JungleFenceGate; + mappings[789] = ItemType.AcaciaFenceGate; + mappings[790] = ItemType.CherryFenceGate; + mappings[791] = ItemType.DarkOakFenceGate; + mappings[792] = ItemType.PaleOakFenceGate; + mappings[793] = ItemType.MangroveFenceGate; + mappings[794] = ItemType.BambooFenceGate; + mappings[795] = ItemType.CrimsonFenceGate; + mappings[796] = ItemType.WarpedFenceGate; + mappings[797] = ItemType.PoweredRail; + mappings[798] = ItemType.DetectorRail; + mappings[799] = ItemType.Rail; + mappings[800] = ItemType.ActivatorRail; + mappings[801] = ItemType.Saddle; + mappings[802] = ItemType.WhiteHarness; + mappings[803] = ItemType.OrangeHarness; + mappings[804] = ItemType.MagentaHarness; + mappings[805] = ItemType.LightBlueHarness; + mappings[806] = ItemType.YellowHarness; + mappings[807] = ItemType.LimeHarness; + mappings[808] = ItemType.PinkHarness; + mappings[809] = ItemType.GrayHarness; + mappings[810] = ItemType.LightGrayHarness; + mappings[811] = ItemType.CyanHarness; + mappings[812] = ItemType.PurpleHarness; + mappings[813] = ItemType.BlueHarness; + mappings[814] = ItemType.BrownHarness; + mappings[815] = ItemType.GreenHarness; + mappings[816] = ItemType.RedHarness; + mappings[817] = ItemType.BlackHarness; + mappings[818] = ItemType.Minecart; + mappings[819] = ItemType.ChestMinecart; + mappings[820] = ItemType.FurnaceMinecart; + mappings[821] = ItemType.TntMinecart; + mappings[822] = ItemType.HopperMinecart; + mappings[823] = ItemType.CarrotOnAStick; + mappings[824] = ItemType.WarpedFungusOnAStick; + mappings[825] = ItemType.PhantomMembrane; + mappings[826] = ItemType.Elytra; + mappings[827] = ItemType.OakBoat; + mappings[828] = ItemType.OakChestBoat; + mappings[829] = ItemType.SpruceBoat; + mappings[830] = ItemType.SpruceChestBoat; + mappings[831] = ItemType.BirchBoat; + mappings[832] = ItemType.BirchChestBoat; + mappings[833] = ItemType.JungleBoat; + mappings[834] = ItemType.JungleChestBoat; + mappings[835] = ItemType.AcaciaBoat; + mappings[836] = ItemType.AcaciaChestBoat; + mappings[837] = ItemType.CherryBoat; + mappings[838] = ItemType.CherryChestBoat; + mappings[839] = ItemType.DarkOakBoat; + mappings[840] = ItemType.DarkOakChestBoat; + mappings[841] = ItemType.PaleOakBoat; + mappings[842] = ItemType.PaleOakChestBoat; + mappings[843] = ItemType.MangroveBoat; + mappings[844] = ItemType.MangroveChestBoat; + mappings[845] = ItemType.BambooRaft; + mappings[846] = ItemType.BambooChestRaft; + mappings[847] = ItemType.StructureBlock; + mappings[848] = ItemType.Jigsaw; + mappings[849] = ItemType.TestBlock; + mappings[850] = ItemType.TestInstanceBlock; + mappings[851] = ItemType.TurtleHelmet; + mappings[852] = ItemType.TurtleScute; + mappings[853] = ItemType.ArmadilloScute; + mappings[854] = ItemType.WolfArmor; + mappings[855] = ItemType.FlintAndSteel; + mappings[856] = ItemType.Bowl; + mappings[857] = ItemType.Apple; + mappings[858] = ItemType.Bow; + mappings[859] = ItemType.Arrow; + mappings[860] = ItemType.Coal; + mappings[861] = ItemType.Charcoal; + mappings[862] = ItemType.Diamond; + mappings[863] = ItemType.Emerald; + mappings[864] = ItemType.LapisLazuli; + mappings[865] = ItemType.Quartz; + mappings[866] = ItemType.AmethystShard; + mappings[867] = ItemType.RawIron; + mappings[868] = ItemType.IronIngot; + mappings[869] = ItemType.RawCopper; + mappings[870] = ItemType.CopperIngot; + mappings[871] = ItemType.RawGold; + mappings[872] = ItemType.GoldIngot; + mappings[873] = ItemType.NetheriteIngot; + mappings[874] = ItemType.NetheriteScrap; + mappings[875] = ItemType.WoodenSword; + mappings[876] = ItemType.WoodenShovel; + mappings[877] = ItemType.WoodenPickaxe; + mappings[878] = ItemType.WoodenAxe; + mappings[879] = ItemType.WoodenHoe; + mappings[880] = ItemType.StoneSword; + mappings[881] = ItemType.StoneShovel; + mappings[882] = ItemType.StonePickaxe; + mappings[883] = ItemType.StoneAxe; + mappings[884] = ItemType.StoneHoe; + mappings[885] = ItemType.GoldenSword; + mappings[886] = ItemType.GoldenShovel; + mappings[887] = ItemType.GoldenPickaxe; + mappings[888] = ItemType.GoldenAxe; + mappings[889] = ItemType.GoldenHoe; + mappings[890] = ItemType.IronSword; + mappings[891] = ItemType.IronShovel; + mappings[892] = ItemType.IronPickaxe; + mappings[893] = ItemType.IronAxe; + mappings[894] = ItemType.IronHoe; + mappings[895] = ItemType.DiamondSword; + mappings[896] = ItemType.DiamondShovel; + mappings[897] = ItemType.DiamondPickaxe; + mappings[898] = ItemType.DiamondAxe; + mappings[899] = ItemType.DiamondHoe; + mappings[900] = ItemType.NetheriteSword; + mappings[901] = ItemType.NetheriteShovel; + mappings[902] = ItemType.NetheritePickaxe; + mappings[903] = ItemType.NetheriteAxe; + mappings[904] = ItemType.NetheriteHoe; + mappings[905] = ItemType.Stick; + mappings[906] = ItemType.MushroomStew; + mappings[907] = ItemType.String; + mappings[908] = ItemType.Feather; + mappings[909] = ItemType.Gunpowder; + mappings[910] = ItemType.WheatSeeds; + mappings[911] = ItemType.Wheat; + mappings[912] = ItemType.Bread; + mappings[913] = ItemType.LeatherHelmet; + mappings[914] = ItemType.LeatherChestplate; + mappings[915] = ItemType.LeatherLeggings; + mappings[916] = ItemType.LeatherBoots; + mappings[917] = ItemType.ChainmailHelmet; + mappings[918] = ItemType.ChainmailChestplate; + mappings[919] = ItemType.ChainmailLeggings; + mappings[920] = ItemType.ChainmailBoots; + mappings[921] = ItemType.IronHelmet; + mappings[922] = ItemType.IronChestplate; + mappings[923] = ItemType.IronLeggings; + mappings[924] = ItemType.IronBoots; + mappings[925] = ItemType.DiamondHelmet; + mappings[926] = ItemType.DiamondChestplate; + mappings[927] = ItemType.DiamondLeggings; + mappings[928] = ItemType.DiamondBoots; + mappings[929] = ItemType.GoldenHelmet; + mappings[930] = ItemType.GoldenChestplate; + mappings[931] = ItemType.GoldenLeggings; + mappings[932] = ItemType.GoldenBoots; + mappings[933] = ItemType.NetheriteHelmet; + mappings[934] = ItemType.NetheriteChestplate; + mappings[935] = ItemType.NetheriteLeggings; + mappings[936] = ItemType.NetheriteBoots; + mappings[937] = ItemType.Flint; + mappings[938] = ItemType.Porkchop; + mappings[939] = ItemType.CookedPorkchop; + mappings[940] = ItemType.Painting; + mappings[941] = ItemType.GoldenApple; + mappings[942] = ItemType.EnchantedGoldenApple; + mappings[943] = ItemType.OakSign; + mappings[944] = ItemType.SpruceSign; + mappings[945] = ItemType.BirchSign; + mappings[946] = ItemType.JungleSign; + mappings[947] = ItemType.AcaciaSign; + mappings[948] = ItemType.CherrySign; + mappings[949] = ItemType.DarkOakSign; + mappings[950] = ItemType.PaleOakSign; + mappings[951] = ItemType.MangroveSign; + mappings[952] = ItemType.BambooSign; + mappings[953] = ItemType.CrimsonSign; + mappings[954] = ItemType.WarpedSign; + mappings[955] = ItemType.OakHangingSign; + mappings[956] = ItemType.SpruceHangingSign; + mappings[957] = ItemType.BirchHangingSign; + mappings[958] = ItemType.JungleHangingSign; + mappings[959] = ItemType.AcaciaHangingSign; + mappings[960] = ItemType.CherryHangingSign; + mappings[961] = ItemType.DarkOakHangingSign; + mappings[962] = ItemType.PaleOakHangingSign; + mappings[963] = ItemType.MangroveHangingSign; + mappings[964] = ItemType.BambooHangingSign; + mappings[965] = ItemType.CrimsonHangingSign; + mappings[966] = ItemType.WarpedHangingSign; + mappings[967] = ItemType.Bucket; + mappings[968] = ItemType.WaterBucket; + mappings[969] = ItemType.LavaBucket; + mappings[970] = ItemType.PowderSnowBucket; + mappings[971] = ItemType.Snowball; + mappings[972] = ItemType.Leather; + mappings[973] = ItemType.MilkBucket; + mappings[974] = ItemType.PufferfishBucket; + mappings[975] = ItemType.SalmonBucket; + mappings[976] = ItemType.CodBucket; + mappings[977] = ItemType.TropicalFishBucket; + mappings[978] = ItemType.AxolotlBucket; + mappings[979] = ItemType.TadpoleBucket; + mappings[980] = ItemType.Brick; + mappings[981] = ItemType.ClayBall; + mappings[982] = ItemType.DriedKelpBlock; + mappings[983] = ItemType.Paper; + mappings[984] = ItemType.Book; + mappings[985] = ItemType.SlimeBall; + mappings[986] = ItemType.Egg; + mappings[987] = ItemType.BlueEgg; + mappings[988] = ItemType.BrownEgg; + mappings[989] = ItemType.Compass; + mappings[990] = ItemType.RecoveryCompass; + mappings[991] = ItemType.Bundle; + mappings[992] = ItemType.WhiteBundle; + mappings[993] = ItemType.OrangeBundle; + mappings[994] = ItemType.MagentaBundle; + mappings[995] = ItemType.LightBlueBundle; + mappings[996] = ItemType.YellowBundle; + mappings[997] = ItemType.LimeBundle; + mappings[998] = ItemType.PinkBundle; + mappings[999] = ItemType.GrayBundle; + mappings[1000] = ItemType.LightGrayBundle; + mappings[1001] = ItemType.CyanBundle; + mappings[1002] = ItemType.PurpleBundle; + mappings[1003] = ItemType.BlueBundle; + mappings[1004] = ItemType.BrownBundle; + mappings[1005] = ItemType.GreenBundle; + mappings[1006] = ItemType.RedBundle; + mappings[1007] = ItemType.BlackBundle; + mappings[1008] = ItemType.FishingRod; + mappings[1009] = ItemType.Clock; + mappings[1010] = ItemType.Spyglass; + mappings[1011] = ItemType.GlowstoneDust; + mappings[1012] = ItemType.Cod; + mappings[1013] = ItemType.Salmon; + mappings[1014] = ItemType.TropicalFish; + mappings[1015] = ItemType.Pufferfish; + mappings[1016] = ItemType.CookedCod; + mappings[1017] = ItemType.CookedSalmon; + mappings[1018] = ItemType.InkSac; + mappings[1019] = ItemType.GlowInkSac; + mappings[1020] = ItemType.CocoaBeans; + mappings[1021] = ItemType.WhiteDye; + mappings[1022] = ItemType.OrangeDye; + mappings[1023] = ItemType.MagentaDye; + mappings[1024] = ItemType.LightBlueDye; + mappings[1025] = ItemType.YellowDye; + mappings[1026] = ItemType.LimeDye; + mappings[1027] = ItemType.PinkDye; + mappings[1028] = ItemType.GrayDye; + mappings[1029] = ItemType.LightGrayDye; + mappings[1030] = ItemType.CyanDye; + mappings[1031] = ItemType.PurpleDye; + mappings[1032] = ItemType.BlueDye; + mappings[1033] = ItemType.BrownDye; + mappings[1034] = ItemType.GreenDye; + mappings[1035] = ItemType.RedDye; + mappings[1036] = ItemType.BlackDye; + mappings[1037] = ItemType.BoneMeal; + mappings[1038] = ItemType.Bone; + mappings[1039] = ItemType.Sugar; + mappings[1040] = ItemType.Cake; + mappings[1041] = ItemType.WhiteBed; + mappings[1042] = ItemType.OrangeBed; + mappings[1043] = ItemType.MagentaBed; + mappings[1044] = ItemType.LightBlueBed; + mappings[1045] = ItemType.YellowBed; + mappings[1046] = ItemType.LimeBed; + mappings[1047] = ItemType.PinkBed; + mappings[1048] = ItemType.GrayBed; + mappings[1049] = ItemType.LightGrayBed; + mappings[1050] = ItemType.CyanBed; + mappings[1051] = ItemType.PurpleBed; + mappings[1052] = ItemType.BlueBed; + mappings[1053] = ItemType.BrownBed; + mappings[1054] = ItemType.GreenBed; + mappings[1055] = ItemType.RedBed; + mappings[1056] = ItemType.BlackBed; + mappings[1057] = ItemType.Cookie; + mappings[1058] = ItemType.Crafter; + mappings[1059] = ItemType.FilledMap; + mappings[1060] = ItemType.Shears; + mappings[1061] = ItemType.MelonSlice; + mappings[1062] = ItemType.DriedKelp; + mappings[1063] = ItemType.PumpkinSeeds; + mappings[1064] = ItemType.MelonSeeds; + mappings[1065] = ItemType.Beef; + mappings[1066] = ItemType.CookedBeef; + mappings[1067] = ItemType.Chicken; + mappings[1068] = ItemType.CookedChicken; + mappings[1069] = ItemType.RottenFlesh; + mappings[1070] = ItemType.EnderPearl; + mappings[1071] = ItemType.BlazeRod; + mappings[1072] = ItemType.GhastTear; + mappings[1073] = ItemType.GoldNugget; + mappings[1074] = ItemType.NetherWart; + mappings[1075] = ItemType.GlassBottle; + mappings[1076] = ItemType.Potion; + mappings[1077] = ItemType.SpiderEye; + mappings[1078] = ItemType.FermentedSpiderEye; + mappings[1079] = ItemType.BlazePowder; + mappings[1080] = ItemType.MagmaCream; + mappings[1081] = ItemType.BrewingStand; + mappings[1082] = ItemType.Cauldron; + mappings[1083] = ItemType.EnderEye; + mappings[1084] = ItemType.GlisteringMelonSlice; + mappings[1085] = ItemType.ArmadilloSpawnEgg; + mappings[1086] = ItemType.AllaySpawnEgg; + mappings[1087] = ItemType.AxolotlSpawnEgg; + mappings[1088] = ItemType.BatSpawnEgg; + mappings[1089] = ItemType.BeeSpawnEgg; + mappings[1090] = ItemType.BlazeSpawnEgg; + mappings[1091] = ItemType.BoggedSpawnEgg; + mappings[1092] = ItemType.BreezeSpawnEgg; + mappings[1093] = ItemType.CatSpawnEgg; + mappings[1094] = ItemType.CamelSpawnEgg; + mappings[1095] = ItemType.CaveSpiderSpawnEgg; + mappings[1096] = ItemType.ChickenSpawnEgg; + mappings[1097] = ItemType.CodSpawnEgg; + mappings[1098] = ItemType.CowSpawnEgg; + mappings[1099] = ItemType.CreeperSpawnEgg; + mappings[1100] = ItemType.DolphinSpawnEgg; + mappings[1101] = ItemType.DonkeySpawnEgg; + mappings[1102] = ItemType.DrownedSpawnEgg; + mappings[1103] = ItemType.ElderGuardianSpawnEgg; + mappings[1104] = ItemType.EnderDragonSpawnEgg; + mappings[1105] = ItemType.EndermanSpawnEgg; + mappings[1106] = ItemType.EndermiteSpawnEgg; + mappings[1107] = ItemType.EvokerSpawnEgg; + mappings[1108] = ItemType.FoxSpawnEgg; + mappings[1109] = ItemType.FrogSpawnEgg; + mappings[1110] = ItemType.GhastSpawnEgg; + mappings[1111] = ItemType.HappyGhastSpawnEgg; + mappings[1112] = ItemType.GlowSquidSpawnEgg; + mappings[1113] = ItemType.GoatSpawnEgg; + mappings[1114] = ItemType.GuardianSpawnEgg; + mappings[1115] = ItemType.HoglinSpawnEgg; + mappings[1116] = ItemType.HorseSpawnEgg; + mappings[1117] = ItemType.HuskSpawnEgg; + mappings[1118] = ItemType.IronGolemSpawnEgg; + mappings[1119] = ItemType.LlamaSpawnEgg; + mappings[1120] = ItemType.MagmaCubeSpawnEgg; + mappings[1121] = ItemType.MooshroomSpawnEgg; + mappings[1122] = ItemType.MuleSpawnEgg; + mappings[1123] = ItemType.OcelotSpawnEgg; + mappings[1124] = ItemType.PandaSpawnEgg; + mappings[1125] = ItemType.ParrotSpawnEgg; + mappings[1126] = ItemType.PhantomSpawnEgg; + mappings[1127] = ItemType.PigSpawnEgg; + mappings[1128] = ItemType.PiglinSpawnEgg; + mappings[1129] = ItemType.PiglinBruteSpawnEgg; + mappings[1130] = ItemType.PillagerSpawnEgg; + mappings[1131] = ItemType.PolarBearSpawnEgg; + mappings[1132] = ItemType.PufferfishSpawnEgg; + mappings[1133] = ItemType.RabbitSpawnEgg; + mappings[1134] = ItemType.RavagerSpawnEgg; + mappings[1135] = ItemType.SalmonSpawnEgg; + mappings[1136] = ItemType.SheepSpawnEgg; + mappings[1137] = ItemType.ShulkerSpawnEgg; + mappings[1138] = ItemType.SilverfishSpawnEgg; + mappings[1139] = ItemType.SkeletonSpawnEgg; + mappings[1140] = ItemType.SkeletonHorseSpawnEgg; + mappings[1141] = ItemType.SlimeSpawnEgg; + mappings[1142] = ItemType.SnifferSpawnEgg; + mappings[1143] = ItemType.SnowGolemSpawnEgg; + mappings[1144] = ItemType.SpiderSpawnEgg; + mappings[1145] = ItemType.SquidSpawnEgg; + mappings[1146] = ItemType.StraySpawnEgg; + mappings[1147] = ItemType.StriderSpawnEgg; + mappings[1148] = ItemType.TadpoleSpawnEgg; + mappings[1149] = ItemType.TraderLlamaSpawnEgg; + mappings[1150] = ItemType.TropicalFishSpawnEgg; + mappings[1151] = ItemType.TurtleSpawnEgg; + mappings[1152] = ItemType.VexSpawnEgg; + mappings[1153] = ItemType.VillagerSpawnEgg; + mappings[1154] = ItemType.VindicatorSpawnEgg; + mappings[1155] = ItemType.WanderingTraderSpawnEgg; + mappings[1156] = ItemType.WardenSpawnEgg; + mappings[1157] = ItemType.WitchSpawnEgg; + mappings[1158] = ItemType.WitherSpawnEgg; + mappings[1159] = ItemType.WitherSkeletonSpawnEgg; + mappings[1160] = ItemType.WolfSpawnEgg; + mappings[1161] = ItemType.ZoglinSpawnEgg; + mappings[1162] = ItemType.CreakingSpawnEgg; + mappings[1163] = ItemType.ZombieSpawnEgg; + mappings[1164] = ItemType.ZombieHorseSpawnEgg; + mappings[1165] = ItemType.ZombieVillagerSpawnEgg; + mappings[1166] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1167] = ItemType.ExperienceBottle; + mappings[1168] = ItemType.FireCharge; + mappings[1169] = ItemType.WindCharge; + mappings[1170] = ItemType.WritableBook; + mappings[1171] = ItemType.WrittenBook; + mappings[1172] = ItemType.BreezeRod; + mappings[1173] = ItemType.Mace; + mappings[1174] = ItemType.ItemFrame; + mappings[1175] = ItemType.GlowItemFrame; + mappings[1176] = ItemType.FlowerPot; + mappings[1177] = ItemType.Carrot; + mappings[1178] = ItemType.Potato; + mappings[1179] = ItemType.BakedPotato; + mappings[1180] = ItemType.PoisonousPotato; + mappings[1181] = ItemType.Map; + mappings[1182] = ItemType.GoldenCarrot; + mappings[1183] = ItemType.SkeletonSkull; + mappings[1184] = ItemType.WitherSkeletonSkull; + mappings[1185] = ItemType.PlayerHead; + mappings[1186] = ItemType.ZombieHead; + mappings[1187] = ItemType.CreeperHead; + mappings[1188] = ItemType.DragonHead; + mappings[1189] = ItemType.PiglinHead; + mappings[1190] = ItemType.NetherStar; + mappings[1191] = ItemType.PumpkinPie; + mappings[1192] = ItemType.FireworkRocket; + mappings[1193] = ItemType.FireworkStar; + mappings[1194] = ItemType.EnchantedBook; + mappings[1195] = ItemType.NetherBrick; + mappings[1196] = ItemType.ResinBrick; + mappings[1197] = ItemType.PrismarineShard; + mappings[1198] = ItemType.PrismarineCrystals; + mappings[1199] = ItemType.Rabbit; + mappings[1200] = ItemType.CookedRabbit; + mappings[1201] = ItemType.RabbitStew; + mappings[1202] = ItemType.RabbitFoot; + mappings[1203] = ItemType.RabbitHide; + mappings[1204] = ItemType.ArmorStand; + mappings[1205] = ItemType.IronHorseArmor; + mappings[1206] = ItemType.GoldenHorseArmor; + mappings[1207] = ItemType.DiamondHorseArmor; + mappings[1208] = ItemType.LeatherHorseArmor; + mappings[1209] = ItemType.Lead; + mappings[1210] = ItemType.NameTag; + mappings[1211] = ItemType.CommandBlockMinecart; + mappings[1212] = ItemType.Mutton; + mappings[1213] = ItemType.CookedMutton; + mappings[1214] = ItemType.WhiteBanner; + mappings[1215] = ItemType.OrangeBanner; + mappings[1216] = ItemType.MagentaBanner; + mappings[1217] = ItemType.LightBlueBanner; + mappings[1218] = ItemType.YellowBanner; + mappings[1219] = ItemType.LimeBanner; + mappings[1220] = ItemType.PinkBanner; + mappings[1221] = ItemType.GrayBanner; + mappings[1222] = ItemType.LightGrayBanner; + mappings[1223] = ItemType.CyanBanner; + mappings[1224] = ItemType.PurpleBanner; + mappings[1225] = ItemType.BlueBanner; + mappings[1226] = ItemType.BrownBanner; + mappings[1227] = ItemType.GreenBanner; + mappings[1228] = ItemType.RedBanner; + mappings[1229] = ItemType.BlackBanner; + mappings[1230] = ItemType.EndCrystal; + mappings[1231] = ItemType.ChorusFruit; + mappings[1232] = ItemType.PoppedChorusFruit; + mappings[1233] = ItemType.TorchflowerSeeds; + mappings[1234] = ItemType.PitcherPod; + mappings[1235] = ItemType.Beetroot; + mappings[1236] = ItemType.BeetrootSeeds; + mappings[1237] = ItemType.BeetrootSoup; + mappings[1238] = ItemType.DragonBreath; + mappings[1239] = ItemType.SplashPotion; + mappings[1240] = ItemType.SpectralArrow; + mappings[1241] = ItemType.TippedArrow; + mappings[1242] = ItemType.LingeringPotion; + mappings[1243] = ItemType.Shield; + mappings[1244] = ItemType.TotemOfUndying; + mappings[1245] = ItemType.ShulkerShell; + mappings[1246] = ItemType.IronNugget; + mappings[1247] = ItemType.KnowledgeBook; + mappings[1248] = ItemType.DebugStick; + mappings[1249] = ItemType.MusicDisc13; + mappings[1250] = ItemType.MusicDiscCat; + mappings[1251] = ItemType.MusicDiscBlocks; + mappings[1252] = ItemType.MusicDiscChirp; + mappings[1253] = ItemType.MusicDiscCreator; + mappings[1254] = ItemType.MusicDiscCreatorMusicBox; + mappings[1255] = ItemType.MusicDiscFar; + mappings[1256] = ItemType.MusicDiscLavaChicken; + mappings[1257] = ItemType.MusicDiscMall; + mappings[1258] = ItemType.MusicDiscMellohi; + mappings[1259] = ItemType.MusicDiscStal; + mappings[1260] = ItemType.MusicDiscStrad; + mappings[1261] = ItemType.MusicDiscWard; + mappings[1262] = ItemType.MusicDisc11; + mappings[1263] = ItemType.MusicDiscWait; + mappings[1264] = ItemType.MusicDiscOtherside; + mappings[1265] = ItemType.MusicDiscRelic; + mappings[1266] = ItemType.MusicDisc5; + mappings[1267] = ItemType.MusicDiscPigstep; + mappings[1268] = ItemType.MusicDiscPrecipice; + mappings[1269] = ItemType.MusicDiscTears; + mappings[1270] = ItemType.DiscFragment5; + mappings[1271] = ItemType.Trident; + mappings[1272] = ItemType.NautilusShell; + mappings[1273] = ItemType.HeartOfTheSea; + mappings[1274] = ItemType.Crossbow; + mappings[1275] = ItemType.SuspiciousStew; + mappings[1276] = ItemType.Loom; + mappings[1277] = ItemType.FlowerBannerPattern; + mappings[1278] = ItemType.CreeperBannerPattern; + mappings[1279] = ItemType.SkullBannerPattern; + mappings[1280] = ItemType.MojangBannerPattern; + mappings[1281] = ItemType.GlobeBannerPattern; + mappings[1282] = ItemType.PiglinBannerPattern; + mappings[1283] = ItemType.FlowBannerPattern; + mappings[1284] = ItemType.GusterBannerPattern; + mappings[1285] = ItemType.FieldMasonedBannerPattern; + mappings[1286] = ItemType.BordureIndentedBannerPattern; + mappings[1287] = ItemType.GoatHorn; + mappings[1288] = ItemType.Composter; + mappings[1289] = ItemType.Barrel; + mappings[1290] = ItemType.Smoker; + mappings[1291] = ItemType.BlastFurnace; + mappings[1292] = ItemType.CartographyTable; + mappings[1293] = ItemType.FletchingTable; + mappings[1294] = ItemType.Grindstone; + mappings[1295] = ItemType.SmithingTable; + mappings[1296] = ItemType.Stonecutter; + mappings[1297] = ItemType.Bell; + mappings[1298] = ItemType.Lantern; + mappings[1299] = ItemType.SoulLantern; + mappings[1300] = ItemType.SweetBerries; + mappings[1301] = ItemType.GlowBerries; + mappings[1302] = ItemType.Campfire; + mappings[1303] = ItemType.SoulCampfire; + mappings[1304] = ItemType.Shroomlight; + mappings[1305] = ItemType.Honeycomb; + mappings[1306] = ItemType.BeeNest; + mappings[1307] = ItemType.Beehive; + mappings[1308] = ItemType.HoneyBottle; + mappings[1309] = ItemType.HoneycombBlock; + mappings[1310] = ItemType.Lodestone; + mappings[1311] = ItemType.CryingObsidian; + mappings[1312] = ItemType.Blackstone; + mappings[1313] = ItemType.BlackstoneSlab; + mappings[1314] = ItemType.BlackstoneStairs; + mappings[1315] = ItemType.GildedBlackstone; + mappings[1316] = ItemType.PolishedBlackstone; + mappings[1317] = ItemType.PolishedBlackstoneSlab; + mappings[1318] = ItemType.PolishedBlackstoneStairs; + mappings[1319] = ItemType.ChiseledPolishedBlackstone; + mappings[1320] = ItemType.PolishedBlackstoneBricks; + mappings[1321] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1322] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1323] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1324] = ItemType.RespawnAnchor; + mappings[1325] = ItemType.Candle; + mappings[1326] = ItemType.WhiteCandle; + mappings[1327] = ItemType.OrangeCandle; + mappings[1328] = ItemType.MagentaCandle; + mappings[1329] = ItemType.LightBlueCandle; + mappings[1330] = ItemType.YellowCandle; + mappings[1331] = ItemType.LimeCandle; + mappings[1332] = ItemType.PinkCandle; + mappings[1333] = ItemType.GrayCandle; + mappings[1334] = ItemType.LightGrayCandle; + mappings[1335] = ItemType.CyanCandle; + mappings[1336] = ItemType.PurpleCandle; + mappings[1337] = ItemType.BlueCandle; + mappings[1338] = ItemType.BrownCandle; + mappings[1339] = ItemType.GreenCandle; + mappings[1340] = ItemType.RedCandle; + mappings[1341] = ItemType.BlackCandle; + mappings[1342] = ItemType.SmallAmethystBud; + mappings[1343] = ItemType.MediumAmethystBud; + mappings[1344] = ItemType.LargeAmethystBud; + mappings[1345] = ItemType.AmethystCluster; + mappings[1346] = ItemType.PointedDripstone; + mappings[1347] = ItemType.OchreFroglight; + mappings[1348] = ItemType.VerdantFroglight; + mappings[1349] = ItemType.PearlescentFroglight; + mappings[1350] = ItemType.Frogspawn; + mappings[1351] = ItemType.EchoShard; + mappings[1352] = ItemType.Brush; + mappings[1353] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1354] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1355] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1356] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1357] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1358] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1359] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1360] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1361] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1362] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1363] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1364] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1365] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1366] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1367] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1368] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1369] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1370] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1371] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1372] = ItemType.AnglerPotterySherd; + mappings[1373] = ItemType.ArcherPotterySherd; + mappings[1374] = ItemType.ArmsUpPotterySherd; + mappings[1375] = ItemType.BladePotterySherd; + mappings[1376] = ItemType.BrewerPotterySherd; + mappings[1377] = ItemType.BurnPotterySherd; + mappings[1378] = ItemType.DangerPotterySherd; + mappings[1379] = ItemType.ExplorerPotterySherd; + mappings[1380] = ItemType.FlowPotterySherd; + mappings[1381] = ItemType.FriendPotterySherd; + mappings[1382] = ItemType.GusterPotterySherd; + mappings[1383] = ItemType.HeartPotterySherd; + mappings[1384] = ItemType.HeartbreakPotterySherd; + mappings[1385] = ItemType.HowlPotterySherd; + mappings[1386] = ItemType.MinerPotterySherd; + mappings[1387] = ItemType.MournerPotterySherd; + mappings[1388] = ItemType.PlentyPotterySherd; + mappings[1389] = ItemType.PrizePotterySherd; + mappings[1390] = ItemType.ScrapePotterySherd; + mappings[1391] = ItemType.SheafPotterySherd; + mappings[1392] = ItemType.ShelterPotterySherd; + mappings[1393] = ItemType.SkullPotterySherd; + mappings[1394] = ItemType.SnortPotterySherd; + mappings[1395] = ItemType.CopperGrate; + mappings[1396] = ItemType.ExposedCopperGrate; + mappings[1397] = ItemType.WeatheredCopperGrate; + mappings[1398] = ItemType.OxidizedCopperGrate; + mappings[1399] = ItemType.WaxedCopperGrate; + mappings[1400] = ItemType.WaxedExposedCopperGrate; + mappings[1401] = ItemType.WaxedWeatheredCopperGrate; + mappings[1402] = ItemType.WaxedOxidizedCopperGrate; + mappings[1403] = ItemType.CopperBulb; + mappings[1404] = ItemType.ExposedCopperBulb; + mappings[1405] = ItemType.WeatheredCopperBulb; + mappings[1406] = ItemType.OxidizedCopperBulb; + mappings[1407] = ItemType.WaxedCopperBulb; + mappings[1408] = ItemType.WaxedExposedCopperBulb; + mappings[1409] = ItemType.WaxedWeatheredCopperBulb; + mappings[1410] = ItemType.WaxedOxidizedCopperBulb; + mappings[1411] = ItemType.TrialSpawner; + mappings[1412] = ItemType.TrialKey; + mappings[1413] = ItemType.OminousTrialKey; + mappings[1414] = ItemType.Vault; + mappings[1415] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1219.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1219.cs new file mode 100644 index 00000000..db6985f7 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1219.cs @@ -0,0 +1,1506 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1219 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1219() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.ShortDryGrass; + mappings[210] = ItemType.TallDryGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.OpenEyeblossom; + mappings[231] = ItemType.ClosedEyeblossom; + mappings[232] = ItemType.Poppy; + mappings[233] = ItemType.BlueOrchid; + mappings[234] = ItemType.Allium; + mappings[235] = ItemType.AzureBluet; + mappings[236] = ItemType.RedTulip; + mappings[237] = ItemType.OrangeTulip; + mappings[238] = ItemType.WhiteTulip; + mappings[239] = ItemType.PinkTulip; + mappings[240] = ItemType.OxeyeDaisy; + mappings[241] = ItemType.Cornflower; + mappings[242] = ItemType.LilyOfTheValley; + mappings[243] = ItemType.WitherRose; + mappings[244] = ItemType.Torchflower; + mappings[245] = ItemType.PitcherPlant; + mappings[246] = ItemType.SporeBlossom; + mappings[247] = ItemType.BrownMushroom; + mappings[248] = ItemType.RedMushroom; + mappings[249] = ItemType.CrimsonFungus; + mappings[250] = ItemType.WarpedFungus; + mappings[251] = ItemType.CrimsonRoots; + mappings[252] = ItemType.WarpedRoots; + mappings[253] = ItemType.NetherSprouts; + mappings[254] = ItemType.WeepingVines; + mappings[255] = ItemType.TwistingVines; + mappings[256] = ItemType.SugarCane; + mappings[257] = ItemType.Kelp; + mappings[258] = ItemType.PinkPetals; + mappings[259] = ItemType.Wildflowers; + mappings[260] = ItemType.LeafLitter; + mappings[261] = ItemType.MossCarpet; + mappings[262] = ItemType.MossBlock; + mappings[263] = ItemType.PaleMossCarpet; + mappings[264] = ItemType.PaleHangingMoss; + mappings[265] = ItemType.PaleMossBlock; + mappings[266] = ItemType.HangingRoots; + mappings[267] = ItemType.BigDripleaf; + mappings[268] = ItemType.SmallDripleaf; + mappings[269] = ItemType.Bamboo; + mappings[270] = ItemType.OakSlab; + mappings[271] = ItemType.SpruceSlab; + mappings[272] = ItemType.BirchSlab; + mappings[273] = ItemType.JungleSlab; + mappings[274] = ItemType.AcaciaSlab; + mappings[275] = ItemType.CherrySlab; + mappings[276] = ItemType.DarkOakSlab; + mappings[277] = ItemType.PaleOakSlab; + mappings[278] = ItemType.MangroveSlab; + mappings[279] = ItemType.BambooSlab; + mappings[280] = ItemType.BambooMosaicSlab; + mappings[281] = ItemType.CrimsonSlab; + mappings[282] = ItemType.WarpedSlab; + mappings[283] = ItemType.StoneSlab; + mappings[284] = ItemType.SmoothStoneSlab; + mappings[285] = ItemType.SandstoneSlab; + mappings[286] = ItemType.CutSandstoneSlab; + mappings[287] = ItemType.PetrifiedOakSlab; + mappings[288] = ItemType.CobblestoneSlab; + mappings[289] = ItemType.BrickSlab; + mappings[290] = ItemType.StoneBrickSlab; + mappings[291] = ItemType.MudBrickSlab; + mappings[292] = ItemType.NetherBrickSlab; + mappings[293] = ItemType.QuartzSlab; + mappings[294] = ItemType.RedSandstoneSlab; + mappings[295] = ItemType.CutRedSandstoneSlab; + mappings[296] = ItemType.PurpurSlab; + mappings[297] = ItemType.PrismarineSlab; + mappings[298] = ItemType.PrismarineBrickSlab; + mappings[299] = ItemType.DarkPrismarineSlab; + mappings[300] = ItemType.SmoothQuartz; + mappings[301] = ItemType.SmoothRedSandstone; + mappings[302] = ItemType.SmoothSandstone; + mappings[303] = ItemType.SmoothStone; + mappings[304] = ItemType.Bricks; + mappings[305] = ItemType.AcaciaShelf; + mappings[306] = ItemType.BambooShelf; + mappings[307] = ItemType.BirchShelf; + mappings[308] = ItemType.CherryShelf; + mappings[309] = ItemType.CrimsonShelf; + mappings[310] = ItemType.DarkOakShelf; + mappings[311] = ItemType.JungleShelf; + mappings[312] = ItemType.MangroveShelf; + mappings[313] = ItemType.OakShelf; + mappings[314] = ItemType.PaleOakShelf; + mappings[315] = ItemType.SpruceShelf; + mappings[316] = ItemType.WarpedShelf; + mappings[317] = ItemType.Bookshelf; + mappings[318] = ItemType.ChiseledBookshelf; + mappings[319] = ItemType.DecoratedPot; + mappings[320] = ItemType.MossyCobblestone; + mappings[321] = ItemType.Obsidian; + mappings[322] = ItemType.Torch; + mappings[323] = ItemType.EndRod; + mappings[324] = ItemType.ChorusPlant; + mappings[325] = ItemType.ChorusFlower; + mappings[326] = ItemType.PurpurBlock; + mappings[327] = ItemType.PurpurPillar; + mappings[328] = ItemType.PurpurStairs; + mappings[329] = ItemType.Spawner; + mappings[330] = ItemType.CreakingHeart; + mappings[331] = ItemType.Chest; + mappings[332] = ItemType.CraftingTable; + mappings[333] = ItemType.Farmland; + mappings[334] = ItemType.Furnace; + mappings[335] = ItemType.Ladder; + mappings[336] = ItemType.CobblestoneStairs; + mappings[337] = ItemType.Snow; + mappings[338] = ItemType.Ice; + mappings[339] = ItemType.SnowBlock; + mappings[340] = ItemType.Cactus; + mappings[341] = ItemType.CactusFlower; + mappings[342] = ItemType.Clay; + mappings[343] = ItemType.Jukebox; + mappings[344] = ItemType.OakFence; + mappings[345] = ItemType.SpruceFence; + mappings[346] = ItemType.BirchFence; + mappings[347] = ItemType.JungleFence; + mappings[348] = ItemType.AcaciaFence; + mappings[349] = ItemType.CherryFence; + mappings[350] = ItemType.DarkOakFence; + mappings[351] = ItemType.PaleOakFence; + mappings[352] = ItemType.MangroveFence; + mappings[353] = ItemType.BambooFence; + mappings[354] = ItemType.CrimsonFence; + mappings[355] = ItemType.WarpedFence; + mappings[356] = ItemType.Pumpkin; + mappings[357] = ItemType.CarvedPumpkin; + mappings[358] = ItemType.JackOLantern; + mappings[359] = ItemType.Netherrack; + mappings[360] = ItemType.SoulSand; + mappings[361] = ItemType.SoulSoil; + mappings[362] = ItemType.Basalt; + mappings[363] = ItemType.PolishedBasalt; + mappings[364] = ItemType.SmoothBasalt; + mappings[365] = ItemType.SoulTorch; + mappings[366] = ItemType.CopperTorch; + mappings[367] = ItemType.Glowstone; + mappings[368] = ItemType.InfestedStone; + mappings[369] = ItemType.InfestedCobblestone; + mappings[370] = ItemType.InfestedStoneBricks; + mappings[371] = ItemType.InfestedMossyStoneBricks; + mappings[372] = ItemType.InfestedCrackedStoneBricks; + mappings[373] = ItemType.InfestedChiseledStoneBricks; + mappings[374] = ItemType.InfestedDeepslate; + mappings[375] = ItemType.StoneBricks; + mappings[376] = ItemType.MossyStoneBricks; + mappings[377] = ItemType.CrackedStoneBricks; + mappings[378] = ItemType.ChiseledStoneBricks; + mappings[379] = ItemType.PackedMud; + mappings[380] = ItemType.MudBricks; + mappings[381] = ItemType.DeepslateBricks; + mappings[382] = ItemType.CrackedDeepslateBricks; + mappings[383] = ItemType.DeepslateTiles; + mappings[384] = ItemType.CrackedDeepslateTiles; + mappings[385] = ItemType.ChiseledDeepslate; + mappings[386] = ItemType.ReinforcedDeepslate; + mappings[387] = ItemType.BrownMushroomBlock; + mappings[388] = ItemType.RedMushroomBlock; + mappings[389] = ItemType.MushroomStem; + mappings[390] = ItemType.IronBars; + mappings[391] = ItemType.CopperBars; + mappings[392] = ItemType.ExposedCopperBars; + mappings[393] = ItemType.WeatheredCopperBars; + mappings[394] = ItemType.OxidizedCopperBars; + mappings[395] = ItemType.WaxedCopperBars; + mappings[396] = ItemType.WaxedExposedCopperBars; + mappings[397] = ItemType.WaxedWeatheredCopperBars; + mappings[398] = ItemType.WaxedOxidizedCopperBars; + mappings[399] = ItemType.IronChain; + mappings[400] = ItemType.CopperChain; + mappings[401] = ItemType.ExposedCopperChain; + mappings[402] = ItemType.WeatheredCopperChain; + mappings[403] = ItemType.OxidizedCopperChain; + mappings[404] = ItemType.WaxedCopperChain; + mappings[405] = ItemType.WaxedExposedCopperChain; + mappings[406] = ItemType.WaxedWeatheredCopperChain; + mappings[407] = ItemType.WaxedOxidizedCopperChain; + mappings[408] = ItemType.GlassPane; + mappings[409] = ItemType.Melon; + mappings[410] = ItemType.Vine; + mappings[411] = ItemType.GlowLichen; + mappings[412] = ItemType.ResinClump; + mappings[413] = ItemType.ResinBlock; + mappings[414] = ItemType.ResinBricks; + mappings[415] = ItemType.ResinBrickStairs; + mappings[416] = ItemType.ResinBrickSlab; + mappings[417] = ItemType.ResinBrickWall; + mappings[418] = ItemType.ChiseledResinBricks; + mappings[419] = ItemType.BrickStairs; + mappings[420] = ItemType.StoneBrickStairs; + mappings[421] = ItemType.MudBrickStairs; + mappings[422] = ItemType.Mycelium; + mappings[423] = ItemType.LilyPad; + mappings[424] = ItemType.NetherBricks; + mappings[425] = ItemType.CrackedNetherBricks; + mappings[426] = ItemType.ChiseledNetherBricks; + mappings[427] = ItemType.NetherBrickFence; + mappings[428] = ItemType.NetherBrickStairs; + mappings[429] = ItemType.Sculk; + mappings[430] = ItemType.SculkVein; + mappings[431] = ItemType.SculkCatalyst; + mappings[432] = ItemType.SculkShrieker; + mappings[433] = ItemType.EnchantingTable; + mappings[434] = ItemType.EndPortalFrame; + mappings[435] = ItemType.EndStone; + mappings[436] = ItemType.EndStoneBricks; + mappings[437] = ItemType.DragonEgg; + mappings[438] = ItemType.SandstoneStairs; + mappings[439] = ItemType.EnderChest; + mappings[440] = ItemType.EmeraldBlock; + mappings[441] = ItemType.OakStairs; + mappings[442] = ItemType.SpruceStairs; + mappings[443] = ItemType.BirchStairs; + mappings[444] = ItemType.JungleStairs; + mappings[445] = ItemType.AcaciaStairs; + mappings[446] = ItemType.CherryStairs; + mappings[447] = ItemType.DarkOakStairs; + mappings[448] = ItemType.PaleOakStairs; + mappings[449] = ItemType.MangroveStairs; + mappings[450] = ItemType.BambooStairs; + mappings[451] = ItemType.BambooMosaicStairs; + mappings[452] = ItemType.CrimsonStairs; + mappings[453] = ItemType.WarpedStairs; + mappings[454] = ItemType.CommandBlock; + mappings[455] = ItemType.Beacon; + mappings[456] = ItemType.CobblestoneWall; + mappings[457] = ItemType.MossyCobblestoneWall; + mappings[458] = ItemType.BrickWall; + mappings[459] = ItemType.PrismarineWall; + mappings[460] = ItemType.RedSandstoneWall; + mappings[461] = ItemType.MossyStoneBrickWall; + mappings[462] = ItemType.GraniteWall; + mappings[463] = ItemType.StoneBrickWall; + mappings[464] = ItemType.MudBrickWall; + mappings[465] = ItemType.NetherBrickWall; + mappings[466] = ItemType.AndesiteWall; + mappings[467] = ItemType.RedNetherBrickWall; + mappings[468] = ItemType.SandstoneWall; + mappings[469] = ItemType.EndStoneBrickWall; + mappings[470] = ItemType.DioriteWall; + mappings[471] = ItemType.BlackstoneWall; + mappings[472] = ItemType.PolishedBlackstoneWall; + mappings[473] = ItemType.PolishedBlackstoneBrickWall; + mappings[474] = ItemType.CobbledDeepslateWall; + mappings[475] = ItemType.PolishedDeepslateWall; + mappings[476] = ItemType.DeepslateBrickWall; + mappings[477] = ItemType.DeepslateTileWall; + mappings[478] = ItemType.Anvil; + mappings[479] = ItemType.ChippedAnvil; + mappings[480] = ItemType.DamagedAnvil; + mappings[481] = ItemType.ChiseledQuartzBlock; + mappings[482] = ItemType.QuartzBlock; + mappings[483] = ItemType.QuartzBricks; + mappings[484] = ItemType.QuartzPillar; + mappings[485] = ItemType.QuartzStairs; + mappings[486] = ItemType.WhiteTerracotta; + mappings[487] = ItemType.OrangeTerracotta; + mappings[488] = ItemType.MagentaTerracotta; + mappings[489] = ItemType.LightBlueTerracotta; + mappings[490] = ItemType.YellowTerracotta; + mappings[491] = ItemType.LimeTerracotta; + mappings[492] = ItemType.PinkTerracotta; + mappings[493] = ItemType.GrayTerracotta; + mappings[494] = ItemType.LightGrayTerracotta; + mappings[495] = ItemType.CyanTerracotta; + mappings[496] = ItemType.PurpleTerracotta; + mappings[497] = ItemType.BlueTerracotta; + mappings[498] = ItemType.BrownTerracotta; + mappings[499] = ItemType.GreenTerracotta; + mappings[500] = ItemType.RedTerracotta; + mappings[501] = ItemType.BlackTerracotta; + mappings[502] = ItemType.Barrier; + mappings[503] = ItemType.Light; + mappings[504] = ItemType.HayBlock; + mappings[505] = ItemType.WhiteCarpet; + mappings[506] = ItemType.OrangeCarpet; + mappings[507] = ItemType.MagentaCarpet; + mappings[508] = ItemType.LightBlueCarpet; + mappings[509] = ItemType.YellowCarpet; + mappings[510] = ItemType.LimeCarpet; + mappings[511] = ItemType.PinkCarpet; + mappings[512] = ItemType.GrayCarpet; + mappings[513] = ItemType.LightGrayCarpet; + mappings[514] = ItemType.CyanCarpet; + mappings[515] = ItemType.PurpleCarpet; + mappings[516] = ItemType.BlueCarpet; + mappings[517] = ItemType.BrownCarpet; + mappings[518] = ItemType.GreenCarpet; + mappings[519] = ItemType.RedCarpet; + mappings[520] = ItemType.BlackCarpet; + mappings[521] = ItemType.Terracotta; + mappings[522] = ItemType.PackedIce; + mappings[523] = ItemType.DirtPath; + mappings[524] = ItemType.Sunflower; + mappings[525] = ItemType.Lilac; + mappings[526] = ItemType.RoseBush; + mappings[527] = ItemType.Peony; + mappings[528] = ItemType.TallGrass; + mappings[529] = ItemType.LargeFern; + mappings[530] = ItemType.WhiteStainedGlass; + mappings[531] = ItemType.OrangeStainedGlass; + mappings[532] = ItemType.MagentaStainedGlass; + mappings[533] = ItemType.LightBlueStainedGlass; + mappings[534] = ItemType.YellowStainedGlass; + mappings[535] = ItemType.LimeStainedGlass; + mappings[536] = ItemType.PinkStainedGlass; + mappings[537] = ItemType.GrayStainedGlass; + mappings[538] = ItemType.LightGrayStainedGlass; + mappings[539] = ItemType.CyanStainedGlass; + mappings[540] = ItemType.PurpleStainedGlass; + mappings[541] = ItemType.BlueStainedGlass; + mappings[542] = ItemType.BrownStainedGlass; + mappings[543] = ItemType.GreenStainedGlass; + mappings[544] = ItemType.RedStainedGlass; + mappings[545] = ItemType.BlackStainedGlass; + mappings[546] = ItemType.WhiteStainedGlassPane; + mappings[547] = ItemType.OrangeStainedGlassPane; + mappings[548] = ItemType.MagentaStainedGlassPane; + mappings[549] = ItemType.LightBlueStainedGlassPane; + mappings[550] = ItemType.YellowStainedGlassPane; + mappings[551] = ItemType.LimeStainedGlassPane; + mappings[552] = ItemType.PinkStainedGlassPane; + mappings[553] = ItemType.GrayStainedGlassPane; + mappings[554] = ItemType.LightGrayStainedGlassPane; + mappings[555] = ItemType.CyanStainedGlassPane; + mappings[556] = ItemType.PurpleStainedGlassPane; + mappings[557] = ItemType.BlueStainedGlassPane; + mappings[558] = ItemType.BrownStainedGlassPane; + mappings[559] = ItemType.GreenStainedGlassPane; + mappings[560] = ItemType.RedStainedGlassPane; + mappings[561] = ItemType.BlackStainedGlassPane; + mappings[562] = ItemType.Prismarine; + mappings[563] = ItemType.PrismarineBricks; + mappings[564] = ItemType.DarkPrismarine; + mappings[565] = ItemType.PrismarineStairs; + mappings[566] = ItemType.PrismarineBrickStairs; + mappings[567] = ItemType.DarkPrismarineStairs; + mappings[568] = ItemType.SeaLantern; + mappings[569] = ItemType.RedSandstone; + mappings[570] = ItemType.ChiseledRedSandstone; + mappings[571] = ItemType.CutRedSandstone; + mappings[572] = ItemType.RedSandstoneStairs; + mappings[573] = ItemType.RepeatingCommandBlock; + mappings[574] = ItemType.ChainCommandBlock; + mappings[575] = ItemType.MagmaBlock; + mappings[576] = ItemType.NetherWartBlock; + mappings[577] = ItemType.WarpedWartBlock; + mappings[578] = ItemType.RedNetherBricks; + mappings[579] = ItemType.BoneBlock; + mappings[580] = ItemType.StructureVoid; + mappings[581] = ItemType.ShulkerBox; + mappings[582] = ItemType.WhiteShulkerBox; + mappings[583] = ItemType.OrangeShulkerBox; + mappings[584] = ItemType.MagentaShulkerBox; + mappings[585] = ItemType.LightBlueShulkerBox; + mappings[586] = ItemType.YellowShulkerBox; + mappings[587] = ItemType.LimeShulkerBox; + mappings[588] = ItemType.PinkShulkerBox; + mappings[589] = ItemType.GrayShulkerBox; + mappings[590] = ItemType.LightGrayShulkerBox; + mappings[591] = ItemType.CyanShulkerBox; + mappings[592] = ItemType.PurpleShulkerBox; + mappings[593] = ItemType.BlueShulkerBox; + mappings[594] = ItemType.BrownShulkerBox; + mappings[595] = ItemType.GreenShulkerBox; + mappings[596] = ItemType.RedShulkerBox; + mappings[597] = ItemType.BlackShulkerBox; + mappings[598] = ItemType.WhiteGlazedTerracotta; + mappings[599] = ItemType.OrangeGlazedTerracotta; + mappings[600] = ItemType.MagentaGlazedTerracotta; + mappings[601] = ItemType.LightBlueGlazedTerracotta; + mappings[602] = ItemType.YellowGlazedTerracotta; + mappings[603] = ItemType.LimeGlazedTerracotta; + mappings[604] = ItemType.PinkGlazedTerracotta; + mappings[605] = ItemType.GrayGlazedTerracotta; + mappings[606] = ItemType.LightGrayGlazedTerracotta; + mappings[607] = ItemType.CyanGlazedTerracotta; + mappings[608] = ItemType.PurpleGlazedTerracotta; + mappings[609] = ItemType.BlueGlazedTerracotta; + mappings[610] = ItemType.BrownGlazedTerracotta; + mappings[611] = ItemType.GreenGlazedTerracotta; + mappings[612] = ItemType.RedGlazedTerracotta; + mappings[613] = ItemType.BlackGlazedTerracotta; + mappings[614] = ItemType.WhiteConcrete; + mappings[615] = ItemType.OrangeConcrete; + mappings[616] = ItemType.MagentaConcrete; + mappings[617] = ItemType.LightBlueConcrete; + mappings[618] = ItemType.YellowConcrete; + mappings[619] = ItemType.LimeConcrete; + mappings[620] = ItemType.PinkConcrete; + mappings[621] = ItemType.GrayConcrete; + mappings[622] = ItemType.LightGrayConcrete; + mappings[623] = ItemType.CyanConcrete; + mappings[624] = ItemType.PurpleConcrete; + mappings[625] = ItemType.BlueConcrete; + mappings[626] = ItemType.BrownConcrete; + mappings[627] = ItemType.GreenConcrete; + mappings[628] = ItemType.RedConcrete; + mappings[629] = ItemType.BlackConcrete; + mappings[630] = ItemType.WhiteConcretePowder; + mappings[631] = ItemType.OrangeConcretePowder; + mappings[632] = ItemType.MagentaConcretePowder; + mappings[633] = ItemType.LightBlueConcretePowder; + mappings[634] = ItemType.YellowConcretePowder; + mappings[635] = ItemType.LimeConcretePowder; + mappings[636] = ItemType.PinkConcretePowder; + mappings[637] = ItemType.GrayConcretePowder; + mappings[638] = ItemType.LightGrayConcretePowder; + mappings[639] = ItemType.CyanConcretePowder; + mappings[640] = ItemType.PurpleConcretePowder; + mappings[641] = ItemType.BlueConcretePowder; + mappings[642] = ItemType.BrownConcretePowder; + mappings[643] = ItemType.GreenConcretePowder; + mappings[644] = ItemType.RedConcretePowder; + mappings[645] = ItemType.BlackConcretePowder; + mappings[646] = ItemType.TurtleEgg; + mappings[647] = ItemType.SnifferEgg; + mappings[648] = ItemType.DriedGhast; + mappings[649] = ItemType.DeadTubeCoralBlock; + mappings[650] = ItemType.DeadBrainCoralBlock; + mappings[651] = ItemType.DeadBubbleCoralBlock; + mappings[652] = ItemType.DeadFireCoralBlock; + mappings[653] = ItemType.DeadHornCoralBlock; + mappings[654] = ItemType.TubeCoralBlock; + mappings[655] = ItemType.BrainCoralBlock; + mappings[656] = ItemType.BubbleCoralBlock; + mappings[657] = ItemType.FireCoralBlock; + mappings[658] = ItemType.HornCoralBlock; + mappings[659] = ItemType.TubeCoral; + mappings[660] = ItemType.BrainCoral; + mappings[661] = ItemType.BubbleCoral; + mappings[662] = ItemType.FireCoral; + mappings[663] = ItemType.HornCoral; + mappings[664] = ItemType.DeadBrainCoral; + mappings[665] = ItemType.DeadBubbleCoral; + mappings[666] = ItemType.DeadFireCoral; + mappings[667] = ItemType.DeadHornCoral; + mappings[668] = ItemType.DeadTubeCoral; + mappings[669] = ItemType.TubeCoralFan; + mappings[670] = ItemType.BrainCoralFan; + mappings[671] = ItemType.BubbleCoralFan; + mappings[672] = ItemType.FireCoralFan; + mappings[673] = ItemType.HornCoralFan; + mappings[674] = ItemType.DeadTubeCoralFan; + mappings[675] = ItemType.DeadBrainCoralFan; + mappings[676] = ItemType.DeadBubbleCoralFan; + mappings[677] = ItemType.DeadFireCoralFan; + mappings[678] = ItemType.DeadHornCoralFan; + mappings[679] = ItemType.BlueIce; + mappings[680] = ItemType.Conduit; + mappings[681] = ItemType.PolishedGraniteStairs; + mappings[682] = ItemType.SmoothRedSandstoneStairs; + mappings[683] = ItemType.MossyStoneBrickStairs; + mappings[684] = ItemType.PolishedDioriteStairs; + mappings[685] = ItemType.MossyCobblestoneStairs; + mappings[686] = ItemType.EndStoneBrickStairs; + mappings[687] = ItemType.StoneStairs; + mappings[688] = ItemType.SmoothSandstoneStairs; + mappings[689] = ItemType.SmoothQuartzStairs; + mappings[690] = ItemType.GraniteStairs; + mappings[691] = ItemType.AndesiteStairs; + mappings[692] = ItemType.RedNetherBrickStairs; + mappings[693] = ItemType.PolishedAndesiteStairs; + mappings[694] = ItemType.DioriteStairs; + mappings[695] = ItemType.CobbledDeepslateStairs; + mappings[696] = ItemType.PolishedDeepslateStairs; + mappings[697] = ItemType.DeepslateBrickStairs; + mappings[698] = ItemType.DeepslateTileStairs; + mappings[699] = ItemType.PolishedGraniteSlab; + mappings[700] = ItemType.SmoothRedSandstoneSlab; + mappings[701] = ItemType.MossyStoneBrickSlab; + mappings[702] = ItemType.PolishedDioriteSlab; + mappings[703] = ItemType.MossyCobblestoneSlab; + mappings[704] = ItemType.EndStoneBrickSlab; + mappings[705] = ItemType.SmoothSandstoneSlab; + mappings[706] = ItemType.SmoothQuartzSlab; + mappings[707] = ItemType.GraniteSlab; + mappings[708] = ItemType.AndesiteSlab; + mappings[709] = ItemType.RedNetherBrickSlab; + mappings[710] = ItemType.PolishedAndesiteSlab; + mappings[711] = ItemType.DioriteSlab; + mappings[712] = ItemType.CobbledDeepslateSlab; + mappings[713] = ItemType.PolishedDeepslateSlab; + mappings[714] = ItemType.DeepslateBrickSlab; + mappings[715] = ItemType.DeepslateTileSlab; + mappings[716] = ItemType.Scaffolding; + mappings[717] = ItemType.Redstone; + mappings[718] = ItemType.RedstoneTorch; + mappings[719] = ItemType.RedstoneBlock; + mappings[720] = ItemType.Repeater; + mappings[721] = ItemType.Comparator; + mappings[722] = ItemType.Piston; + mappings[723] = ItemType.StickyPiston; + mappings[724] = ItemType.SlimeBlock; + mappings[725] = ItemType.HoneyBlock; + mappings[726] = ItemType.Observer; + mappings[727] = ItemType.Hopper; + mappings[728] = ItemType.Dispenser; + mappings[729] = ItemType.Dropper; + mappings[730] = ItemType.Lectern; + mappings[731] = ItemType.Target; + mappings[732] = ItemType.Lever; + mappings[733] = ItemType.LightningRod; + mappings[734] = ItemType.ExposedLightningRod; + mappings[735] = ItemType.WeatheredLightningRod; + mappings[736] = ItemType.OxidizedLightningRod; + mappings[737] = ItemType.WaxedLightningRod; + mappings[738] = ItemType.WaxedExposedLightningRod; + mappings[739] = ItemType.WaxedWeatheredLightningRod; + mappings[740] = ItemType.WaxedOxidizedLightningRod; + mappings[741] = ItemType.DaylightDetector; + mappings[742] = ItemType.SculkSensor; + mappings[743] = ItemType.CalibratedSculkSensor; + mappings[744] = ItemType.TripwireHook; + mappings[745] = ItemType.TrappedChest; + mappings[746] = ItemType.Tnt; + mappings[747] = ItemType.RedstoneLamp; + mappings[748] = ItemType.NoteBlock; + mappings[749] = ItemType.StoneButton; + mappings[750] = ItemType.PolishedBlackstoneButton; + mappings[751] = ItemType.OakButton; + mappings[752] = ItemType.SpruceButton; + mappings[753] = ItemType.BirchButton; + mappings[754] = ItemType.JungleButton; + mappings[755] = ItemType.AcaciaButton; + mappings[756] = ItemType.CherryButton; + mappings[757] = ItemType.DarkOakButton; + mappings[758] = ItemType.PaleOakButton; + mappings[759] = ItemType.MangroveButton; + mappings[760] = ItemType.BambooButton; + mappings[761] = ItemType.CrimsonButton; + mappings[762] = ItemType.WarpedButton; + mappings[763] = ItemType.StonePressurePlate; + mappings[764] = ItemType.PolishedBlackstonePressurePlate; + mappings[765] = ItemType.LightWeightedPressurePlate; + mappings[766] = ItemType.HeavyWeightedPressurePlate; + mappings[767] = ItemType.OakPressurePlate; + mappings[768] = ItemType.SprucePressurePlate; + mappings[769] = ItemType.BirchPressurePlate; + mappings[770] = ItemType.JunglePressurePlate; + mappings[771] = ItemType.AcaciaPressurePlate; + mappings[772] = ItemType.CherryPressurePlate; + mappings[773] = ItemType.DarkOakPressurePlate; + mappings[774] = ItemType.PaleOakPressurePlate; + mappings[775] = ItemType.MangrovePressurePlate; + mappings[776] = ItemType.BambooPressurePlate; + mappings[777] = ItemType.CrimsonPressurePlate; + mappings[778] = ItemType.WarpedPressurePlate; + mappings[779] = ItemType.IronDoor; + mappings[780] = ItemType.OakDoor; + mappings[781] = ItemType.SpruceDoor; + mappings[782] = ItemType.BirchDoor; + mappings[783] = ItemType.JungleDoor; + mappings[784] = ItemType.AcaciaDoor; + mappings[785] = ItemType.CherryDoor; + mappings[786] = ItemType.DarkOakDoor; + mappings[787] = ItemType.PaleOakDoor; + mappings[788] = ItemType.MangroveDoor; + mappings[789] = ItemType.BambooDoor; + mappings[790] = ItemType.CrimsonDoor; + mappings[791] = ItemType.WarpedDoor; + mappings[792] = ItemType.CopperDoor; + mappings[793] = ItemType.ExposedCopperDoor; + mappings[794] = ItemType.WeatheredCopperDoor; + mappings[795] = ItemType.OxidizedCopperDoor; + mappings[796] = ItemType.WaxedCopperDoor; + mappings[797] = ItemType.WaxedExposedCopperDoor; + mappings[798] = ItemType.WaxedWeatheredCopperDoor; + mappings[799] = ItemType.WaxedOxidizedCopperDoor; + mappings[800] = ItemType.IronTrapdoor; + mappings[801] = ItemType.OakTrapdoor; + mappings[802] = ItemType.SpruceTrapdoor; + mappings[803] = ItemType.BirchTrapdoor; + mappings[804] = ItemType.JungleTrapdoor; + mappings[805] = ItemType.AcaciaTrapdoor; + mappings[806] = ItemType.CherryTrapdoor; + mappings[807] = ItemType.DarkOakTrapdoor; + mappings[808] = ItemType.PaleOakTrapdoor; + mappings[809] = ItemType.MangroveTrapdoor; + mappings[810] = ItemType.BambooTrapdoor; + mappings[811] = ItemType.CrimsonTrapdoor; + mappings[812] = ItemType.WarpedTrapdoor; + mappings[813] = ItemType.CopperTrapdoor; + mappings[814] = ItemType.ExposedCopperTrapdoor; + mappings[815] = ItemType.WeatheredCopperTrapdoor; + mappings[816] = ItemType.OxidizedCopperTrapdoor; + mappings[817] = ItemType.WaxedCopperTrapdoor; + mappings[818] = ItemType.WaxedExposedCopperTrapdoor; + mappings[819] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[820] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[821] = ItemType.OakFenceGate; + mappings[822] = ItemType.SpruceFenceGate; + mappings[823] = ItemType.BirchFenceGate; + mappings[824] = ItemType.JungleFenceGate; + mappings[825] = ItemType.AcaciaFenceGate; + mappings[826] = ItemType.CherryFenceGate; + mappings[827] = ItemType.DarkOakFenceGate; + mappings[828] = ItemType.PaleOakFenceGate; + mappings[829] = ItemType.MangroveFenceGate; + mappings[830] = ItemType.BambooFenceGate; + mappings[831] = ItemType.CrimsonFenceGate; + mappings[832] = ItemType.WarpedFenceGate; + mappings[833] = ItemType.PoweredRail; + mappings[834] = ItemType.DetectorRail; + mappings[835] = ItemType.Rail; + mappings[836] = ItemType.ActivatorRail; + mappings[837] = ItemType.Saddle; + mappings[838] = ItemType.WhiteHarness; + mappings[839] = ItemType.OrangeHarness; + mappings[840] = ItemType.MagentaHarness; + mappings[841] = ItemType.LightBlueHarness; + mappings[842] = ItemType.YellowHarness; + mappings[843] = ItemType.LimeHarness; + mappings[844] = ItemType.PinkHarness; + mappings[845] = ItemType.GrayHarness; + mappings[846] = ItemType.LightGrayHarness; + mappings[847] = ItemType.CyanHarness; + mappings[848] = ItemType.PurpleHarness; + mappings[849] = ItemType.BlueHarness; + mappings[850] = ItemType.BrownHarness; + mappings[851] = ItemType.GreenHarness; + mappings[852] = ItemType.RedHarness; + mappings[853] = ItemType.BlackHarness; + mappings[854] = ItemType.Minecart; + mappings[855] = ItemType.ChestMinecart; + mappings[856] = ItemType.FurnaceMinecart; + mappings[857] = ItemType.TntMinecart; + mappings[858] = ItemType.HopperMinecart; + mappings[859] = ItemType.CarrotOnAStick; + mappings[860] = ItemType.WarpedFungusOnAStick; + mappings[861] = ItemType.PhantomMembrane; + mappings[862] = ItemType.Elytra; + mappings[863] = ItemType.OakBoat; + mappings[864] = ItemType.OakChestBoat; + mappings[865] = ItemType.SpruceBoat; + mappings[866] = ItemType.SpruceChestBoat; + mappings[867] = ItemType.BirchBoat; + mappings[868] = ItemType.BirchChestBoat; + mappings[869] = ItemType.JungleBoat; + mappings[870] = ItemType.JungleChestBoat; + mappings[871] = ItemType.AcaciaBoat; + mappings[872] = ItemType.AcaciaChestBoat; + mappings[873] = ItemType.CherryBoat; + mappings[874] = ItemType.CherryChestBoat; + mappings[875] = ItemType.DarkOakBoat; + mappings[876] = ItemType.DarkOakChestBoat; + mappings[877] = ItemType.PaleOakBoat; + mappings[878] = ItemType.PaleOakChestBoat; + mappings[879] = ItemType.MangroveBoat; + mappings[880] = ItemType.MangroveChestBoat; + mappings[881] = ItemType.BambooRaft; + mappings[882] = ItemType.BambooChestRaft; + mappings[883] = ItemType.StructureBlock; + mappings[884] = ItemType.Jigsaw; + mappings[885] = ItemType.TestBlock; + mappings[886] = ItemType.TestInstanceBlock; + mappings[887] = ItemType.TurtleHelmet; + mappings[888] = ItemType.TurtleScute; + mappings[889] = ItemType.ArmadilloScute; + mappings[890] = ItemType.WolfArmor; + mappings[891] = ItemType.FlintAndSteel; + mappings[892] = ItemType.Bowl; + mappings[893] = ItemType.Apple; + mappings[894] = ItemType.Bow; + mappings[895] = ItemType.Arrow; + mappings[896] = ItemType.Coal; + mappings[897] = ItemType.Charcoal; + mappings[898] = ItemType.Diamond; + mappings[899] = ItemType.Emerald; + mappings[900] = ItemType.LapisLazuli; + mappings[901] = ItemType.Quartz; + mappings[902] = ItemType.AmethystShard; + mappings[903] = ItemType.RawIron; + mappings[904] = ItemType.IronIngot; + mappings[905] = ItemType.RawCopper; + mappings[906] = ItemType.CopperIngot; + mappings[907] = ItemType.RawGold; + mappings[908] = ItemType.GoldIngot; + mappings[909] = ItemType.NetheriteIngot; + mappings[910] = ItemType.NetheriteScrap; + mappings[911] = ItemType.WoodenSword; + mappings[912] = ItemType.WoodenShovel; + mappings[913] = ItemType.WoodenPickaxe; + mappings[914] = ItemType.WoodenAxe; + mappings[915] = ItemType.WoodenHoe; + mappings[916] = ItemType.CopperSword; + mappings[917] = ItemType.CopperShovel; + mappings[918] = ItemType.CopperPickaxe; + mappings[919] = ItemType.CopperAxe; + mappings[920] = ItemType.CopperHoe; + mappings[921] = ItemType.StoneSword; + mappings[922] = ItemType.StoneShovel; + mappings[923] = ItemType.StonePickaxe; + mappings[924] = ItemType.StoneAxe; + mappings[925] = ItemType.StoneHoe; + mappings[926] = ItemType.GoldenSword; + mappings[927] = ItemType.GoldenShovel; + mappings[928] = ItemType.GoldenPickaxe; + mappings[929] = ItemType.GoldenAxe; + mappings[930] = ItemType.GoldenHoe; + mappings[931] = ItemType.IronSword; + mappings[932] = ItemType.IronShovel; + mappings[933] = ItemType.IronPickaxe; + mappings[934] = ItemType.IronAxe; + mappings[935] = ItemType.IronHoe; + mappings[936] = ItemType.DiamondSword; + mappings[937] = ItemType.DiamondShovel; + mappings[938] = ItemType.DiamondPickaxe; + mappings[939] = ItemType.DiamondAxe; + mappings[940] = ItemType.DiamondHoe; + mappings[941] = ItemType.NetheriteSword; + mappings[942] = ItemType.NetheriteShovel; + mappings[943] = ItemType.NetheritePickaxe; + mappings[944] = ItemType.NetheriteAxe; + mappings[945] = ItemType.NetheriteHoe; + mappings[946] = ItemType.Stick; + mappings[947] = ItemType.MushroomStew; + mappings[948] = ItemType.String; + mappings[949] = ItemType.Feather; + mappings[950] = ItemType.Gunpowder; + mappings[951] = ItemType.WheatSeeds; + mappings[952] = ItemType.Wheat; + mappings[953] = ItemType.Bread; + mappings[954] = ItemType.LeatherHelmet; + mappings[955] = ItemType.LeatherChestplate; + mappings[956] = ItemType.LeatherLeggings; + mappings[957] = ItemType.LeatherBoots; + mappings[958] = ItemType.CopperHelmet; + mappings[959] = ItemType.CopperChestplate; + mappings[960] = ItemType.CopperLeggings; + mappings[961] = ItemType.CopperBoots; + mappings[962] = ItemType.ChainmailHelmet; + mappings[963] = ItemType.ChainmailChestplate; + mappings[964] = ItemType.ChainmailLeggings; + mappings[965] = ItemType.ChainmailBoots; + mappings[966] = ItemType.IronHelmet; + mappings[967] = ItemType.IronChestplate; + mappings[968] = ItemType.IronLeggings; + mappings[969] = ItemType.IronBoots; + mappings[970] = ItemType.DiamondHelmet; + mappings[971] = ItemType.DiamondChestplate; + mappings[972] = ItemType.DiamondLeggings; + mappings[973] = ItemType.DiamondBoots; + mappings[974] = ItemType.GoldenHelmet; + mappings[975] = ItemType.GoldenChestplate; + mappings[976] = ItemType.GoldenLeggings; + mappings[977] = ItemType.GoldenBoots; + mappings[978] = ItemType.NetheriteHelmet; + mappings[979] = ItemType.NetheriteChestplate; + mappings[980] = ItemType.NetheriteLeggings; + mappings[981] = ItemType.NetheriteBoots; + mappings[982] = ItemType.Flint; + mappings[983] = ItemType.Porkchop; + mappings[984] = ItemType.CookedPorkchop; + mappings[985] = ItemType.Painting; + mappings[986] = ItemType.GoldenApple; + mappings[987] = ItemType.EnchantedGoldenApple; + mappings[988] = ItemType.OakSign; + mappings[989] = ItemType.SpruceSign; + mappings[990] = ItemType.BirchSign; + mappings[991] = ItemType.JungleSign; + mappings[992] = ItemType.AcaciaSign; + mappings[993] = ItemType.CherrySign; + mappings[994] = ItemType.DarkOakSign; + mappings[995] = ItemType.PaleOakSign; + mappings[996] = ItemType.MangroveSign; + mappings[997] = ItemType.BambooSign; + mappings[998] = ItemType.CrimsonSign; + mappings[999] = ItemType.WarpedSign; + mappings[1000] = ItemType.OakHangingSign; + mappings[1001] = ItemType.SpruceHangingSign; + mappings[1002] = ItemType.BirchHangingSign; + mappings[1003] = ItemType.JungleHangingSign; + mappings[1004] = ItemType.AcaciaHangingSign; + mappings[1005] = ItemType.CherryHangingSign; + mappings[1006] = ItemType.DarkOakHangingSign; + mappings[1007] = ItemType.PaleOakHangingSign; + mappings[1008] = ItemType.MangroveHangingSign; + mappings[1009] = ItemType.BambooHangingSign; + mappings[1010] = ItemType.CrimsonHangingSign; + mappings[1011] = ItemType.WarpedHangingSign; + mappings[1012] = ItemType.Bucket; + mappings[1013] = ItemType.WaterBucket; + mappings[1014] = ItemType.LavaBucket; + mappings[1015] = ItemType.PowderSnowBucket; + mappings[1016] = ItemType.Snowball; + mappings[1017] = ItemType.Leather; + mappings[1018] = ItemType.MilkBucket; + mappings[1019] = ItemType.PufferfishBucket; + mappings[1020] = ItemType.SalmonBucket; + mappings[1021] = ItemType.CodBucket; + mappings[1022] = ItemType.TropicalFishBucket; + mappings[1023] = ItemType.AxolotlBucket; + mappings[1024] = ItemType.TadpoleBucket; + mappings[1025] = ItemType.Brick; + mappings[1026] = ItemType.ClayBall; + mappings[1027] = ItemType.DriedKelpBlock; + mappings[1028] = ItemType.Paper; + mappings[1029] = ItemType.Book; + mappings[1030] = ItemType.SlimeBall; + mappings[1031] = ItemType.Egg; + mappings[1032] = ItemType.BlueEgg; + mappings[1033] = ItemType.BrownEgg; + mappings[1034] = ItemType.Compass; + mappings[1035] = ItemType.RecoveryCompass; + mappings[1036] = ItemType.Bundle; + mappings[1037] = ItemType.WhiteBundle; + mappings[1038] = ItemType.OrangeBundle; + mappings[1039] = ItemType.MagentaBundle; + mappings[1040] = ItemType.LightBlueBundle; + mappings[1041] = ItemType.YellowBundle; + mappings[1042] = ItemType.LimeBundle; + mappings[1043] = ItemType.PinkBundle; + mappings[1044] = ItemType.GrayBundle; + mappings[1045] = ItemType.LightGrayBundle; + mappings[1046] = ItemType.CyanBundle; + mappings[1047] = ItemType.PurpleBundle; + mappings[1048] = ItemType.BlueBundle; + mappings[1049] = ItemType.BrownBundle; + mappings[1050] = ItemType.GreenBundle; + mappings[1051] = ItemType.RedBundle; + mappings[1052] = ItemType.BlackBundle; + mappings[1053] = ItemType.FishingRod; + mappings[1054] = ItemType.Clock; + mappings[1055] = ItemType.Spyglass; + mappings[1056] = ItemType.GlowstoneDust; + mappings[1057] = ItemType.Cod; + mappings[1058] = ItemType.Salmon; + mappings[1059] = ItemType.TropicalFish; + mappings[1060] = ItemType.Pufferfish; + mappings[1061] = ItemType.CookedCod; + mappings[1062] = ItemType.CookedSalmon; + mappings[1063] = ItemType.InkSac; + mappings[1064] = ItemType.GlowInkSac; + mappings[1065] = ItemType.CocoaBeans; + mappings[1066] = ItemType.WhiteDye; + mappings[1067] = ItemType.OrangeDye; + mappings[1068] = ItemType.MagentaDye; + mappings[1069] = ItemType.LightBlueDye; + mappings[1070] = ItemType.YellowDye; + mappings[1071] = ItemType.LimeDye; + mappings[1072] = ItemType.PinkDye; + mappings[1073] = ItemType.GrayDye; + mappings[1074] = ItemType.LightGrayDye; + mappings[1075] = ItemType.CyanDye; + mappings[1076] = ItemType.PurpleDye; + mappings[1077] = ItemType.BlueDye; + mappings[1078] = ItemType.BrownDye; + mappings[1079] = ItemType.GreenDye; + mappings[1080] = ItemType.RedDye; + mappings[1081] = ItemType.BlackDye; + mappings[1082] = ItemType.BoneMeal; + mappings[1083] = ItemType.Bone; + mappings[1084] = ItemType.Sugar; + mappings[1085] = ItemType.Cake; + mappings[1086] = ItemType.WhiteBed; + mappings[1087] = ItemType.OrangeBed; + mappings[1088] = ItemType.MagentaBed; + mappings[1089] = ItemType.LightBlueBed; + mappings[1090] = ItemType.YellowBed; + mappings[1091] = ItemType.LimeBed; + mappings[1092] = ItemType.PinkBed; + mappings[1093] = ItemType.GrayBed; + mappings[1094] = ItemType.LightGrayBed; + mappings[1095] = ItemType.CyanBed; + mappings[1096] = ItemType.PurpleBed; + mappings[1097] = ItemType.BlueBed; + mappings[1098] = ItemType.BrownBed; + mappings[1099] = ItemType.GreenBed; + mappings[1100] = ItemType.RedBed; + mappings[1101] = ItemType.BlackBed; + mappings[1102] = ItemType.Cookie; + mappings[1103] = ItemType.Crafter; + mappings[1104] = ItemType.FilledMap; + mappings[1105] = ItemType.Shears; + mappings[1106] = ItemType.MelonSlice; + mappings[1107] = ItemType.DriedKelp; + mappings[1108] = ItemType.PumpkinSeeds; + mappings[1109] = ItemType.MelonSeeds; + mappings[1110] = ItemType.Beef; + mappings[1111] = ItemType.CookedBeef; + mappings[1112] = ItemType.Chicken; + mappings[1113] = ItemType.CookedChicken; + mappings[1114] = ItemType.RottenFlesh; + mappings[1115] = ItemType.EnderPearl; + mappings[1116] = ItemType.BlazeRod; + mappings[1117] = ItemType.GhastTear; + mappings[1118] = ItemType.GoldNugget; + mappings[1119] = ItemType.NetherWart; + mappings[1120] = ItemType.GlassBottle; + mappings[1121] = ItemType.Potion; + mappings[1122] = ItemType.SpiderEye; + mappings[1123] = ItemType.FermentedSpiderEye; + mappings[1124] = ItemType.BlazePowder; + mappings[1125] = ItemType.MagmaCream; + mappings[1126] = ItemType.BrewingStand; + mappings[1127] = ItemType.Cauldron; + mappings[1128] = ItemType.EnderEye; + mappings[1129] = ItemType.GlisteringMelonSlice; + mappings[1130] = ItemType.ArmadilloSpawnEgg; + mappings[1131] = ItemType.AllaySpawnEgg; + mappings[1132] = ItemType.AxolotlSpawnEgg; + mappings[1133] = ItemType.BatSpawnEgg; + mappings[1134] = ItemType.BeeSpawnEgg; + mappings[1135] = ItemType.BlazeSpawnEgg; + mappings[1136] = ItemType.BoggedSpawnEgg; + mappings[1137] = ItemType.BreezeSpawnEgg; + mappings[1138] = ItemType.CatSpawnEgg; + mappings[1139] = ItemType.CamelSpawnEgg; + mappings[1140] = ItemType.CaveSpiderSpawnEgg; + mappings[1141] = ItemType.ChickenSpawnEgg; + mappings[1142] = ItemType.CodSpawnEgg; + mappings[1143] = ItemType.CopperGolemSpawnEgg; + mappings[1144] = ItemType.CowSpawnEgg; + mappings[1145] = ItemType.CreeperSpawnEgg; + mappings[1146] = ItemType.DolphinSpawnEgg; + mappings[1147] = ItemType.DonkeySpawnEgg; + mappings[1148] = ItemType.DrownedSpawnEgg; + mappings[1149] = ItemType.ElderGuardianSpawnEgg; + mappings[1150] = ItemType.EnderDragonSpawnEgg; + mappings[1151] = ItemType.EndermanSpawnEgg; + mappings[1152] = ItemType.EndermiteSpawnEgg; + mappings[1153] = ItemType.EvokerSpawnEgg; + mappings[1154] = ItemType.FoxSpawnEgg; + mappings[1155] = ItemType.FrogSpawnEgg; + mappings[1156] = ItemType.GhastSpawnEgg; + mappings[1157] = ItemType.HappyGhastSpawnEgg; + mappings[1158] = ItemType.GlowSquidSpawnEgg; + mappings[1159] = ItemType.GoatSpawnEgg; + mappings[1160] = ItemType.GuardianSpawnEgg; + mappings[1161] = ItemType.HoglinSpawnEgg; + mappings[1162] = ItemType.HorseSpawnEgg; + mappings[1163] = ItemType.HuskSpawnEgg; + mappings[1164] = ItemType.IronGolemSpawnEgg; + mappings[1165] = ItemType.LlamaSpawnEgg; + mappings[1166] = ItemType.MagmaCubeSpawnEgg; + mappings[1167] = ItemType.MooshroomSpawnEgg; + mappings[1168] = ItemType.MuleSpawnEgg; + mappings[1169] = ItemType.OcelotSpawnEgg; + mappings[1170] = ItemType.PandaSpawnEgg; + mappings[1171] = ItemType.ParrotSpawnEgg; + mappings[1172] = ItemType.PhantomSpawnEgg; + mappings[1173] = ItemType.PigSpawnEgg; + mappings[1174] = ItemType.PiglinSpawnEgg; + mappings[1175] = ItemType.PiglinBruteSpawnEgg; + mappings[1176] = ItemType.PillagerSpawnEgg; + mappings[1177] = ItemType.PolarBearSpawnEgg; + mappings[1178] = ItemType.PufferfishSpawnEgg; + mappings[1179] = ItemType.RabbitSpawnEgg; + mappings[1180] = ItemType.RavagerSpawnEgg; + mappings[1181] = ItemType.SalmonSpawnEgg; + mappings[1182] = ItemType.SheepSpawnEgg; + mappings[1183] = ItemType.ShulkerSpawnEgg; + mappings[1184] = ItemType.SilverfishSpawnEgg; + mappings[1185] = ItemType.SkeletonSpawnEgg; + mappings[1186] = ItemType.SkeletonHorseSpawnEgg; + mappings[1187] = ItemType.SlimeSpawnEgg; + mappings[1188] = ItemType.SnifferSpawnEgg; + mappings[1189] = ItemType.SnowGolemSpawnEgg; + mappings[1190] = ItemType.SpiderSpawnEgg; + mappings[1191] = ItemType.SquidSpawnEgg; + mappings[1192] = ItemType.StraySpawnEgg; + mappings[1193] = ItemType.StriderSpawnEgg; + mappings[1194] = ItemType.TadpoleSpawnEgg; + mappings[1195] = ItemType.TraderLlamaSpawnEgg; + mappings[1196] = ItemType.TropicalFishSpawnEgg; + mappings[1197] = ItemType.TurtleSpawnEgg; + mappings[1198] = ItemType.VexSpawnEgg; + mappings[1199] = ItemType.VillagerSpawnEgg; + mappings[1200] = ItemType.VindicatorSpawnEgg; + mappings[1201] = ItemType.WanderingTraderSpawnEgg; + mappings[1202] = ItemType.WardenSpawnEgg; + mappings[1203] = ItemType.WitchSpawnEgg; + mappings[1204] = ItemType.WitherSpawnEgg; + mappings[1205] = ItemType.WitherSkeletonSpawnEgg; + mappings[1206] = ItemType.WolfSpawnEgg; + mappings[1207] = ItemType.ZoglinSpawnEgg; + mappings[1208] = ItemType.CreakingSpawnEgg; + mappings[1209] = ItemType.ZombieSpawnEgg; + mappings[1210] = ItemType.ZombieHorseSpawnEgg; + mappings[1211] = ItemType.ZombieVillagerSpawnEgg; + mappings[1212] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1213] = ItemType.ExperienceBottle; + mappings[1214] = ItemType.FireCharge; + mappings[1215] = ItemType.WindCharge; + mappings[1216] = ItemType.WritableBook; + mappings[1217] = ItemType.WrittenBook; + mappings[1218] = ItemType.BreezeRod; + mappings[1219] = ItemType.Mace; + mappings[1220] = ItemType.ItemFrame; + mappings[1221] = ItemType.GlowItemFrame; + mappings[1222] = ItemType.FlowerPot; + mappings[1223] = ItemType.Carrot; + mappings[1224] = ItemType.Potato; + mappings[1225] = ItemType.BakedPotato; + mappings[1226] = ItemType.PoisonousPotato; + mappings[1227] = ItemType.Map; + mappings[1228] = ItemType.GoldenCarrot; + mappings[1229] = ItemType.SkeletonSkull; + mappings[1230] = ItemType.WitherSkeletonSkull; + mappings[1231] = ItemType.PlayerHead; + mappings[1232] = ItemType.ZombieHead; + mappings[1233] = ItemType.CreeperHead; + mappings[1234] = ItemType.DragonHead; + mappings[1235] = ItemType.PiglinHead; + mappings[1236] = ItemType.NetherStar; + mappings[1237] = ItemType.PumpkinPie; + mappings[1238] = ItemType.FireworkRocket; + mappings[1239] = ItemType.FireworkStar; + mappings[1240] = ItemType.EnchantedBook; + mappings[1241] = ItemType.NetherBrick; + mappings[1242] = ItemType.ResinBrick; + mappings[1243] = ItemType.PrismarineShard; + mappings[1244] = ItemType.PrismarineCrystals; + mappings[1245] = ItemType.Rabbit; + mappings[1246] = ItemType.CookedRabbit; + mappings[1247] = ItemType.RabbitStew; + mappings[1248] = ItemType.RabbitFoot; + mappings[1249] = ItemType.RabbitHide; + mappings[1250] = ItemType.ArmorStand; + mappings[1251] = ItemType.CopperHorseArmor; + mappings[1252] = ItemType.IronHorseArmor; + mappings[1253] = ItemType.GoldenHorseArmor; + mappings[1254] = ItemType.DiamondHorseArmor; + mappings[1255] = ItemType.LeatherHorseArmor; + mappings[1256] = ItemType.Lead; + mappings[1257] = ItemType.NameTag; + mappings[1258] = ItemType.CommandBlockMinecart; + mappings[1259] = ItemType.Mutton; + mappings[1260] = ItemType.CookedMutton; + mappings[1261] = ItemType.WhiteBanner; + mappings[1262] = ItemType.OrangeBanner; + mappings[1263] = ItemType.MagentaBanner; + mappings[1264] = ItemType.LightBlueBanner; + mappings[1265] = ItemType.YellowBanner; + mappings[1266] = ItemType.LimeBanner; + mappings[1267] = ItemType.PinkBanner; + mappings[1268] = ItemType.GrayBanner; + mappings[1269] = ItemType.LightGrayBanner; + mappings[1270] = ItemType.CyanBanner; + mappings[1271] = ItemType.PurpleBanner; + mappings[1272] = ItemType.BlueBanner; + mappings[1273] = ItemType.BrownBanner; + mappings[1274] = ItemType.GreenBanner; + mappings[1275] = ItemType.RedBanner; + mappings[1276] = ItemType.BlackBanner; + mappings[1277] = ItemType.EndCrystal; + mappings[1278] = ItemType.ChorusFruit; + mappings[1279] = ItemType.PoppedChorusFruit; + mappings[1280] = ItemType.TorchflowerSeeds; + mappings[1281] = ItemType.PitcherPod; + mappings[1282] = ItemType.Beetroot; + mappings[1283] = ItemType.BeetrootSeeds; + mappings[1284] = ItemType.BeetrootSoup; + mappings[1285] = ItemType.DragonBreath; + mappings[1286] = ItemType.SplashPotion; + mappings[1287] = ItemType.SpectralArrow; + mappings[1288] = ItemType.TippedArrow; + mappings[1289] = ItemType.LingeringPotion; + mappings[1290] = ItemType.Shield; + mappings[1291] = ItemType.TotemOfUndying; + mappings[1292] = ItemType.ShulkerShell; + mappings[1293] = ItemType.IronNugget; + mappings[1294] = ItemType.CopperNugget; + mappings[1295] = ItemType.KnowledgeBook; + mappings[1296] = ItemType.DebugStick; + mappings[1297] = ItemType.MusicDisc13; + mappings[1298] = ItemType.MusicDiscCat; + mappings[1299] = ItemType.MusicDiscBlocks; + mappings[1300] = ItemType.MusicDiscChirp; + mappings[1301] = ItemType.MusicDiscCreator; + mappings[1302] = ItemType.MusicDiscCreatorMusicBox; + mappings[1303] = ItemType.MusicDiscFar; + mappings[1304] = ItemType.MusicDiscLavaChicken; + mappings[1305] = ItemType.MusicDiscMall; + mappings[1306] = ItemType.MusicDiscMellohi; + mappings[1307] = ItemType.MusicDiscStal; + mappings[1308] = ItemType.MusicDiscStrad; + mappings[1309] = ItemType.MusicDiscWard; + mappings[1310] = ItemType.MusicDisc11; + mappings[1311] = ItemType.MusicDiscWait; + mappings[1312] = ItemType.MusicDiscOtherside; + mappings[1313] = ItemType.MusicDiscRelic; + mappings[1314] = ItemType.MusicDisc5; + mappings[1315] = ItemType.MusicDiscPigstep; + mappings[1316] = ItemType.MusicDiscPrecipice; + mappings[1317] = ItemType.MusicDiscTears; + mappings[1318] = ItemType.DiscFragment5; + mappings[1319] = ItemType.Trident; + mappings[1320] = ItemType.NautilusShell; + mappings[1321] = ItemType.HeartOfTheSea; + mappings[1322] = ItemType.Crossbow; + mappings[1323] = ItemType.SuspiciousStew; + mappings[1324] = ItemType.Loom; + mappings[1325] = ItemType.FlowerBannerPattern; + mappings[1326] = ItemType.CreeperBannerPattern; + mappings[1327] = ItemType.SkullBannerPattern; + mappings[1328] = ItemType.MojangBannerPattern; + mappings[1329] = ItemType.GlobeBannerPattern; + mappings[1330] = ItemType.PiglinBannerPattern; + mappings[1331] = ItemType.FlowBannerPattern; + mappings[1332] = ItemType.GusterBannerPattern; + mappings[1333] = ItemType.FieldMasonedBannerPattern; + mappings[1334] = ItemType.BordureIndentedBannerPattern; + mappings[1335] = ItemType.GoatHorn; + mappings[1336] = ItemType.Composter; + mappings[1337] = ItemType.Barrel; + mappings[1338] = ItemType.Smoker; + mappings[1339] = ItemType.BlastFurnace; + mappings[1340] = ItemType.CartographyTable; + mappings[1341] = ItemType.FletchingTable; + mappings[1342] = ItemType.Grindstone; + mappings[1343] = ItemType.SmithingTable; + mappings[1344] = ItemType.Stonecutter; + mappings[1345] = ItemType.Bell; + mappings[1346] = ItemType.Lantern; + mappings[1347] = ItemType.SoulLantern; + mappings[1348] = ItemType.CopperLantern; + mappings[1349] = ItemType.ExposedCopperLantern; + mappings[1350] = ItemType.WeatheredCopperLantern; + mappings[1351] = ItemType.OxidizedCopperLantern; + mappings[1352] = ItemType.WaxedCopperLantern; + mappings[1353] = ItemType.WaxedExposedCopperLantern; + mappings[1354] = ItemType.WaxedWeatheredCopperLantern; + mappings[1355] = ItemType.WaxedOxidizedCopperLantern; + mappings[1356] = ItemType.SweetBerries; + mappings[1357] = ItemType.GlowBerries; + mappings[1358] = ItemType.Campfire; + mappings[1359] = ItemType.SoulCampfire; + mappings[1360] = ItemType.Shroomlight; + mappings[1361] = ItemType.Honeycomb; + mappings[1362] = ItemType.BeeNest; + mappings[1363] = ItemType.Beehive; + mappings[1364] = ItemType.HoneyBottle; + mappings[1365] = ItemType.HoneycombBlock; + mappings[1366] = ItemType.Lodestone; + mappings[1367] = ItemType.CryingObsidian; + mappings[1368] = ItemType.Blackstone; + mappings[1369] = ItemType.BlackstoneSlab; + mappings[1370] = ItemType.BlackstoneStairs; + mappings[1371] = ItemType.GildedBlackstone; + mappings[1372] = ItemType.PolishedBlackstone; + mappings[1373] = ItemType.PolishedBlackstoneSlab; + mappings[1374] = ItemType.PolishedBlackstoneStairs; + mappings[1375] = ItemType.ChiseledPolishedBlackstone; + mappings[1376] = ItemType.PolishedBlackstoneBricks; + mappings[1377] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1378] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1379] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1380] = ItemType.RespawnAnchor; + mappings[1381] = ItemType.Candle; + mappings[1382] = ItemType.WhiteCandle; + mappings[1383] = ItemType.OrangeCandle; + mappings[1384] = ItemType.MagentaCandle; + mappings[1385] = ItemType.LightBlueCandle; + mappings[1386] = ItemType.YellowCandle; + mappings[1387] = ItemType.LimeCandle; + mappings[1388] = ItemType.PinkCandle; + mappings[1389] = ItemType.GrayCandle; + mappings[1390] = ItemType.LightGrayCandle; + mappings[1391] = ItemType.CyanCandle; + mappings[1392] = ItemType.PurpleCandle; + mappings[1393] = ItemType.BlueCandle; + mappings[1394] = ItemType.BrownCandle; + mappings[1395] = ItemType.GreenCandle; + mappings[1396] = ItemType.RedCandle; + mappings[1397] = ItemType.BlackCandle; + mappings[1398] = ItemType.SmallAmethystBud; + mappings[1399] = ItemType.MediumAmethystBud; + mappings[1400] = ItemType.LargeAmethystBud; + mappings[1401] = ItemType.AmethystCluster; + mappings[1402] = ItemType.PointedDripstone; + mappings[1403] = ItemType.OchreFroglight; + mappings[1404] = ItemType.VerdantFroglight; + mappings[1405] = ItemType.PearlescentFroglight; + mappings[1406] = ItemType.Frogspawn; + mappings[1407] = ItemType.EchoShard; + mappings[1408] = ItemType.Brush; + mappings[1409] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1410] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1411] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1412] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1413] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1414] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1415] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1416] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1417] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1418] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1419] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1420] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1421] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1422] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1423] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1424] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1425] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1426] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1427] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1428] = ItemType.AnglerPotterySherd; + mappings[1429] = ItemType.ArcherPotterySherd; + mappings[1430] = ItemType.ArmsUpPotterySherd; + mappings[1431] = ItemType.BladePotterySherd; + mappings[1432] = ItemType.BrewerPotterySherd; + mappings[1433] = ItemType.BurnPotterySherd; + mappings[1434] = ItemType.DangerPotterySherd; + mappings[1435] = ItemType.ExplorerPotterySherd; + mappings[1436] = ItemType.FlowPotterySherd; + mappings[1437] = ItemType.FriendPotterySherd; + mappings[1438] = ItemType.GusterPotterySherd; + mappings[1439] = ItemType.HeartPotterySherd; + mappings[1440] = ItemType.HeartbreakPotterySherd; + mappings[1441] = ItemType.HowlPotterySherd; + mappings[1442] = ItemType.MinerPotterySherd; + mappings[1443] = ItemType.MournerPotterySherd; + mappings[1444] = ItemType.PlentyPotterySherd; + mappings[1445] = ItemType.PrizePotterySherd; + mappings[1446] = ItemType.ScrapePotterySherd; + mappings[1447] = ItemType.SheafPotterySherd; + mappings[1448] = ItemType.ShelterPotterySherd; + mappings[1449] = ItemType.SkullPotterySherd; + mappings[1450] = ItemType.SnortPotterySherd; + mappings[1451] = ItemType.CopperGrate; + mappings[1452] = ItemType.ExposedCopperGrate; + mappings[1453] = ItemType.WeatheredCopperGrate; + mappings[1454] = ItemType.OxidizedCopperGrate; + mappings[1455] = ItemType.WaxedCopperGrate; + mappings[1456] = ItemType.WaxedExposedCopperGrate; + mappings[1457] = ItemType.WaxedWeatheredCopperGrate; + mappings[1458] = ItemType.WaxedOxidizedCopperGrate; + mappings[1459] = ItemType.CopperBulb; + mappings[1460] = ItemType.ExposedCopperBulb; + mappings[1461] = ItemType.WeatheredCopperBulb; + mappings[1462] = ItemType.OxidizedCopperBulb; + mappings[1463] = ItemType.WaxedCopperBulb; + mappings[1464] = ItemType.WaxedExposedCopperBulb; + mappings[1465] = ItemType.WaxedWeatheredCopperBulb; + mappings[1466] = ItemType.WaxedOxidizedCopperBulb; + mappings[1467] = ItemType.CopperChest; + mappings[1468] = ItemType.ExposedCopperChest; + mappings[1469] = ItemType.WeatheredCopperChest; + mappings[1470] = ItemType.OxidizedCopperChest; + mappings[1471] = ItemType.WaxedCopperChest; + mappings[1472] = ItemType.WaxedExposedCopperChest; + mappings[1473] = ItemType.WaxedWeatheredCopperChest; + mappings[1474] = ItemType.WaxedOxidizedCopperChest; + mappings[1475] = ItemType.CopperGolemStatue; + mappings[1476] = ItemType.ExposedCopperGolemStatue; + mappings[1477] = ItemType.WeatheredCopperGolemStatue; + mappings[1478] = ItemType.OxidizedCopperGolemStatue; + mappings[1479] = ItemType.WaxedCopperGolemStatue; + mappings[1480] = ItemType.WaxedExposedCopperGolemStatue; + mappings[1481] = ItemType.WaxedWeatheredCopperGolemStatue; + mappings[1482] = ItemType.WaxedOxidizedCopperGolemStatue; + mappings[1483] = ItemType.TrialSpawner; + mappings[1484] = ItemType.TrialKey; + mappings[1485] = ItemType.OminousTrialKey; + mappings[1486] = ItemType.Vault; + mappings[1487] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette18.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette18.cs index 0d8cc231..57f942d9 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette18.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette18.cs @@ -70,7 +70,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[1769472] = ItemType.PoweredRail; mappings[1835008] = ItemType.DetectorRail; mappings[1900544] = ItemType.StickyPiston; - mappings[2031617] = ItemType.Grass; + mappings[2031617] = ItemType.ShortGrass; mappings[2031618] = ItemType.Fern; mappings[2097152] = ItemType.DeadBush; mappings[2162688] = ItemType.Piston; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette19.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette19.cs index 332ff5b7..c4251f62 100644 --- a/MinecraftClient/Inventory/ItemPalettes/ItemPalette19.cs +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette19.cs @@ -66,7 +66,7 @@ namespace MinecraftClient.Inventory.ItemPalettes mappings[1769472] = ItemType.PoweredRail; mappings[1835008] = ItemType.DetectorRail; mappings[1900544] = ItemType.StickyPiston; - mappings[2031617] = ItemType.Grass; + mappings[2031617] = ItemType.ShortGrass; mappings[2031618] = ItemType.Fern; mappings[2097152] = ItemType.DeadBush; mappings[2293760] = ItemType.WhiteWool; diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette261.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette261.cs new file mode 100644 index 00000000..4acbe644 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette261.cs @@ -0,0 +1,1524 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette261 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette261() + { + mappings[0] = ItemType.Air; + mappings[1] = ItemType.Stone; + mappings[2] = ItemType.Granite; + mappings[3] = ItemType.PolishedGranite; + mappings[4] = ItemType.Diorite; + mappings[5] = ItemType.PolishedDiorite; + mappings[6] = ItemType.Andesite; + mappings[7] = ItemType.PolishedAndesite; + mappings[8] = ItemType.Deepslate; + mappings[9] = ItemType.CobbledDeepslate; + mappings[10] = ItemType.PolishedDeepslate; + mappings[11] = ItemType.Calcite; + mappings[12] = ItemType.Tuff; + mappings[13] = ItemType.TuffSlab; + mappings[14] = ItemType.TuffStairs; + mappings[15] = ItemType.TuffWall; + mappings[16] = ItemType.ChiseledTuff; + mappings[17] = ItemType.PolishedTuff; + mappings[18] = ItemType.PolishedTuffSlab; + mappings[19] = ItemType.PolishedTuffStairs; + mappings[20] = ItemType.PolishedTuffWall; + mappings[21] = ItemType.TuffBricks; + mappings[22] = ItemType.TuffBrickSlab; + mappings[23] = ItemType.TuffBrickStairs; + mappings[24] = ItemType.TuffBrickWall; + mappings[25] = ItemType.ChiseledTuffBricks; + mappings[26] = ItemType.DripstoneBlock; + mappings[27] = ItemType.GrassBlock; + mappings[28] = ItemType.Dirt; + mappings[29] = ItemType.CoarseDirt; + mappings[30] = ItemType.Podzol; + mappings[31] = ItemType.RootedDirt; + mappings[32] = ItemType.Mud; + mappings[33] = ItemType.CrimsonNylium; + mappings[34] = ItemType.WarpedNylium; + mappings[35] = ItemType.Cobblestone; + mappings[36] = ItemType.OakPlanks; + mappings[37] = ItemType.SprucePlanks; + mappings[38] = ItemType.BirchPlanks; + mappings[39] = ItemType.JunglePlanks; + mappings[40] = ItemType.AcaciaPlanks; + mappings[41] = ItemType.CherryPlanks; + mappings[42] = ItemType.DarkOakPlanks; + mappings[43] = ItemType.PaleOakPlanks; + mappings[44] = ItemType.MangrovePlanks; + mappings[45] = ItemType.BambooPlanks; + mappings[46] = ItemType.CrimsonPlanks; + mappings[47] = ItemType.WarpedPlanks; + mappings[48] = ItemType.BambooMosaic; + mappings[49] = ItemType.OakSapling; + mappings[50] = ItemType.SpruceSapling; + mappings[51] = ItemType.BirchSapling; + mappings[52] = ItemType.JungleSapling; + mappings[53] = ItemType.AcaciaSapling; + mappings[54] = ItemType.CherrySapling; + mappings[55] = ItemType.DarkOakSapling; + mappings[56] = ItemType.PaleOakSapling; + mappings[57] = ItemType.MangrovePropagule; + mappings[58] = ItemType.Bedrock; + mappings[59] = ItemType.Sand; + mappings[60] = ItemType.SuspiciousSand; + mappings[61] = ItemType.SuspiciousGravel; + mappings[62] = ItemType.RedSand; + mappings[63] = ItemType.Gravel; + mappings[64] = ItemType.CoalOre; + mappings[65] = ItemType.DeepslateCoalOre; + mappings[66] = ItemType.IronOre; + mappings[67] = ItemType.DeepslateIronOre; + mappings[68] = ItemType.CopperOre; + mappings[69] = ItemType.DeepslateCopperOre; + mappings[70] = ItemType.GoldOre; + mappings[71] = ItemType.DeepslateGoldOre; + mappings[72] = ItemType.RedstoneOre; + mappings[73] = ItemType.DeepslateRedstoneOre; + mappings[74] = ItemType.EmeraldOre; + mappings[75] = ItemType.DeepslateEmeraldOre; + mappings[76] = ItemType.LapisOre; + mappings[77] = ItemType.DeepslateLapisOre; + mappings[78] = ItemType.DiamondOre; + mappings[79] = ItemType.DeepslateDiamondOre; + mappings[80] = ItemType.NetherGoldOre; + mappings[81] = ItemType.NetherQuartzOre; + mappings[82] = ItemType.AncientDebris; + mappings[83] = ItemType.CoalBlock; + mappings[84] = ItemType.RawIronBlock; + mappings[85] = ItemType.RawCopperBlock; + mappings[86] = ItemType.RawGoldBlock; + mappings[87] = ItemType.HeavyCore; + mappings[88] = ItemType.AmethystBlock; + mappings[89] = ItemType.BuddingAmethyst; + mappings[90] = ItemType.IronBlock; + mappings[91] = ItemType.CopperBlock; + mappings[92] = ItemType.GoldBlock; + mappings[93] = ItemType.DiamondBlock; + mappings[94] = ItemType.NetheriteBlock; + mappings[95] = ItemType.ExposedCopper; + mappings[96] = ItemType.WeatheredCopper; + mappings[97] = ItemType.OxidizedCopper; + mappings[98] = ItemType.ChiseledCopper; + mappings[99] = ItemType.ExposedChiseledCopper; + mappings[100] = ItemType.WeatheredChiseledCopper; + mappings[101] = ItemType.OxidizedChiseledCopper; + mappings[102] = ItemType.CutCopper; + mappings[103] = ItemType.ExposedCutCopper; + mappings[104] = ItemType.WeatheredCutCopper; + mappings[105] = ItemType.OxidizedCutCopper; + mappings[106] = ItemType.CutCopperStairs; + mappings[107] = ItemType.ExposedCutCopperStairs; + mappings[108] = ItemType.WeatheredCutCopperStairs; + mappings[109] = ItemType.OxidizedCutCopperStairs; + mappings[110] = ItemType.CutCopperSlab; + mappings[111] = ItemType.ExposedCutCopperSlab; + mappings[112] = ItemType.WeatheredCutCopperSlab; + mappings[113] = ItemType.OxidizedCutCopperSlab; + mappings[114] = ItemType.WaxedCopperBlock; + mappings[115] = ItemType.WaxedExposedCopper; + mappings[116] = ItemType.WaxedWeatheredCopper; + mappings[117] = ItemType.WaxedOxidizedCopper; + mappings[118] = ItemType.WaxedChiseledCopper; + mappings[119] = ItemType.WaxedExposedChiseledCopper; + mappings[120] = ItemType.WaxedWeatheredChiseledCopper; + mappings[121] = ItemType.WaxedOxidizedChiseledCopper; + mappings[122] = ItemType.WaxedCutCopper; + mappings[123] = ItemType.WaxedExposedCutCopper; + mappings[124] = ItemType.WaxedWeatheredCutCopper; + mappings[125] = ItemType.WaxedOxidizedCutCopper; + mappings[126] = ItemType.WaxedCutCopperStairs; + mappings[127] = ItemType.WaxedExposedCutCopperStairs; + mappings[128] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[129] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[130] = ItemType.WaxedCutCopperSlab; + mappings[131] = ItemType.WaxedExposedCutCopperSlab; + mappings[132] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[133] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[134] = ItemType.OakLog; + mappings[135] = ItemType.SpruceLog; + mappings[136] = ItemType.BirchLog; + mappings[137] = ItemType.JungleLog; + mappings[138] = ItemType.AcaciaLog; + mappings[139] = ItemType.CherryLog; + mappings[140] = ItemType.PaleOakLog; + mappings[141] = ItemType.DarkOakLog; + mappings[142] = ItemType.MangroveLog; + mappings[143] = ItemType.MangroveRoots; + mappings[144] = ItemType.MuddyMangroveRoots; + mappings[145] = ItemType.CrimsonStem; + mappings[146] = ItemType.WarpedStem; + mappings[147] = ItemType.BambooBlock; + mappings[148] = ItemType.StrippedOakLog; + mappings[149] = ItemType.StrippedSpruceLog; + mappings[150] = ItemType.StrippedBirchLog; + mappings[151] = ItemType.StrippedJungleLog; + mappings[152] = ItemType.StrippedAcaciaLog; + mappings[153] = ItemType.StrippedCherryLog; + mappings[154] = ItemType.StrippedDarkOakLog; + mappings[155] = ItemType.StrippedPaleOakLog; + mappings[156] = ItemType.StrippedMangroveLog; + mappings[157] = ItemType.StrippedCrimsonStem; + mappings[158] = ItemType.StrippedWarpedStem; + mappings[159] = ItemType.StrippedOakWood; + mappings[160] = ItemType.StrippedSpruceWood; + mappings[161] = ItemType.StrippedBirchWood; + mappings[162] = ItemType.StrippedJungleWood; + mappings[163] = ItemType.StrippedAcaciaWood; + mappings[164] = ItemType.StrippedCherryWood; + mappings[165] = ItemType.StrippedDarkOakWood; + mappings[166] = ItemType.StrippedPaleOakWood; + mappings[167] = ItemType.StrippedMangroveWood; + mappings[168] = ItemType.StrippedCrimsonHyphae; + mappings[169] = ItemType.StrippedWarpedHyphae; + mappings[170] = ItemType.StrippedBambooBlock; + mappings[171] = ItemType.OakWood; + mappings[172] = ItemType.SpruceWood; + mappings[173] = ItemType.BirchWood; + mappings[174] = ItemType.JungleWood; + mappings[175] = ItemType.AcaciaWood; + mappings[176] = ItemType.CherryWood; + mappings[177] = ItemType.PaleOakWood; + mappings[178] = ItemType.DarkOakWood; + mappings[179] = ItemType.MangroveWood; + mappings[180] = ItemType.CrimsonHyphae; + mappings[181] = ItemType.WarpedHyphae; + mappings[182] = ItemType.OakLeaves; + mappings[183] = ItemType.SpruceLeaves; + mappings[184] = ItemType.BirchLeaves; + mappings[185] = ItemType.JungleLeaves; + mappings[186] = ItemType.AcaciaLeaves; + mappings[187] = ItemType.CherryLeaves; + mappings[188] = ItemType.DarkOakLeaves; + mappings[189] = ItemType.PaleOakLeaves; + mappings[190] = ItemType.MangroveLeaves; + mappings[191] = ItemType.AzaleaLeaves; + mappings[192] = ItemType.FloweringAzaleaLeaves; + mappings[193] = ItemType.Sponge; + mappings[194] = ItemType.WetSponge; + mappings[195] = ItemType.Glass; + mappings[196] = ItemType.TintedGlass; + mappings[197] = ItemType.LapisBlock; + mappings[198] = ItemType.Sandstone; + mappings[199] = ItemType.ChiseledSandstone; + mappings[200] = ItemType.CutSandstone; + mappings[201] = ItemType.Cobweb; + mappings[202] = ItemType.ShortGrass; + mappings[203] = ItemType.Fern; + mappings[204] = ItemType.Bush; + mappings[205] = ItemType.Azalea; + mappings[206] = ItemType.FloweringAzalea; + mappings[207] = ItemType.DeadBush; + mappings[208] = ItemType.FireflyBush; + mappings[209] = ItemType.ShortDryGrass; + mappings[210] = ItemType.TallDryGrass; + mappings[211] = ItemType.Seagrass; + mappings[212] = ItemType.SeaPickle; + mappings[213] = ItemType.WhiteWool; + mappings[214] = ItemType.OrangeWool; + mappings[215] = ItemType.MagentaWool; + mappings[216] = ItemType.LightBlueWool; + mappings[217] = ItemType.YellowWool; + mappings[218] = ItemType.LimeWool; + mappings[219] = ItemType.PinkWool; + mappings[220] = ItemType.GrayWool; + mappings[221] = ItemType.LightGrayWool; + mappings[222] = ItemType.CyanWool; + mappings[223] = ItemType.PurpleWool; + mappings[224] = ItemType.BlueWool; + mappings[225] = ItemType.BrownWool; + mappings[226] = ItemType.GreenWool; + mappings[227] = ItemType.RedWool; + mappings[228] = ItemType.BlackWool; + mappings[229] = ItemType.Dandelion; + mappings[230] = ItemType.GoldenDandelion; + mappings[231] = ItemType.OpenEyeblossom; + mappings[232] = ItemType.ClosedEyeblossom; + mappings[233] = ItemType.Poppy; + mappings[234] = ItemType.BlueOrchid; + mappings[235] = ItemType.Allium; + mappings[236] = ItemType.AzureBluet; + mappings[237] = ItemType.RedTulip; + mappings[238] = ItemType.OrangeTulip; + mappings[239] = ItemType.WhiteTulip; + mappings[240] = ItemType.PinkTulip; + mappings[241] = ItemType.OxeyeDaisy; + mappings[242] = ItemType.Cornflower; + mappings[243] = ItemType.LilyOfTheValley; + mappings[244] = ItemType.WitherRose; + mappings[245] = ItemType.Torchflower; + mappings[246] = ItemType.PitcherPlant; + mappings[247] = ItemType.SporeBlossom; + mappings[248] = ItemType.BrownMushroom; + mappings[249] = ItemType.RedMushroom; + mappings[250] = ItemType.CrimsonFungus; + mappings[251] = ItemType.WarpedFungus; + mappings[252] = ItemType.CrimsonRoots; + mappings[253] = ItemType.WarpedRoots; + mappings[254] = ItemType.NetherSprouts; + mappings[255] = ItemType.WeepingVines; + mappings[256] = ItemType.TwistingVines; + mappings[257] = ItemType.SugarCane; + mappings[258] = ItemType.Kelp; + mappings[259] = ItemType.PinkPetals; + mappings[260] = ItemType.Wildflowers; + mappings[261] = ItemType.LeafLitter; + mappings[262] = ItemType.MossCarpet; + mappings[263] = ItemType.MossBlock; + mappings[264] = ItemType.PaleMossCarpet; + mappings[265] = ItemType.PaleHangingMoss; + mappings[266] = ItemType.PaleMossBlock; + mappings[267] = ItemType.HangingRoots; + mappings[268] = ItemType.BigDripleaf; + mappings[269] = ItemType.SmallDripleaf; + mappings[270] = ItemType.Bamboo; + mappings[271] = ItemType.OakSlab; + mappings[272] = ItemType.SpruceSlab; + mappings[273] = ItemType.BirchSlab; + mappings[274] = ItemType.JungleSlab; + mappings[275] = ItemType.AcaciaSlab; + mappings[276] = ItemType.CherrySlab; + mappings[277] = ItemType.DarkOakSlab; + mappings[278] = ItemType.PaleOakSlab; + mappings[279] = ItemType.MangroveSlab; + mappings[280] = ItemType.BambooSlab; + mappings[281] = ItemType.BambooMosaicSlab; + mappings[282] = ItemType.CrimsonSlab; + mappings[283] = ItemType.WarpedSlab; + mappings[284] = ItemType.StoneSlab; + mappings[285] = ItemType.SmoothStoneSlab; + mappings[286] = ItemType.SandstoneSlab; + mappings[287] = ItemType.CutSandstoneSlab; + mappings[288] = ItemType.PetrifiedOakSlab; + mappings[289] = ItemType.CobblestoneSlab; + mappings[290] = ItemType.BrickSlab; + mappings[291] = ItemType.StoneBrickSlab; + mappings[292] = ItemType.MudBrickSlab; + mappings[293] = ItemType.NetherBrickSlab; + mappings[294] = ItemType.QuartzSlab; + mappings[295] = ItemType.RedSandstoneSlab; + mappings[296] = ItemType.CutRedSandstoneSlab; + mappings[297] = ItemType.PurpurSlab; + mappings[298] = ItemType.PrismarineSlab; + mappings[299] = ItemType.PrismarineBrickSlab; + mappings[300] = ItemType.DarkPrismarineSlab; + mappings[301] = ItemType.SmoothQuartz; + mappings[302] = ItemType.SmoothRedSandstone; + mappings[303] = ItemType.SmoothSandstone; + mappings[304] = ItemType.SmoothStone; + mappings[305] = ItemType.Bricks; + mappings[306] = ItemType.AcaciaShelf; + mappings[307] = ItemType.BambooShelf; + mappings[308] = ItemType.BirchShelf; + mappings[309] = ItemType.CherryShelf; + mappings[310] = ItemType.CrimsonShelf; + mappings[311] = ItemType.DarkOakShelf; + mappings[312] = ItemType.JungleShelf; + mappings[313] = ItemType.MangroveShelf; + mappings[314] = ItemType.OakShelf; + mappings[315] = ItemType.PaleOakShelf; + mappings[316] = ItemType.SpruceShelf; + mappings[317] = ItemType.WarpedShelf; + mappings[318] = ItemType.Bookshelf; + mappings[319] = ItemType.ChiseledBookshelf; + mappings[320] = ItemType.DecoratedPot; + mappings[321] = ItemType.MossyCobblestone; + mappings[322] = ItemType.Obsidian; + mappings[323] = ItemType.Torch; + mappings[324] = ItemType.EndRod; + mappings[325] = ItemType.ChorusPlant; + mappings[326] = ItemType.ChorusFlower; + mappings[327] = ItemType.PurpurBlock; + mappings[328] = ItemType.PurpurPillar; + mappings[329] = ItemType.PurpurStairs; + mappings[330] = ItemType.Spawner; + mappings[331] = ItemType.CreakingHeart; + mappings[332] = ItemType.Chest; + mappings[333] = ItemType.CraftingTable; + mappings[334] = ItemType.Farmland; + mappings[335] = ItemType.Furnace; + mappings[336] = ItemType.Ladder; + mappings[337] = ItemType.CobblestoneStairs; + mappings[338] = ItemType.Snow; + mappings[339] = ItemType.Ice; + mappings[340] = ItemType.SnowBlock; + mappings[341] = ItemType.Cactus; + mappings[342] = ItemType.CactusFlower; + mappings[343] = ItemType.Clay; + mappings[344] = ItemType.Jukebox; + mappings[345] = ItemType.OakFence; + mappings[346] = ItemType.SpruceFence; + mappings[347] = ItemType.BirchFence; + mappings[348] = ItemType.JungleFence; + mappings[349] = ItemType.AcaciaFence; + mappings[350] = ItemType.CherryFence; + mappings[351] = ItemType.DarkOakFence; + mappings[352] = ItemType.PaleOakFence; + mappings[353] = ItemType.MangroveFence; + mappings[354] = ItemType.BambooFence; + mappings[355] = ItemType.CrimsonFence; + mappings[356] = ItemType.WarpedFence; + mappings[357] = ItemType.Pumpkin; + mappings[358] = ItemType.CarvedPumpkin; + mappings[359] = ItemType.JackOLantern; + mappings[360] = ItemType.Netherrack; + mappings[361] = ItemType.SoulSand; + mappings[362] = ItemType.SoulSoil; + mappings[363] = ItemType.Basalt; + mappings[364] = ItemType.PolishedBasalt; + mappings[365] = ItemType.SmoothBasalt; + mappings[366] = ItemType.SoulTorch; + mappings[367] = ItemType.CopperTorch; + mappings[368] = ItemType.Glowstone; + mappings[369] = ItemType.InfestedStone; + mappings[370] = ItemType.InfestedCobblestone; + mappings[371] = ItemType.InfestedStoneBricks; + mappings[372] = ItemType.InfestedMossyStoneBricks; + mappings[373] = ItemType.InfestedCrackedStoneBricks; + mappings[374] = ItemType.InfestedChiseledStoneBricks; + mappings[375] = ItemType.InfestedDeepslate; + mappings[376] = ItemType.StoneBricks; + mappings[377] = ItemType.MossyStoneBricks; + mappings[378] = ItemType.CrackedStoneBricks; + mappings[379] = ItemType.ChiseledStoneBricks; + mappings[380] = ItemType.PackedMud; + mappings[381] = ItemType.MudBricks; + mappings[382] = ItemType.DeepslateBricks; + mappings[383] = ItemType.CrackedDeepslateBricks; + mappings[384] = ItemType.DeepslateTiles; + mappings[385] = ItemType.CrackedDeepslateTiles; + mappings[386] = ItemType.ChiseledDeepslate; + mappings[387] = ItemType.ReinforcedDeepslate; + mappings[388] = ItemType.BrownMushroomBlock; + mappings[389] = ItemType.RedMushroomBlock; + mappings[390] = ItemType.MushroomStem; + mappings[391] = ItemType.IronBars; + mappings[392] = ItemType.CopperBars; + mappings[393] = ItemType.ExposedCopperBars; + mappings[394] = ItemType.WeatheredCopperBars; + mappings[395] = ItemType.OxidizedCopperBars; + mappings[396] = ItemType.WaxedCopperBars; + mappings[397] = ItemType.WaxedExposedCopperBars; + mappings[398] = ItemType.WaxedWeatheredCopperBars; + mappings[399] = ItemType.WaxedOxidizedCopperBars; + mappings[400] = ItemType.IronChain; + mappings[401] = ItemType.CopperChain; + mappings[402] = ItemType.ExposedCopperChain; + mappings[403] = ItemType.WeatheredCopperChain; + mappings[404] = ItemType.OxidizedCopperChain; + mappings[405] = ItemType.WaxedCopperChain; + mappings[406] = ItemType.WaxedExposedCopperChain; + mappings[407] = ItemType.WaxedWeatheredCopperChain; + mappings[408] = ItemType.WaxedOxidizedCopperChain; + mappings[409] = ItemType.GlassPane; + mappings[410] = ItemType.Melon; + mappings[411] = ItemType.Vine; + mappings[412] = ItemType.GlowLichen; + mappings[413] = ItemType.ResinClump; + mappings[414] = ItemType.ResinBlock; + mappings[415] = ItemType.ResinBricks; + mappings[416] = ItemType.ResinBrickStairs; + mappings[417] = ItemType.ResinBrickSlab; + mappings[418] = ItemType.ResinBrickWall; + mappings[419] = ItemType.ChiseledResinBricks; + mappings[420] = ItemType.BrickStairs; + mappings[421] = ItemType.StoneBrickStairs; + mappings[422] = ItemType.MudBrickStairs; + mappings[423] = ItemType.Mycelium; + mappings[424] = ItemType.LilyPad; + mappings[425] = ItemType.NetherBricks; + mappings[426] = ItemType.CrackedNetherBricks; + mappings[427] = ItemType.ChiseledNetherBricks; + mappings[428] = ItemType.NetherBrickFence; + mappings[429] = ItemType.NetherBrickStairs; + mappings[430] = ItemType.Sculk; + mappings[431] = ItemType.SculkVein; + mappings[432] = ItemType.SculkCatalyst; + mappings[433] = ItemType.SculkShrieker; + mappings[434] = ItemType.EnchantingTable; + mappings[435] = ItemType.EndPortalFrame; + mappings[436] = ItemType.EndStone; + mappings[437] = ItemType.EndStoneBricks; + mappings[438] = ItemType.DragonEgg; + mappings[439] = ItemType.SandstoneStairs; + mappings[440] = ItemType.EnderChest; + mappings[441] = ItemType.EmeraldBlock; + mappings[442] = ItemType.OakStairs; + mappings[443] = ItemType.SpruceStairs; + mappings[444] = ItemType.BirchStairs; + mappings[445] = ItemType.JungleStairs; + mappings[446] = ItemType.AcaciaStairs; + mappings[447] = ItemType.CherryStairs; + mappings[448] = ItemType.DarkOakStairs; + mappings[449] = ItemType.PaleOakStairs; + mappings[450] = ItemType.MangroveStairs; + mappings[451] = ItemType.BambooStairs; + mappings[452] = ItemType.BambooMosaicStairs; + mappings[453] = ItemType.CrimsonStairs; + mappings[454] = ItemType.WarpedStairs; + mappings[455] = ItemType.CommandBlock; + mappings[456] = ItemType.Beacon; + mappings[457] = ItemType.CobblestoneWall; + mappings[458] = ItemType.MossyCobblestoneWall; + mappings[459] = ItemType.BrickWall; + mappings[460] = ItemType.PrismarineWall; + mappings[461] = ItemType.RedSandstoneWall; + mappings[462] = ItemType.MossyStoneBrickWall; + mappings[463] = ItemType.GraniteWall; + mappings[464] = ItemType.StoneBrickWall; + mappings[465] = ItemType.MudBrickWall; + mappings[466] = ItemType.NetherBrickWall; + mappings[467] = ItemType.AndesiteWall; + mappings[468] = ItemType.RedNetherBrickWall; + mappings[469] = ItemType.SandstoneWall; + mappings[470] = ItemType.EndStoneBrickWall; + mappings[471] = ItemType.DioriteWall; + mappings[472] = ItemType.BlackstoneWall; + mappings[473] = ItemType.PolishedBlackstoneWall; + mappings[474] = ItemType.PolishedBlackstoneBrickWall; + mappings[475] = ItemType.CobbledDeepslateWall; + mappings[476] = ItemType.PolishedDeepslateWall; + mappings[477] = ItemType.DeepslateBrickWall; + mappings[478] = ItemType.DeepslateTileWall; + mappings[479] = ItemType.Anvil; + mappings[480] = ItemType.ChippedAnvil; + mappings[481] = ItemType.DamagedAnvil; + mappings[482] = ItemType.ChiseledQuartzBlock; + mappings[483] = ItemType.QuartzBlock; + mappings[484] = ItemType.QuartzBricks; + mappings[485] = ItemType.QuartzPillar; + mappings[486] = ItemType.QuartzStairs; + mappings[487] = ItemType.WhiteTerracotta; + mappings[488] = ItemType.OrangeTerracotta; + mappings[489] = ItemType.MagentaTerracotta; + mappings[490] = ItemType.LightBlueTerracotta; + mappings[491] = ItemType.YellowTerracotta; + mappings[492] = ItemType.LimeTerracotta; + mappings[493] = ItemType.PinkTerracotta; + mappings[494] = ItemType.GrayTerracotta; + mappings[495] = ItemType.LightGrayTerracotta; + mappings[496] = ItemType.CyanTerracotta; + mappings[497] = ItemType.PurpleTerracotta; + mappings[498] = ItemType.BlueTerracotta; + mappings[499] = ItemType.BrownTerracotta; + mappings[500] = ItemType.GreenTerracotta; + mappings[501] = ItemType.RedTerracotta; + mappings[502] = ItemType.BlackTerracotta; + mappings[503] = ItemType.Barrier; + mappings[504] = ItemType.Light; + mappings[505] = ItemType.HayBlock; + mappings[506] = ItemType.WhiteCarpet; + mappings[507] = ItemType.OrangeCarpet; + mappings[508] = ItemType.MagentaCarpet; + mappings[509] = ItemType.LightBlueCarpet; + mappings[510] = ItemType.YellowCarpet; + mappings[511] = ItemType.LimeCarpet; + mappings[512] = ItemType.PinkCarpet; + mappings[513] = ItemType.GrayCarpet; + mappings[514] = ItemType.LightGrayCarpet; + mappings[515] = ItemType.CyanCarpet; + mappings[516] = ItemType.PurpleCarpet; + mappings[517] = ItemType.BlueCarpet; + mappings[518] = ItemType.BrownCarpet; + mappings[519] = ItemType.GreenCarpet; + mappings[520] = ItemType.RedCarpet; + mappings[521] = ItemType.BlackCarpet; + mappings[522] = ItemType.Terracotta; + mappings[523] = ItemType.PackedIce; + mappings[524] = ItemType.DirtPath; + mappings[525] = ItemType.Sunflower; + mappings[526] = ItemType.Lilac; + mappings[527] = ItemType.RoseBush; + mappings[528] = ItemType.Peony; + mappings[529] = ItemType.TallGrass; + mappings[530] = ItemType.LargeFern; + mappings[531] = ItemType.WhiteStainedGlass; + mappings[532] = ItemType.OrangeStainedGlass; + mappings[533] = ItemType.MagentaStainedGlass; + mappings[534] = ItemType.LightBlueStainedGlass; + mappings[535] = ItemType.YellowStainedGlass; + mappings[536] = ItemType.LimeStainedGlass; + mappings[537] = ItemType.PinkStainedGlass; + mappings[538] = ItemType.GrayStainedGlass; + mappings[539] = ItemType.LightGrayStainedGlass; + mappings[540] = ItemType.CyanStainedGlass; + mappings[541] = ItemType.PurpleStainedGlass; + mappings[542] = ItemType.BlueStainedGlass; + mappings[543] = ItemType.BrownStainedGlass; + mappings[544] = ItemType.GreenStainedGlass; + mappings[545] = ItemType.RedStainedGlass; + mappings[546] = ItemType.BlackStainedGlass; + mappings[547] = ItemType.WhiteStainedGlassPane; + mappings[548] = ItemType.OrangeStainedGlassPane; + mappings[549] = ItemType.MagentaStainedGlassPane; + mappings[550] = ItemType.LightBlueStainedGlassPane; + mappings[551] = ItemType.YellowStainedGlassPane; + mappings[552] = ItemType.LimeStainedGlassPane; + mappings[553] = ItemType.PinkStainedGlassPane; + mappings[554] = ItemType.GrayStainedGlassPane; + mappings[555] = ItemType.LightGrayStainedGlassPane; + mappings[556] = ItemType.CyanStainedGlassPane; + mappings[557] = ItemType.PurpleStainedGlassPane; + mappings[558] = ItemType.BlueStainedGlassPane; + mappings[559] = ItemType.BrownStainedGlassPane; + mappings[560] = ItemType.GreenStainedGlassPane; + mappings[561] = ItemType.RedStainedGlassPane; + mappings[562] = ItemType.BlackStainedGlassPane; + mappings[563] = ItemType.Prismarine; + mappings[564] = ItemType.PrismarineBricks; + mappings[565] = ItemType.DarkPrismarine; + mappings[566] = ItemType.PrismarineStairs; + mappings[567] = ItemType.PrismarineBrickStairs; + mappings[568] = ItemType.DarkPrismarineStairs; + mappings[569] = ItemType.SeaLantern; + mappings[570] = ItemType.RedSandstone; + mappings[571] = ItemType.ChiseledRedSandstone; + mappings[572] = ItemType.CutRedSandstone; + mappings[573] = ItemType.RedSandstoneStairs; + mappings[574] = ItemType.RepeatingCommandBlock; + mappings[575] = ItemType.ChainCommandBlock; + mappings[576] = ItemType.MagmaBlock; + mappings[577] = ItemType.NetherWartBlock; + mappings[578] = ItemType.WarpedWartBlock; + mappings[579] = ItemType.RedNetherBricks; + mappings[580] = ItemType.BoneBlock; + mappings[581] = ItemType.StructureVoid; + mappings[582] = ItemType.ShulkerBox; + mappings[583] = ItemType.WhiteShulkerBox; + mappings[584] = ItemType.OrangeShulkerBox; + mappings[585] = ItemType.MagentaShulkerBox; + mappings[586] = ItemType.LightBlueShulkerBox; + mappings[587] = ItemType.YellowShulkerBox; + mappings[588] = ItemType.LimeShulkerBox; + mappings[589] = ItemType.PinkShulkerBox; + mappings[590] = ItemType.GrayShulkerBox; + mappings[591] = ItemType.LightGrayShulkerBox; + mappings[592] = ItemType.CyanShulkerBox; + mappings[593] = ItemType.PurpleShulkerBox; + mappings[594] = ItemType.BlueShulkerBox; + mappings[595] = ItemType.BrownShulkerBox; + mappings[596] = ItemType.GreenShulkerBox; + mappings[597] = ItemType.RedShulkerBox; + mappings[598] = ItemType.BlackShulkerBox; + mappings[599] = ItemType.WhiteGlazedTerracotta; + mappings[600] = ItemType.OrangeGlazedTerracotta; + mappings[601] = ItemType.MagentaGlazedTerracotta; + mappings[602] = ItemType.LightBlueGlazedTerracotta; + mappings[603] = ItemType.YellowGlazedTerracotta; + mappings[604] = ItemType.LimeGlazedTerracotta; + mappings[605] = ItemType.PinkGlazedTerracotta; + mappings[606] = ItemType.GrayGlazedTerracotta; + mappings[607] = ItemType.LightGrayGlazedTerracotta; + mappings[608] = ItemType.CyanGlazedTerracotta; + mappings[609] = ItemType.PurpleGlazedTerracotta; + mappings[610] = ItemType.BlueGlazedTerracotta; + mappings[611] = ItemType.BrownGlazedTerracotta; + mappings[612] = ItemType.GreenGlazedTerracotta; + mappings[613] = ItemType.RedGlazedTerracotta; + mappings[614] = ItemType.BlackGlazedTerracotta; + mappings[615] = ItemType.WhiteConcrete; + mappings[616] = ItemType.OrangeConcrete; + mappings[617] = ItemType.MagentaConcrete; + mappings[618] = ItemType.LightBlueConcrete; + mappings[619] = ItemType.YellowConcrete; + mappings[620] = ItemType.LimeConcrete; + mappings[621] = ItemType.PinkConcrete; + mappings[622] = ItemType.GrayConcrete; + mappings[623] = ItemType.LightGrayConcrete; + mappings[624] = ItemType.CyanConcrete; + mappings[625] = ItemType.PurpleConcrete; + mappings[626] = ItemType.BlueConcrete; + mappings[627] = ItemType.BrownConcrete; + mappings[628] = ItemType.GreenConcrete; + mappings[629] = ItemType.RedConcrete; + mappings[630] = ItemType.BlackConcrete; + mappings[631] = ItemType.WhiteConcretePowder; + mappings[632] = ItemType.OrangeConcretePowder; + mappings[633] = ItemType.MagentaConcretePowder; + mappings[634] = ItemType.LightBlueConcretePowder; + mappings[635] = ItemType.YellowConcretePowder; + mappings[636] = ItemType.LimeConcretePowder; + mappings[637] = ItemType.PinkConcretePowder; + mappings[638] = ItemType.GrayConcretePowder; + mappings[639] = ItemType.LightGrayConcretePowder; + mappings[640] = ItemType.CyanConcretePowder; + mappings[641] = ItemType.PurpleConcretePowder; + mappings[642] = ItemType.BlueConcretePowder; + mappings[643] = ItemType.BrownConcretePowder; + mappings[644] = ItemType.GreenConcretePowder; + mappings[645] = ItemType.RedConcretePowder; + mappings[646] = ItemType.BlackConcretePowder; + mappings[647] = ItemType.TurtleEgg; + mappings[648] = ItemType.SnifferEgg; + mappings[649] = ItemType.DriedGhast; + mappings[650] = ItemType.DeadTubeCoralBlock; + mappings[651] = ItemType.DeadBrainCoralBlock; + mappings[652] = ItemType.DeadBubbleCoralBlock; + mappings[653] = ItemType.DeadFireCoralBlock; + mappings[654] = ItemType.DeadHornCoralBlock; + mappings[655] = ItemType.TubeCoralBlock; + mappings[656] = ItemType.BrainCoralBlock; + mappings[657] = ItemType.BubbleCoralBlock; + mappings[658] = ItemType.FireCoralBlock; + mappings[659] = ItemType.HornCoralBlock; + mappings[660] = ItemType.TubeCoral; + mappings[661] = ItemType.BrainCoral; + mappings[662] = ItemType.BubbleCoral; + mappings[663] = ItemType.FireCoral; + mappings[664] = ItemType.HornCoral; + mappings[665] = ItemType.DeadBrainCoral; + mappings[666] = ItemType.DeadBubbleCoral; + mappings[667] = ItemType.DeadFireCoral; + mappings[668] = ItemType.DeadHornCoral; + mappings[669] = ItemType.DeadTubeCoral; + mappings[670] = ItemType.TubeCoralFan; + mappings[671] = ItemType.BrainCoralFan; + mappings[672] = ItemType.BubbleCoralFan; + mappings[673] = ItemType.FireCoralFan; + mappings[674] = ItemType.HornCoralFan; + mappings[675] = ItemType.DeadTubeCoralFan; + mappings[676] = ItemType.DeadBrainCoralFan; + mappings[677] = ItemType.DeadBubbleCoralFan; + mappings[678] = ItemType.DeadFireCoralFan; + mappings[679] = ItemType.DeadHornCoralFan; + mappings[680] = ItemType.BlueIce; + mappings[681] = ItemType.Conduit; + mappings[682] = ItemType.PolishedGraniteStairs; + mappings[683] = ItemType.SmoothRedSandstoneStairs; + mappings[684] = ItemType.MossyStoneBrickStairs; + mappings[685] = ItemType.PolishedDioriteStairs; + mappings[686] = ItemType.MossyCobblestoneStairs; + mappings[687] = ItemType.EndStoneBrickStairs; + mappings[688] = ItemType.StoneStairs; + mappings[689] = ItemType.SmoothSandstoneStairs; + mappings[690] = ItemType.SmoothQuartzStairs; + mappings[691] = ItemType.GraniteStairs; + mappings[692] = ItemType.AndesiteStairs; + mappings[693] = ItemType.RedNetherBrickStairs; + mappings[694] = ItemType.PolishedAndesiteStairs; + mappings[695] = ItemType.DioriteStairs; + mappings[696] = ItemType.CobbledDeepslateStairs; + mappings[697] = ItemType.PolishedDeepslateStairs; + mappings[698] = ItemType.DeepslateBrickStairs; + mappings[699] = ItemType.DeepslateTileStairs; + mappings[700] = ItemType.PolishedGraniteSlab; + mappings[701] = ItemType.SmoothRedSandstoneSlab; + mappings[702] = ItemType.MossyStoneBrickSlab; + mappings[703] = ItemType.PolishedDioriteSlab; + mappings[704] = ItemType.MossyCobblestoneSlab; + mappings[705] = ItemType.EndStoneBrickSlab; + mappings[706] = ItemType.SmoothSandstoneSlab; + mappings[707] = ItemType.SmoothQuartzSlab; + mappings[708] = ItemType.GraniteSlab; + mappings[709] = ItemType.AndesiteSlab; + mappings[710] = ItemType.RedNetherBrickSlab; + mappings[711] = ItemType.PolishedAndesiteSlab; + mappings[712] = ItemType.DioriteSlab; + mappings[713] = ItemType.CobbledDeepslateSlab; + mappings[714] = ItemType.PolishedDeepslateSlab; + mappings[715] = ItemType.DeepslateBrickSlab; + mappings[716] = ItemType.DeepslateTileSlab; + mappings[717] = ItemType.Scaffolding; + mappings[718] = ItemType.Redstone; + mappings[719] = ItemType.RedstoneTorch; + mappings[720] = ItemType.RedstoneBlock; + mappings[721] = ItemType.Repeater; + mappings[722] = ItemType.Comparator; + mappings[723] = ItemType.Piston; + mappings[724] = ItemType.StickyPiston; + mappings[725] = ItemType.SlimeBlock; + mappings[726] = ItemType.HoneyBlock; + mappings[727] = ItemType.Observer; + mappings[728] = ItemType.Hopper; + mappings[729] = ItemType.Dispenser; + mappings[730] = ItemType.Dropper; + mappings[731] = ItemType.Lectern; + mappings[732] = ItemType.Target; + mappings[733] = ItemType.Lever; + mappings[734] = ItemType.LightningRod; + mappings[735] = ItemType.ExposedLightningRod; + mappings[736] = ItemType.WeatheredLightningRod; + mappings[737] = ItemType.OxidizedLightningRod; + mappings[738] = ItemType.WaxedLightningRod; + mappings[739] = ItemType.WaxedExposedLightningRod; + mappings[740] = ItemType.WaxedWeatheredLightningRod; + mappings[741] = ItemType.WaxedOxidizedLightningRod; + mappings[742] = ItemType.DaylightDetector; + mappings[743] = ItemType.SculkSensor; + mappings[744] = ItemType.CalibratedSculkSensor; + mappings[745] = ItemType.TripwireHook; + mappings[746] = ItemType.TrappedChest; + mappings[747] = ItemType.Tnt; + mappings[748] = ItemType.RedstoneLamp; + mappings[749] = ItemType.NoteBlock; + mappings[750] = ItemType.StoneButton; + mappings[751] = ItemType.PolishedBlackstoneButton; + mappings[752] = ItemType.OakButton; + mappings[753] = ItemType.SpruceButton; + mappings[754] = ItemType.BirchButton; + mappings[755] = ItemType.JungleButton; + mappings[756] = ItemType.AcaciaButton; + mappings[757] = ItemType.CherryButton; + mappings[758] = ItemType.DarkOakButton; + mappings[759] = ItemType.PaleOakButton; + mappings[760] = ItemType.MangroveButton; + mappings[761] = ItemType.BambooButton; + mappings[762] = ItemType.CrimsonButton; + mappings[763] = ItemType.WarpedButton; + mappings[764] = ItemType.StonePressurePlate; + mappings[765] = ItemType.PolishedBlackstonePressurePlate; + mappings[766] = ItemType.LightWeightedPressurePlate; + mappings[767] = ItemType.HeavyWeightedPressurePlate; + mappings[768] = ItemType.OakPressurePlate; + mappings[769] = ItemType.SprucePressurePlate; + mappings[770] = ItemType.BirchPressurePlate; + mappings[771] = ItemType.JunglePressurePlate; + mappings[772] = ItemType.AcaciaPressurePlate; + mappings[773] = ItemType.CherryPressurePlate; + mappings[774] = ItemType.DarkOakPressurePlate; + mappings[775] = ItemType.PaleOakPressurePlate; + mappings[776] = ItemType.MangrovePressurePlate; + mappings[777] = ItemType.BambooPressurePlate; + mappings[778] = ItemType.CrimsonPressurePlate; + mappings[779] = ItemType.WarpedPressurePlate; + mappings[780] = ItemType.IronDoor; + mappings[781] = ItemType.OakDoor; + mappings[782] = ItemType.SpruceDoor; + mappings[783] = ItemType.BirchDoor; + mappings[784] = ItemType.JungleDoor; + mappings[785] = ItemType.AcaciaDoor; + mappings[786] = ItemType.CherryDoor; + mappings[787] = ItemType.DarkOakDoor; + mappings[788] = ItemType.PaleOakDoor; + mappings[789] = ItemType.MangroveDoor; + mappings[790] = ItemType.BambooDoor; + mappings[791] = ItemType.CrimsonDoor; + mappings[792] = ItemType.WarpedDoor; + mappings[793] = ItemType.CopperDoor; + mappings[794] = ItemType.ExposedCopperDoor; + mappings[795] = ItemType.WeatheredCopperDoor; + mappings[796] = ItemType.OxidizedCopperDoor; + mappings[797] = ItemType.WaxedCopperDoor; + mappings[798] = ItemType.WaxedExposedCopperDoor; + mappings[799] = ItemType.WaxedWeatheredCopperDoor; + mappings[800] = ItemType.WaxedOxidizedCopperDoor; + mappings[801] = ItemType.IronTrapdoor; + mappings[802] = ItemType.OakTrapdoor; + mappings[803] = ItemType.SpruceTrapdoor; + mappings[804] = ItemType.BirchTrapdoor; + mappings[805] = ItemType.JungleTrapdoor; + mappings[806] = ItemType.AcaciaTrapdoor; + mappings[807] = ItemType.CherryTrapdoor; + mappings[808] = ItemType.DarkOakTrapdoor; + mappings[809] = ItemType.PaleOakTrapdoor; + mappings[810] = ItemType.MangroveTrapdoor; + mappings[811] = ItemType.BambooTrapdoor; + mappings[812] = ItemType.CrimsonTrapdoor; + mappings[813] = ItemType.WarpedTrapdoor; + mappings[814] = ItemType.CopperTrapdoor; + mappings[815] = ItemType.ExposedCopperTrapdoor; + mappings[816] = ItemType.WeatheredCopperTrapdoor; + mappings[817] = ItemType.OxidizedCopperTrapdoor; + mappings[818] = ItemType.WaxedCopperTrapdoor; + mappings[819] = ItemType.WaxedExposedCopperTrapdoor; + mappings[820] = ItemType.WaxedWeatheredCopperTrapdoor; + mappings[821] = ItemType.WaxedOxidizedCopperTrapdoor; + mappings[822] = ItemType.OakFenceGate; + mappings[823] = ItemType.SpruceFenceGate; + mappings[824] = ItemType.BirchFenceGate; + mappings[825] = ItemType.JungleFenceGate; + mappings[826] = ItemType.AcaciaFenceGate; + mappings[827] = ItemType.CherryFenceGate; + mappings[828] = ItemType.DarkOakFenceGate; + mappings[829] = ItemType.PaleOakFenceGate; + mappings[830] = ItemType.MangroveFenceGate; + mappings[831] = ItemType.BambooFenceGate; + mappings[832] = ItemType.CrimsonFenceGate; + mappings[833] = ItemType.WarpedFenceGate; + mappings[834] = ItemType.PoweredRail; + mappings[835] = ItemType.DetectorRail; + mappings[836] = ItemType.Rail; + mappings[837] = ItemType.ActivatorRail; + mappings[838] = ItemType.Saddle; + mappings[839] = ItemType.WhiteHarness; + mappings[840] = ItemType.OrangeHarness; + mappings[841] = ItemType.MagentaHarness; + mappings[842] = ItemType.LightBlueHarness; + mappings[843] = ItemType.YellowHarness; + mappings[844] = ItemType.LimeHarness; + mappings[845] = ItemType.PinkHarness; + mappings[846] = ItemType.GrayHarness; + mappings[847] = ItemType.LightGrayHarness; + mappings[848] = ItemType.CyanHarness; + mappings[849] = ItemType.PurpleHarness; + mappings[850] = ItemType.BlueHarness; + mappings[851] = ItemType.BrownHarness; + mappings[852] = ItemType.GreenHarness; + mappings[853] = ItemType.RedHarness; + mappings[854] = ItemType.BlackHarness; + mappings[855] = ItemType.Minecart; + mappings[856] = ItemType.ChestMinecart; + mappings[857] = ItemType.FurnaceMinecart; + mappings[858] = ItemType.TntMinecart; + mappings[859] = ItemType.HopperMinecart; + mappings[860] = ItemType.CarrotOnAStick; + mappings[861] = ItemType.WarpedFungusOnAStick; + mappings[862] = ItemType.PhantomMembrane; + mappings[863] = ItemType.Elytra; + mappings[864] = ItemType.OakBoat; + mappings[865] = ItemType.OakChestBoat; + mappings[866] = ItemType.SpruceBoat; + mappings[867] = ItemType.SpruceChestBoat; + mappings[868] = ItemType.BirchBoat; + mappings[869] = ItemType.BirchChestBoat; + mappings[870] = ItemType.JungleBoat; + mappings[871] = ItemType.JungleChestBoat; + mappings[872] = ItemType.AcaciaBoat; + mappings[873] = ItemType.AcaciaChestBoat; + mappings[874] = ItemType.CherryBoat; + mappings[875] = ItemType.CherryChestBoat; + mappings[876] = ItemType.DarkOakBoat; + mappings[877] = ItemType.DarkOakChestBoat; + mappings[878] = ItemType.PaleOakBoat; + mappings[879] = ItemType.PaleOakChestBoat; + mappings[880] = ItemType.MangroveBoat; + mappings[881] = ItemType.MangroveChestBoat; + mappings[882] = ItemType.BambooRaft; + mappings[883] = ItemType.BambooChestRaft; + mappings[884] = ItemType.StructureBlock; + mappings[885] = ItemType.Jigsaw; + mappings[886] = ItemType.TestBlock; + mappings[887] = ItemType.TestInstanceBlock; + mappings[888] = ItemType.TurtleHelmet; + mappings[889] = ItemType.TurtleScute; + mappings[890] = ItemType.ArmadilloScute; + mappings[891] = ItemType.WolfArmor; + mappings[892] = ItemType.FlintAndSteel; + mappings[893] = ItemType.Bowl; + mappings[894] = ItemType.Apple; + mappings[895] = ItemType.Bow; + mappings[896] = ItemType.Arrow; + mappings[897] = ItemType.Coal; + mappings[898] = ItemType.Charcoal; + mappings[899] = ItemType.Diamond; + mappings[900] = ItemType.Emerald; + mappings[901] = ItemType.LapisLazuli; + mappings[902] = ItemType.Quartz; + mappings[903] = ItemType.AmethystShard; + mappings[904] = ItemType.RawIron; + mappings[905] = ItemType.IronIngot; + mappings[906] = ItemType.RawCopper; + mappings[907] = ItemType.CopperIngot; + mappings[908] = ItemType.RawGold; + mappings[909] = ItemType.GoldIngot; + mappings[910] = ItemType.NetheriteIngot; + mappings[911] = ItemType.NetheriteScrap; + mappings[912] = ItemType.WoodenSword; + mappings[913] = ItemType.WoodenShovel; + mappings[914] = ItemType.WoodenPickaxe; + mappings[915] = ItemType.WoodenAxe; + mappings[916] = ItemType.WoodenHoe; + mappings[917] = ItemType.CopperSword; + mappings[918] = ItemType.CopperShovel; + mappings[919] = ItemType.CopperPickaxe; + mappings[920] = ItemType.CopperAxe; + mappings[921] = ItemType.CopperHoe; + mappings[922] = ItemType.StoneSword; + mappings[923] = ItemType.StoneShovel; + mappings[924] = ItemType.StonePickaxe; + mappings[925] = ItemType.StoneAxe; + mappings[926] = ItemType.StoneHoe; + mappings[927] = ItemType.GoldenSword; + mappings[928] = ItemType.GoldenShovel; + mappings[929] = ItemType.GoldenPickaxe; + mappings[930] = ItemType.GoldenAxe; + mappings[931] = ItemType.GoldenHoe; + mappings[932] = ItemType.IronSword; + mappings[933] = ItemType.IronShovel; + mappings[934] = ItemType.IronPickaxe; + mappings[935] = ItemType.IronAxe; + mappings[936] = ItemType.IronHoe; + mappings[937] = ItemType.DiamondSword; + mappings[938] = ItemType.DiamondShovel; + mappings[939] = ItemType.DiamondPickaxe; + mappings[940] = ItemType.DiamondAxe; + mappings[941] = ItemType.DiamondHoe; + mappings[942] = ItemType.NetheriteSword; + mappings[943] = ItemType.NetheriteShovel; + mappings[944] = ItemType.NetheritePickaxe; + mappings[945] = ItemType.NetheriteAxe; + mappings[946] = ItemType.NetheriteHoe; + mappings[947] = ItemType.Stick; + mappings[948] = ItemType.MushroomStew; + mappings[949] = ItemType.String; + mappings[950] = ItemType.Feather; + mappings[951] = ItemType.Gunpowder; + mappings[952] = ItemType.WheatSeeds; + mappings[953] = ItemType.Wheat; + mappings[954] = ItemType.Bread; + mappings[955] = ItemType.LeatherHelmet; + mappings[956] = ItemType.LeatherChestplate; + mappings[957] = ItemType.LeatherLeggings; + mappings[958] = ItemType.LeatherBoots; + mappings[959] = ItemType.CopperHelmet; + mappings[960] = ItemType.CopperChestplate; + mappings[961] = ItemType.CopperLeggings; + mappings[962] = ItemType.CopperBoots; + mappings[963] = ItemType.ChainmailHelmet; + mappings[964] = ItemType.ChainmailChestplate; + mappings[965] = ItemType.ChainmailLeggings; + mappings[966] = ItemType.ChainmailBoots; + mappings[967] = ItemType.IronHelmet; + mappings[968] = ItemType.IronChestplate; + mappings[969] = ItemType.IronLeggings; + mappings[970] = ItemType.IronBoots; + mappings[971] = ItemType.DiamondHelmet; + mappings[972] = ItemType.DiamondChestplate; + mappings[973] = ItemType.DiamondLeggings; + mappings[974] = ItemType.DiamondBoots; + mappings[975] = ItemType.GoldenHelmet; + mappings[976] = ItemType.GoldenChestplate; + mappings[977] = ItemType.GoldenLeggings; + mappings[978] = ItemType.GoldenBoots; + mappings[979] = ItemType.NetheriteHelmet; + mappings[980] = ItemType.NetheriteChestplate; + mappings[981] = ItemType.NetheriteLeggings; + mappings[982] = ItemType.NetheriteBoots; + mappings[983] = ItemType.Flint; + mappings[984] = ItemType.Porkchop; + mappings[985] = ItemType.CookedPorkchop; + mappings[986] = ItemType.Painting; + mappings[987] = ItemType.GoldenApple; + mappings[988] = ItemType.EnchantedGoldenApple; + mappings[989] = ItemType.OakSign; + mappings[990] = ItemType.SpruceSign; + mappings[991] = ItemType.BirchSign; + mappings[992] = ItemType.JungleSign; + mappings[993] = ItemType.AcaciaSign; + mappings[994] = ItemType.CherrySign; + mappings[995] = ItemType.DarkOakSign; + mappings[996] = ItemType.PaleOakSign; + mappings[997] = ItemType.MangroveSign; + mappings[998] = ItemType.BambooSign; + mappings[999] = ItemType.CrimsonSign; + mappings[1000] = ItemType.WarpedSign; + mappings[1001] = ItemType.OakHangingSign; + mappings[1002] = ItemType.SpruceHangingSign; + mappings[1003] = ItemType.BirchHangingSign; + mappings[1004] = ItemType.JungleHangingSign; + mappings[1005] = ItemType.AcaciaHangingSign; + mappings[1006] = ItemType.CherryHangingSign; + mappings[1007] = ItemType.DarkOakHangingSign; + mappings[1008] = ItemType.PaleOakHangingSign; + mappings[1009] = ItemType.MangroveHangingSign; + mappings[1010] = ItemType.BambooHangingSign; + mappings[1011] = ItemType.CrimsonHangingSign; + mappings[1012] = ItemType.WarpedHangingSign; + mappings[1013] = ItemType.Bucket; + mappings[1014] = ItemType.WaterBucket; + mappings[1015] = ItemType.LavaBucket; + mappings[1016] = ItemType.PowderSnowBucket; + mappings[1017] = ItemType.Snowball; + mappings[1018] = ItemType.Leather; + mappings[1019] = ItemType.MilkBucket; + mappings[1020] = ItemType.PufferfishBucket; + mappings[1021] = ItemType.SalmonBucket; + mappings[1022] = ItemType.CodBucket; + mappings[1023] = ItemType.TropicalFishBucket; + mappings[1024] = ItemType.AxolotlBucket; + mappings[1025] = ItemType.TadpoleBucket; + mappings[1026] = ItemType.Brick; + mappings[1027] = ItemType.ClayBall; + mappings[1028] = ItemType.DriedKelpBlock; + mappings[1029] = ItemType.Paper; + mappings[1030] = ItemType.Book; + mappings[1031] = ItemType.SlimeBall; + mappings[1032] = ItemType.Egg; + mappings[1033] = ItemType.BlueEgg; + mappings[1034] = ItemType.BrownEgg; + mappings[1035] = ItemType.Compass; + mappings[1036] = ItemType.RecoveryCompass; + mappings[1037] = ItemType.Bundle; + mappings[1038] = ItemType.WhiteBundle; + mappings[1039] = ItemType.OrangeBundle; + mappings[1040] = ItemType.MagentaBundle; + mappings[1041] = ItemType.LightBlueBundle; + mappings[1042] = ItemType.YellowBundle; + mappings[1043] = ItemType.LimeBundle; + mappings[1044] = ItemType.PinkBundle; + mappings[1045] = ItemType.GrayBundle; + mappings[1046] = ItemType.LightGrayBundle; + mappings[1047] = ItemType.CyanBundle; + mappings[1048] = ItemType.PurpleBundle; + mappings[1049] = ItemType.BlueBundle; + mappings[1050] = ItemType.BrownBundle; + mappings[1051] = ItemType.GreenBundle; + mappings[1052] = ItemType.RedBundle; + mappings[1053] = ItemType.BlackBundle; + mappings[1054] = ItemType.FishingRod; + mappings[1055] = ItemType.Clock; + mappings[1056] = ItemType.Spyglass; + mappings[1057] = ItemType.GlowstoneDust; + mappings[1058] = ItemType.Cod; + mappings[1059] = ItemType.Salmon; + mappings[1060] = ItemType.TropicalFish; + mappings[1061] = ItemType.Pufferfish; + mappings[1062] = ItemType.CookedCod; + mappings[1063] = ItemType.CookedSalmon; + mappings[1064] = ItemType.InkSac; + mappings[1065] = ItemType.GlowInkSac; + mappings[1066] = ItemType.CocoaBeans; + mappings[1067] = ItemType.WhiteDye; + mappings[1068] = ItemType.OrangeDye; + mappings[1069] = ItemType.MagentaDye; + mappings[1070] = ItemType.LightBlueDye; + mappings[1071] = ItemType.YellowDye; + mappings[1072] = ItemType.LimeDye; + mappings[1073] = ItemType.PinkDye; + mappings[1074] = ItemType.GrayDye; + mappings[1075] = ItemType.LightGrayDye; + mappings[1076] = ItemType.CyanDye; + mappings[1077] = ItemType.PurpleDye; + mappings[1078] = ItemType.BlueDye; + mappings[1079] = ItemType.BrownDye; + mappings[1080] = ItemType.GreenDye; + mappings[1081] = ItemType.RedDye; + mappings[1082] = ItemType.BlackDye; + mappings[1083] = ItemType.BoneMeal; + mappings[1084] = ItemType.Bone; + mappings[1085] = ItemType.Sugar; + mappings[1086] = ItemType.Cake; + mappings[1087] = ItemType.WhiteBed; + mappings[1088] = ItemType.OrangeBed; + mappings[1089] = ItemType.MagentaBed; + mappings[1090] = ItemType.LightBlueBed; + mappings[1091] = ItemType.YellowBed; + mappings[1092] = ItemType.LimeBed; + mappings[1093] = ItemType.PinkBed; + mappings[1094] = ItemType.GrayBed; + mappings[1095] = ItemType.LightGrayBed; + mappings[1096] = ItemType.CyanBed; + mappings[1097] = ItemType.PurpleBed; + mappings[1098] = ItemType.BlueBed; + mappings[1099] = ItemType.BrownBed; + mappings[1100] = ItemType.GreenBed; + mappings[1101] = ItemType.RedBed; + mappings[1102] = ItemType.BlackBed; + mappings[1103] = ItemType.Cookie; + mappings[1104] = ItemType.Crafter; + mappings[1105] = ItemType.FilledMap; + mappings[1106] = ItemType.Shears; + mappings[1107] = ItemType.MelonSlice; + mappings[1108] = ItemType.DriedKelp; + mappings[1109] = ItemType.PumpkinSeeds; + mappings[1110] = ItemType.MelonSeeds; + mappings[1111] = ItemType.Beef; + mappings[1112] = ItemType.CookedBeef; + mappings[1113] = ItemType.Chicken; + mappings[1114] = ItemType.CookedChicken; + mappings[1115] = ItemType.RottenFlesh; + mappings[1116] = ItemType.EnderPearl; + mappings[1117] = ItemType.BlazeRod; + mappings[1118] = ItemType.GhastTear; + mappings[1119] = ItemType.GoldNugget; + mappings[1120] = ItemType.NetherWart; + mappings[1121] = ItemType.GlassBottle; + mappings[1122] = ItemType.Potion; + mappings[1123] = ItemType.SpiderEye; + mappings[1124] = ItemType.FermentedSpiderEye; + mappings[1125] = ItemType.BlazePowder; + mappings[1126] = ItemType.MagmaCream; + mappings[1127] = ItemType.BrewingStand; + mappings[1128] = ItemType.Cauldron; + mappings[1129] = ItemType.EnderEye; + mappings[1130] = ItemType.GlisteringMelonSlice; + mappings[1131] = ItemType.ChickenSpawnEgg; + mappings[1132] = ItemType.CowSpawnEgg; + mappings[1133] = ItemType.PigSpawnEgg; + mappings[1134] = ItemType.SheepSpawnEgg; + mappings[1135] = ItemType.CamelSpawnEgg; + mappings[1136] = ItemType.DonkeySpawnEgg; + mappings[1137] = ItemType.HorseSpawnEgg; + mappings[1138] = ItemType.MuleSpawnEgg; + mappings[1139] = ItemType.CatSpawnEgg; + mappings[1140] = ItemType.ParrotSpawnEgg; + mappings[1141] = ItemType.WolfSpawnEgg; + mappings[1142] = ItemType.ArmadilloSpawnEgg; + mappings[1143] = ItemType.BatSpawnEgg; + mappings[1144] = ItemType.BeeSpawnEgg; + mappings[1145] = ItemType.FoxSpawnEgg; + mappings[1146] = ItemType.GoatSpawnEgg; + mappings[1147] = ItemType.LlamaSpawnEgg; + mappings[1148] = ItemType.OcelotSpawnEgg; + mappings[1149] = ItemType.PandaSpawnEgg; + mappings[1150] = ItemType.PolarBearSpawnEgg; + mappings[1151] = ItemType.RabbitSpawnEgg; + mappings[1152] = ItemType.AxolotlSpawnEgg; + mappings[1153] = ItemType.CodSpawnEgg; + mappings[1154] = ItemType.DolphinSpawnEgg; + mappings[1155] = ItemType.FrogSpawnEgg; + mappings[1156] = ItemType.GlowSquidSpawnEgg; + mappings[1157] = ItemType.NautilusSpawnEgg; + mappings[1158] = ItemType.PufferfishSpawnEgg; + mappings[1159] = ItemType.SalmonSpawnEgg; + mappings[1160] = ItemType.SquidSpawnEgg; + mappings[1161] = ItemType.TadpoleSpawnEgg; + mappings[1162] = ItemType.TropicalFishSpawnEgg; + mappings[1163] = ItemType.TurtleSpawnEgg; + mappings[1164] = ItemType.AllaySpawnEgg; + mappings[1165] = ItemType.MooshroomSpawnEgg; + mappings[1166] = ItemType.SnifferSpawnEgg; + mappings[1167] = ItemType.CopperGolemSpawnEgg; + mappings[1168] = ItemType.IronGolemSpawnEgg; + mappings[1169] = ItemType.SnowGolemSpawnEgg; + mappings[1170] = ItemType.TraderLlamaSpawnEgg; + mappings[1171] = ItemType.VillagerSpawnEgg; + mappings[1172] = ItemType.WanderingTraderSpawnEgg; + mappings[1173] = ItemType.BoggedSpawnEgg; + mappings[1174] = ItemType.CamelHuskSpawnEgg; + mappings[1175] = ItemType.DrownedSpawnEgg; + mappings[1176] = ItemType.HuskSpawnEgg; + mappings[1177] = ItemType.ParchedSpawnEgg; + mappings[1178] = ItemType.SkeletonSpawnEgg; + mappings[1179] = ItemType.SkeletonHorseSpawnEgg; + mappings[1180] = ItemType.StraySpawnEgg; + mappings[1181] = ItemType.WitherSpawnEgg; + mappings[1182] = ItemType.WitherSkeletonSpawnEgg; + mappings[1183] = ItemType.ZombieSpawnEgg; + mappings[1184] = ItemType.ZombieHorseSpawnEgg; + mappings[1185] = ItemType.ZombieNautilusSpawnEgg; + mappings[1186] = ItemType.ZombieVillagerSpawnEgg; + mappings[1187] = ItemType.CaveSpiderSpawnEgg; + mappings[1188] = ItemType.SpiderSpawnEgg; + mappings[1189] = ItemType.BreezeSpawnEgg; + mappings[1190] = ItemType.CreakingSpawnEgg; + mappings[1191] = ItemType.CreeperSpawnEgg; + mappings[1192] = ItemType.ElderGuardianSpawnEgg; + mappings[1193] = ItemType.GuardianSpawnEgg; + mappings[1194] = ItemType.PhantomSpawnEgg; + mappings[1195] = ItemType.SilverfishSpawnEgg; + mappings[1196] = ItemType.SlimeSpawnEgg; + mappings[1197] = ItemType.WardenSpawnEgg; + mappings[1198] = ItemType.WitchSpawnEgg; + mappings[1199] = ItemType.EvokerSpawnEgg; + mappings[1200] = ItemType.PillagerSpawnEgg; + mappings[1201] = ItemType.RavagerSpawnEgg; + mappings[1202] = ItemType.VindicatorSpawnEgg; + mappings[1203] = ItemType.VexSpawnEgg; + mappings[1204] = ItemType.BlazeSpawnEgg; + mappings[1205] = ItemType.GhastSpawnEgg; + mappings[1206] = ItemType.HappyGhastSpawnEgg; + mappings[1207] = ItemType.HoglinSpawnEgg; + mappings[1208] = ItemType.MagmaCubeSpawnEgg; + mappings[1209] = ItemType.PiglinSpawnEgg; + mappings[1210] = ItemType.PiglinBruteSpawnEgg; + mappings[1211] = ItemType.StriderSpawnEgg; + mappings[1212] = ItemType.ZoglinSpawnEgg; + mappings[1213] = ItemType.ZombifiedPiglinSpawnEgg; + mappings[1214] = ItemType.EnderDragonSpawnEgg; + mappings[1215] = ItemType.EndermanSpawnEgg; + mappings[1216] = ItemType.EndermiteSpawnEgg; + mappings[1217] = ItemType.ShulkerSpawnEgg; + mappings[1218] = ItemType.ExperienceBottle; + mappings[1219] = ItemType.FireCharge; + mappings[1220] = ItemType.WindCharge; + mappings[1221] = ItemType.WritableBook; + mappings[1222] = ItemType.WrittenBook; + mappings[1223] = ItemType.BreezeRod; + mappings[1224] = ItemType.Mace; + mappings[1225] = ItemType.ItemFrame; + mappings[1226] = ItemType.GlowItemFrame; + mappings[1227] = ItemType.FlowerPot; + mappings[1228] = ItemType.Carrot; + mappings[1229] = ItemType.Potato; + mappings[1230] = ItemType.BakedPotato; + mappings[1231] = ItemType.PoisonousPotato; + mappings[1232] = ItemType.Map; + mappings[1233] = ItemType.GoldenCarrot; + mappings[1234] = ItemType.SkeletonSkull; + mappings[1235] = ItemType.WitherSkeletonSkull; + mappings[1236] = ItemType.PlayerHead; + mappings[1237] = ItemType.ZombieHead; + mappings[1238] = ItemType.CreeperHead; + mappings[1239] = ItemType.DragonHead; + mappings[1240] = ItemType.PiglinHead; + mappings[1241] = ItemType.NetherStar; + mappings[1242] = ItemType.PumpkinPie; + mappings[1243] = ItemType.FireworkRocket; + mappings[1244] = ItemType.FireworkStar; + mappings[1245] = ItemType.EnchantedBook; + mappings[1246] = ItemType.NetherBrick; + mappings[1247] = ItemType.ResinBrick; + mappings[1248] = ItemType.PrismarineShard; + mappings[1249] = ItemType.PrismarineCrystals; + mappings[1250] = ItemType.Rabbit; + mappings[1251] = ItemType.CookedRabbit; + mappings[1252] = ItemType.RabbitStew; + mappings[1253] = ItemType.RabbitFoot; + mappings[1254] = ItemType.RabbitHide; + mappings[1255] = ItemType.ArmorStand; + mappings[1256] = ItemType.CopperHorseArmor; + mappings[1257] = ItemType.IronHorseArmor; + mappings[1258] = ItemType.GoldenHorseArmor; + mappings[1259] = ItemType.DiamondHorseArmor; + mappings[1260] = ItemType.NetheriteHorseArmor; + mappings[1261] = ItemType.LeatherHorseArmor; + mappings[1262] = ItemType.Lead; + mappings[1263] = ItemType.NameTag; + mappings[1264] = ItemType.CommandBlockMinecart; + mappings[1265] = ItemType.Mutton; + mappings[1266] = ItemType.CookedMutton; + mappings[1267] = ItemType.WhiteBanner; + mappings[1268] = ItemType.OrangeBanner; + mappings[1269] = ItemType.MagentaBanner; + mappings[1270] = ItemType.LightBlueBanner; + mappings[1271] = ItemType.YellowBanner; + mappings[1272] = ItemType.LimeBanner; + mappings[1273] = ItemType.PinkBanner; + mappings[1274] = ItemType.GrayBanner; + mappings[1275] = ItemType.LightGrayBanner; + mappings[1276] = ItemType.CyanBanner; + mappings[1277] = ItemType.PurpleBanner; + mappings[1278] = ItemType.BlueBanner; + mappings[1279] = ItemType.BrownBanner; + mappings[1280] = ItemType.GreenBanner; + mappings[1281] = ItemType.RedBanner; + mappings[1282] = ItemType.BlackBanner; + mappings[1283] = ItemType.EndCrystal; + mappings[1284] = ItemType.ChorusFruit; + mappings[1285] = ItemType.PoppedChorusFruit; + mappings[1286] = ItemType.TorchflowerSeeds; + mappings[1287] = ItemType.PitcherPod; + mappings[1288] = ItemType.Beetroot; + mappings[1289] = ItemType.BeetrootSeeds; + mappings[1290] = ItemType.BeetrootSoup; + mappings[1291] = ItemType.DragonBreath; + mappings[1292] = ItemType.SplashPotion; + mappings[1293] = ItemType.SpectralArrow; + mappings[1294] = ItemType.TippedArrow; + mappings[1295] = ItemType.LingeringPotion; + mappings[1296] = ItemType.Shield; + mappings[1297] = ItemType.WoodenSpear; + mappings[1298] = ItemType.StoneSpear; + mappings[1299] = ItemType.CopperSpear; + mappings[1300] = ItemType.IronSpear; + mappings[1301] = ItemType.GoldenSpear; + mappings[1302] = ItemType.DiamondSpear; + mappings[1303] = ItemType.NetheriteSpear; + mappings[1304] = ItemType.TotemOfUndying; + mappings[1305] = ItemType.ShulkerShell; + mappings[1306] = ItemType.IronNugget; + mappings[1307] = ItemType.CopperNugget; + mappings[1308] = ItemType.KnowledgeBook; + mappings[1309] = ItemType.DebugStick; + mappings[1310] = ItemType.MusicDisc13; + mappings[1311] = ItemType.MusicDiscCat; + mappings[1312] = ItemType.MusicDiscBlocks; + mappings[1313] = ItemType.MusicDiscChirp; + mappings[1314] = ItemType.MusicDiscCreator; + mappings[1315] = ItemType.MusicDiscCreatorMusicBox; + mappings[1316] = ItemType.MusicDiscFar; + mappings[1317] = ItemType.MusicDiscLavaChicken; + mappings[1318] = ItemType.MusicDiscMall; + mappings[1319] = ItemType.MusicDiscMellohi; + mappings[1320] = ItemType.MusicDiscStal; + mappings[1321] = ItemType.MusicDiscStrad; + mappings[1322] = ItemType.MusicDiscWard; + mappings[1323] = ItemType.MusicDisc11; + mappings[1324] = ItemType.MusicDiscWait; + mappings[1325] = ItemType.MusicDiscOtherside; + mappings[1326] = ItemType.MusicDiscRelic; + mappings[1327] = ItemType.MusicDisc5; + mappings[1328] = ItemType.MusicDiscPigstep; + mappings[1329] = ItemType.MusicDiscPrecipice; + mappings[1330] = ItemType.MusicDiscTears; + mappings[1331] = ItemType.DiscFragment5; + mappings[1332] = ItemType.Trident; + mappings[1333] = ItemType.NautilusShell; + mappings[1334] = ItemType.IronNautilusArmor; + mappings[1335] = ItemType.GoldenNautilusArmor; + mappings[1336] = ItemType.DiamondNautilusArmor; + mappings[1337] = ItemType.NetheriteNautilusArmor; + mappings[1338] = ItemType.CopperNautilusArmor; + mappings[1339] = ItemType.HeartOfTheSea; + mappings[1340] = ItemType.Crossbow; + mappings[1341] = ItemType.SuspiciousStew; + mappings[1342] = ItemType.Loom; + mappings[1343] = ItemType.FlowerBannerPattern; + mappings[1344] = ItemType.CreeperBannerPattern; + mappings[1345] = ItemType.SkullBannerPattern; + mappings[1346] = ItemType.MojangBannerPattern; + mappings[1347] = ItemType.GlobeBannerPattern; + mappings[1348] = ItemType.PiglinBannerPattern; + mappings[1349] = ItemType.FlowBannerPattern; + mappings[1350] = ItemType.GusterBannerPattern; + mappings[1351] = ItemType.FieldMasonedBannerPattern; + mappings[1352] = ItemType.BordureIndentedBannerPattern; + mappings[1353] = ItemType.GoatHorn; + mappings[1354] = ItemType.Composter; + mappings[1355] = ItemType.Barrel; + mappings[1356] = ItemType.Smoker; + mappings[1357] = ItemType.BlastFurnace; + mappings[1358] = ItemType.CartographyTable; + mappings[1359] = ItemType.FletchingTable; + mappings[1360] = ItemType.Grindstone; + mappings[1361] = ItemType.SmithingTable; + mappings[1362] = ItemType.Stonecutter; + mappings[1363] = ItemType.Bell; + mappings[1364] = ItemType.Lantern; + mappings[1365] = ItemType.SoulLantern; + mappings[1366] = ItemType.CopperLantern; + mappings[1367] = ItemType.ExposedCopperLantern; + mappings[1368] = ItemType.WeatheredCopperLantern; + mappings[1369] = ItemType.OxidizedCopperLantern; + mappings[1370] = ItemType.WaxedCopperLantern; + mappings[1371] = ItemType.WaxedExposedCopperLantern; + mappings[1372] = ItemType.WaxedWeatheredCopperLantern; + mappings[1373] = ItemType.WaxedOxidizedCopperLantern; + mappings[1374] = ItemType.SweetBerries; + mappings[1375] = ItemType.GlowBerries; + mappings[1376] = ItemType.Campfire; + mappings[1377] = ItemType.SoulCampfire; + mappings[1378] = ItemType.Shroomlight; + mappings[1379] = ItemType.Honeycomb; + mappings[1380] = ItemType.BeeNest; + mappings[1381] = ItemType.Beehive; + mappings[1382] = ItemType.HoneyBottle; + mappings[1383] = ItemType.HoneycombBlock; + mappings[1384] = ItemType.Lodestone; + mappings[1385] = ItemType.CryingObsidian; + mappings[1386] = ItemType.Blackstone; + mappings[1387] = ItemType.BlackstoneSlab; + mappings[1388] = ItemType.BlackstoneStairs; + mappings[1389] = ItemType.GildedBlackstone; + mappings[1390] = ItemType.PolishedBlackstone; + mappings[1391] = ItemType.PolishedBlackstoneSlab; + mappings[1392] = ItemType.PolishedBlackstoneStairs; + mappings[1393] = ItemType.ChiseledPolishedBlackstone; + mappings[1394] = ItemType.PolishedBlackstoneBricks; + mappings[1395] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1396] = ItemType.PolishedBlackstoneBrickStairs; + mappings[1397] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[1398] = ItemType.RespawnAnchor; + mappings[1399] = ItemType.Candle; + mappings[1400] = ItemType.WhiteCandle; + mappings[1401] = ItemType.OrangeCandle; + mappings[1402] = ItemType.MagentaCandle; + mappings[1403] = ItemType.LightBlueCandle; + mappings[1404] = ItemType.YellowCandle; + mappings[1405] = ItemType.LimeCandle; + mappings[1406] = ItemType.PinkCandle; + mappings[1407] = ItemType.GrayCandle; + mappings[1408] = ItemType.LightGrayCandle; + mappings[1409] = ItemType.CyanCandle; + mappings[1410] = ItemType.PurpleCandle; + mappings[1411] = ItemType.BlueCandle; + mappings[1412] = ItemType.BrownCandle; + mappings[1413] = ItemType.GreenCandle; + mappings[1414] = ItemType.RedCandle; + mappings[1415] = ItemType.BlackCandle; + mappings[1416] = ItemType.SmallAmethystBud; + mappings[1417] = ItemType.MediumAmethystBud; + mappings[1418] = ItemType.LargeAmethystBud; + mappings[1419] = ItemType.AmethystCluster; + mappings[1420] = ItemType.PointedDripstone; + mappings[1421] = ItemType.OchreFroglight; + mappings[1422] = ItemType.VerdantFroglight; + mappings[1423] = ItemType.PearlescentFroglight; + mappings[1424] = ItemType.Frogspawn; + mappings[1425] = ItemType.EchoShard; + mappings[1426] = ItemType.Brush; + mappings[1427] = ItemType.NetheriteUpgradeSmithingTemplate; + mappings[1428] = ItemType.SentryArmorTrimSmithingTemplate; + mappings[1429] = ItemType.DuneArmorTrimSmithingTemplate; + mappings[1430] = ItemType.CoastArmorTrimSmithingTemplate; + mappings[1431] = ItemType.WildArmorTrimSmithingTemplate; + mappings[1432] = ItemType.WardArmorTrimSmithingTemplate; + mappings[1433] = ItemType.EyeArmorTrimSmithingTemplate; + mappings[1434] = ItemType.VexArmorTrimSmithingTemplate; + mappings[1435] = ItemType.TideArmorTrimSmithingTemplate; + mappings[1436] = ItemType.SnoutArmorTrimSmithingTemplate; + mappings[1437] = ItemType.RibArmorTrimSmithingTemplate; + mappings[1438] = ItemType.SpireArmorTrimSmithingTemplate; + mappings[1439] = ItemType.WayfinderArmorTrimSmithingTemplate; + mappings[1440] = ItemType.ShaperArmorTrimSmithingTemplate; + mappings[1441] = ItemType.SilenceArmorTrimSmithingTemplate; + mappings[1442] = ItemType.RaiserArmorTrimSmithingTemplate; + mappings[1443] = ItemType.HostArmorTrimSmithingTemplate; + mappings[1444] = ItemType.FlowArmorTrimSmithingTemplate; + mappings[1445] = ItemType.BoltArmorTrimSmithingTemplate; + mappings[1446] = ItemType.AnglerPotterySherd; + mappings[1447] = ItemType.ArcherPotterySherd; + mappings[1448] = ItemType.ArmsUpPotterySherd; + mappings[1449] = ItemType.BladePotterySherd; + mappings[1450] = ItemType.BrewerPotterySherd; + mappings[1451] = ItemType.BurnPotterySherd; + mappings[1452] = ItemType.DangerPotterySherd; + mappings[1453] = ItemType.ExplorerPotterySherd; + mappings[1454] = ItemType.FlowPotterySherd; + mappings[1455] = ItemType.FriendPotterySherd; + mappings[1456] = ItemType.GusterPotterySherd; + mappings[1457] = ItemType.HeartPotterySherd; + mappings[1458] = ItemType.HeartbreakPotterySherd; + mappings[1459] = ItemType.HowlPotterySherd; + mappings[1460] = ItemType.MinerPotterySherd; + mappings[1461] = ItemType.MournerPotterySherd; + mappings[1462] = ItemType.PlentyPotterySherd; + mappings[1463] = ItemType.PrizePotterySherd; + mappings[1464] = ItemType.ScrapePotterySherd; + mappings[1465] = ItemType.SheafPotterySherd; + mappings[1466] = ItemType.ShelterPotterySherd; + mappings[1467] = ItemType.SkullPotterySherd; + mappings[1468] = ItemType.SnortPotterySherd; + mappings[1469] = ItemType.CopperGrate; + mappings[1470] = ItemType.ExposedCopperGrate; + mappings[1471] = ItemType.WeatheredCopperGrate; + mappings[1472] = ItemType.OxidizedCopperGrate; + mappings[1473] = ItemType.WaxedCopperGrate; + mappings[1474] = ItemType.WaxedExposedCopperGrate; + mappings[1475] = ItemType.WaxedWeatheredCopperGrate; + mappings[1476] = ItemType.WaxedOxidizedCopperGrate; + mappings[1477] = ItemType.CopperBulb; + mappings[1478] = ItemType.ExposedCopperBulb; + mappings[1479] = ItemType.WeatheredCopperBulb; + mappings[1480] = ItemType.OxidizedCopperBulb; + mappings[1481] = ItemType.WaxedCopperBulb; + mappings[1482] = ItemType.WaxedExposedCopperBulb; + mappings[1483] = ItemType.WaxedWeatheredCopperBulb; + mappings[1484] = ItemType.WaxedOxidizedCopperBulb; + mappings[1485] = ItemType.CopperChest; + mappings[1486] = ItemType.ExposedCopperChest; + mappings[1487] = ItemType.WeatheredCopperChest; + mappings[1488] = ItemType.OxidizedCopperChest; + mappings[1489] = ItemType.WaxedCopperChest; + mappings[1490] = ItemType.WaxedExposedCopperChest; + mappings[1491] = ItemType.WaxedWeatheredCopperChest; + mappings[1492] = ItemType.WaxedOxidizedCopperChest; + mappings[1493] = ItemType.CopperGolemStatue; + mappings[1494] = ItemType.ExposedCopperGolemStatue; + mappings[1495] = ItemType.WeatheredCopperGolemStatue; + mappings[1496] = ItemType.OxidizedCopperGolemStatue; + mappings[1497] = ItemType.WaxedCopperGolemStatue; + mappings[1498] = ItemType.WaxedExposedCopperGolemStatue; + mappings[1499] = ItemType.WaxedWeatheredCopperGolemStatue; + mappings[1500] = ItemType.WaxedOxidizedCopperGolemStatue; + mappings[1501] = ItemType.TrialSpawner; + mappings[1502] = ItemType.TrialKey; + mappings[1503] = ItemType.OminousTrialKey; + mappings[1504] = ItemType.Vault; + mappings[1505] = ItemType.OminousBottle; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemRarity.cs b/MinecraftClient/Inventory/ItemRarity.cs new file mode 100644 index 00000000..a6cc0330 --- /dev/null +++ b/MinecraftClient/Inventory/ItemRarity.cs @@ -0,0 +1,9 @@ +namespace MinecraftClient.Inventory; + +public enum ItemRarity : int +{ + Common = 0, + Uncommon, + Rare, + Epic +} \ No newline at end of file diff --git a/MinecraftClient/Inventory/ItemType.cs b/MinecraftClient/Inventory/ItemType.cs index 175ebcef..aaff1d72 100644 --- a/MinecraftClient/Inventory/ItemType.cs +++ b/MinecraftClient/Inventory/ItemType.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Inventory +namespace MinecraftClient.Inventory { /// /// Generated using the --generator flag on the client @@ -26,6 +26,7 @@ AcaciaPlanks, AcaciaPressurePlate, AcaciaSapling, + AcaciaShelf, AcaciaSign, AcaciaSlab, AcaciaStairs, @@ -47,6 +48,8 @@ Anvil, Apple, ArcherPotterySherd, + ArmadilloScute, + ArmadilloSpawnEgg, ArmorStand, ArmsUpPotterySherd, Arrow, @@ -70,6 +73,7 @@ BambooPlanks, BambooPressurePlate, BambooRaft, + BambooShelf, BambooSign, BambooSlab, BambooStairs, @@ -101,6 +105,7 @@ BirchPlanks, BirchPressurePlate, BirchSapling, + BirchShelf, BirchSign, BirchSlab, BirchStairs, @@ -108,12 +113,14 @@ BirchWood, BlackBanner, BlackBed, + BlackBundle, BlackCandle, BlackCarpet, BlackConcrete, BlackConcretePowder, BlackDye, BlackGlazedTerracotta, + BlackHarness, BlackShulkerBox, BlackStainedGlass, BlackStainedGlassPane, @@ -130,12 +137,15 @@ BlazeSpawnEgg, BlueBanner, BlueBed, + BlueBundle, BlueCandle, BlueCarpet, BlueConcrete, BlueConcretePowder, BlueDye, + BlueEgg, BlueGlazedTerracotta, + BlueHarness, BlueIce, BlueOrchid, BlueShulkerBox, @@ -143,17 +153,21 @@ BlueStainedGlassPane, BlueTerracotta, BlueWool, + BoggedSpawnEgg, + BoltArmorTrimSmithingTemplate, Bone, BoneBlock, BoneMeal, Book, Bookshelf, + BordureIndentedBannerPattern, Bow, Bowl, BrainCoral, BrainCoralBlock, BrainCoralFan, Bread, + BreezeRod, BreezeSpawnEgg, BrewerPotterySherd, BrewingStand, @@ -164,12 +178,15 @@ Bricks, BrownBanner, BrownBed, + BrownBundle, BrownCandle, BrownCarpet, BrownConcrete, BrownConcretePowder, BrownDye, + BrownEgg, BrownGlazedTerracotta, + BrownHarness, BrownMushroom, BrownMushroomBlock, BrownShulkerBox, @@ -185,10 +202,13 @@ BuddingAmethyst, Bundle, BurnPotterySherd, + Bush, Cactus, + CactusFlower, Cake, Calcite, CalibratedSculkSensor, + CamelHuskSpawnEgg, CamelSpawnEgg, Campfire, Candle, @@ -218,6 +238,7 @@ CherryPlanks, CherryPressurePlate, CherrySapling, + CherryShelf, CherrySign, CherrySlab, CherryStairs, @@ -235,6 +256,7 @@ ChiseledPolishedBlackstone, ChiseledQuartzBlock, ChiseledRedSandstone, + ChiseledResinBricks, ChiseledSandstone, ChiseledStoneBricks, ChiseledTuff, @@ -245,6 +267,7 @@ Clay, ClayBall, Clock, + ClosedEyeblossom, Coal, CoalBlock, CoalOre, @@ -277,12 +300,32 @@ CookedRabbit, CookedSalmon, Cookie, + CopperAxe, + CopperBars, CopperBlock, + CopperBoots, CopperBulb, + CopperChain, + CopperChest, + CopperChestplate, CopperDoor, + CopperGolemSpawnEgg, + CopperGolemStatue, CopperGrate, + CopperHelmet, + CopperHoe, + CopperHorseArmor, CopperIngot, + CopperLantern, + CopperLeggings, + CopperNugget, + CopperNautilusArmor, CopperOre, + CopperPickaxe, + CopperShovel, + CopperSpear, + CopperSword, + CopperTorch, CopperTrapdoor, Cornflower, CowSpawnEgg, @@ -293,6 +336,8 @@ CrackedStoneBricks, Crafter, CraftingTable, + CreakingHeart, + CreakingSpawnEgg, CreeperBannerPattern, CreeperHead, CreeperSpawnEgg, @@ -307,6 +352,7 @@ CrimsonPlanks, CrimsonPressurePlate, CrimsonRoots, + CrimsonShelf, CrimsonSign, CrimsonSlab, CrimsonStairs, @@ -323,12 +369,14 @@ CutSandstoneSlab, CyanBanner, CyanBed, + CyanBundle, CyanCandle, CyanCarpet, CyanConcrete, CyanConcretePowder, CyanDye, CyanGlazedTerracotta, + CyanHarness, CyanShulkerBox, CyanStainedGlass, CyanStainedGlassPane, @@ -349,6 +397,7 @@ DarkOakPlanks, DarkOakPressurePlate, DarkOakSapling, + DarkOakShelf, DarkOakSign, DarkOakSlab, DarkOakStairs, @@ -403,8 +452,10 @@ DiamondHoe, DiamondHorseArmor, DiamondLeggings, + DiamondNautilusArmor, DiamondOre, DiamondPickaxe, + DiamondSpear, DiamondShovel, DiamondSword, Diorite, @@ -420,11 +471,14 @@ DragonBreath, DragonEgg, DragonHead, + DriedGhast, DriedKelp, DriedKelpBlock, DripstoneBlock, Dropper, DrownedSpawnEgg, + DryShortGrass, + DryTallGrass, DuneArmorTrimSmithingTemplate, EchoShard, Egg, @@ -455,29 +509,40 @@ ExplorerPotterySherd, ExposedChiseledCopper, ExposedCopper, + ExposedCopperBars, ExposedCopperBulb, + ExposedCopperChain, + ExposedCopperChest, ExposedCopperDoor, + ExposedCopperGolemStatue, ExposedCopperGrate, + ExposedCopperLantern, ExposedCopperTrapdoor, ExposedCutCopper, ExposedCutCopperSlab, ExposedCutCopperStairs, + ExposedLightningRod, EyeArmorTrimSmithingTemplate, Farmland, Feather, FermentedSpiderEye, Fern, + FieldMasonedBannerPattern, FilledMap, FireCharge, FireCoral, FireCoralBlock, FireCoralFan, + FireflyBush, FireworkRocket, FireworkStar, FishingRod, FletchingTable, Flint, FlintAndSteel, + FlowArmorTrimSmithingTemplate, + FlowBannerPattern, + FlowPotterySherd, FlowerBannerPattern, FlowerPot, FloweringAzalea, @@ -513,29 +578,33 @@ GoldenAxe, GoldenBoots, GoldenCarrot, + GoldenDandelion, GoldenChestplate, GoldenHelmet, GoldenHoe, GoldenHorseArmor, GoldenLeggings, + GoldenNautilusArmor, GoldenPickaxe, GoldenShovel, + GoldenSpear, GoldenSword, Granite, GraniteSlab, GraniteStairs, GraniteWall, - Grass, // 1.20.3+ renamed to ShortGrass GrassBlock, Gravel, GrayBanner, GrayBed, + GrayBundle, GrayCandle, GrayCarpet, GrayConcrete, GrayConcretePowder, GrayDye, GrayGlazedTerracotta, + GrayHarness, GrayShulkerBox, GrayStainedGlass, GrayStainedGlassPane, @@ -543,12 +612,14 @@ GrayWool, GreenBanner, GreenBed, + GreenBundle, GreenCandle, GreenCarpet, GreenConcrete, GreenConcretePowder, GreenDye, GreenGlazedTerracotta, + GreenHarness, GreenShulkerBox, GreenStainedGlass, GreenStainedGlassPane, @@ -557,11 +628,15 @@ Grindstone, GuardianSpawnEgg, Gunpowder, + GusterBannerPattern, + GusterPotterySherd, HangingRoots, + HappyGhastSpawnEgg, HayBlock, HeartOfTheSea, HeartPotterySherd, HeartbreakPotterySherd, + HeavyCore, HeavyWeightedPressurePlate, HoglinSpawnEgg, HoneyBlock, @@ -590,6 +665,7 @@ IronBars, IronBlock, IronBoots, + IronChain, IronChestplate, IronDoor, IronGolemSpawnEgg, @@ -598,9 +674,11 @@ IronHorseArmor, IronIngot, IronLeggings, + IronNautilusArmor, IronNugget, IronOre, IronPickaxe, + IronSpear, IronShovel, IronSword, IronTrapdoor, @@ -620,6 +698,7 @@ JunglePlanks, JunglePressurePlate, JungleSapling, + JungleShelf, JungleSign, JungleSlab, JungleStairs, @@ -636,6 +715,7 @@ LargeFern, LavaBucket, Lead, + LeafLitter, Leather, LeatherBoots, LeatherChestplate, @@ -647,12 +727,14 @@ Light, LightBlueBanner, LightBlueBed, + LightBlueBundle, LightBlueCandle, LightBlueCarpet, LightBlueConcrete, LightBlueConcretePowder, LightBlueDye, LightBlueGlazedTerracotta, + LightBlueHarness, LightBlueShulkerBox, LightBlueStainedGlass, LightBlueStainedGlassPane, @@ -660,12 +742,14 @@ LightBlueWool, LightGrayBanner, LightGrayBed, + LightGrayBundle, LightGrayCandle, LightGrayCarpet, LightGrayConcrete, LightGrayConcretePowder, LightGrayDye, LightGrayGlazedTerracotta, + LightGrayHarness, LightGrayShulkerBox, LightGrayStainedGlass, LightGrayStainedGlassPane, @@ -678,12 +762,14 @@ LilyPad, LimeBanner, LimeBed, + LimeBundle, LimeCandle, LimeCarpet, LimeConcrete, LimeConcretePowder, LimeDye, LimeGlazedTerracotta, + LimeHarness, LimeShulkerBox, LimeStainedGlass, LimeStainedGlassPane, @@ -693,14 +779,17 @@ LlamaSpawnEgg, Lodestone, Loom, + Mace, MagentaBanner, MagentaBed, + MagentaBundle, MagentaCandle, MagentaCarpet, MagentaConcrete, MagentaConcretePowder, MagentaDye, MagentaGlazedTerracotta, + MagentaHarness, MagentaShulkerBox, MagentaStainedGlass, MagentaStainedGlassPane, @@ -722,6 +811,7 @@ MangrovePressurePlate, MangrovePropagule, MangroveRoots, + MangroveShelf, MangroveSign, MangroveSlab, MangroveStairs, @@ -763,20 +853,26 @@ MusicDiscBlocks, MusicDiscCat, MusicDiscChirp, + MusicDiscCreator, + MusicDiscCreatorMusicBox, MusicDiscFar, + MusicDiscLavaChicken, MusicDiscMall, MusicDiscMellohi, MusicDiscOtherside, MusicDiscPigstep, + MusicDiscPrecipice, MusicDiscRelic, MusicDiscStal, MusicDiscStrad, + MusicDiscTears, MusicDiscWait, MusicDiscWard, Mutton, Mycelium, NameTag, NautilusShell, + NautilusSpawnEgg, NetherBrick, NetherBrickFence, NetherBrickSlab, @@ -795,11 +891,14 @@ NetheriteChestplate, NetheriteHelmet, NetheriteHoe, + NetheriteHorseArmor, NetheriteIngot, NetheriteLeggings, + NetheriteNautilusArmor, NetheritePickaxe, NetheriteScrap, NetheriteShovel, + NetheriteSpear, NetheriteSword, NetheriteUpgradeSmithingTemplate, Netherrack, @@ -816,6 +915,7 @@ OakPlanks, OakPressurePlate, OakSapling, + OakShelf, OakSign, OakSlab, OakStairs, @@ -825,14 +925,19 @@ Obsidian, OcelotSpawnEgg, OchreFroglight, + OminousBottle, + OminousTrialKey, + OpenEyeblossom, OrangeBanner, OrangeBed, + OrangeBundle, OrangeCandle, OrangeCarpet, OrangeConcrete, OrangeConcretePowder, OrangeDye, OrangeGlazedTerracotta, + OrangeHarness, OrangeShulkerBox, OrangeStainedGlass, OrangeStainedGlassPane, @@ -842,18 +947,46 @@ OxeyeDaisy, OxidizedChiseledCopper, OxidizedCopper, + OxidizedCopperBars, OxidizedCopperBulb, + OxidizedCopperChain, + OxidizedCopperChest, OxidizedCopperDoor, + OxidizedCopperGolemStatue, OxidizedCopperGrate, + OxidizedCopperLantern, OxidizedCopperTrapdoor, OxidizedCutCopper, OxidizedCutCopperSlab, OxidizedCutCopperStairs, + OxidizedLightningRod, PackedIce, PackedMud, Painting, + PaleHangingMoss, + PaleMossBlock, + PaleMossCarpet, + PaleOakBoat, + PaleOakButton, + PaleOakChestBoat, + PaleOakDoor, + PaleOakFence, + PaleOakFenceGate, + PaleOakHangingSign, + PaleOakLeaves, + PaleOakLog, + PaleOakPlanks, + PaleOakPressurePlate, + PaleOakSapling, + PaleOakShelf, + PaleOakSign, + PaleOakSlab, + PaleOakStairs, + PaleOakTrapdoor, + PaleOakWood, PandaSpawnEgg, Paper, + ParchedSpawnEgg, ParrotSpawnEgg, PearlescentFroglight, Peony, @@ -868,12 +1001,14 @@ PillagerSpawnEgg, PinkBanner, PinkBed, + PinkBundle, PinkCandle, PinkCarpet, PinkConcrete, PinkConcretePowder, PinkDye, PinkGlazedTerracotta, + PinkHarness, PinkPetals, PinkShulkerBox, PinkStainedGlass, @@ -943,12 +1078,14 @@ PumpkinSeeds, PurpleBanner, PurpleBed, + PurpleBundle, PurpleCandle, PurpleCarpet, PurpleConcrete, PurpleConcretePowder, PurpleDye, PurpleGlazedTerracotta, + PurpleHarness, PurpleShulkerBox, PurpleStainedGlass, PurpleStainedGlassPane, @@ -981,12 +1118,14 @@ RecoveryCompass, RedBanner, RedBed, + RedBundle, RedCandle, RedCarpet, RedConcrete, RedConcretePowder, RedDye, RedGlazedTerracotta, + RedHarness, RedMushroom, RedMushroomBlock, RedNetherBrickSlab, @@ -1012,6 +1151,13 @@ ReinforcedDeepslate, Repeater, RepeatingCommandBlock, + ResinBlock, + ResinBrick, + ResinBrickSlab, + ResinBrickStairs, + ResinBrickWall, + ResinBricks, + ResinClump, RespawnAnchor, RibArmorTrimSmithingTemplate, RootedDirt, @@ -1027,12 +1173,12 @@ SandstoneStairs, SandstoneWall, Scaffolding, + ScrapePotterySherd, Sculk, SculkCatalyst, SculkSensor, SculkShrieker, SculkVein, - Scute, SeaLantern, SeaPickle, Seagrass, @@ -1043,6 +1189,7 @@ SheepSpawnEgg, ShelterPotterySherd, Shield, + ShortDryGrass, ShortGrass, Shroomlight, ShulkerBox, @@ -1107,6 +1254,7 @@ SprucePlanks, SprucePressurePlate, SpruceSapling, + SpruceShelf, SpruceSign, SpruceSlab, SpruceStairs, @@ -1128,6 +1276,7 @@ StonePressurePlate, StoneShovel, StoneSlab, + StoneSpear, StoneStairs, StoneSword, Stonecutter, @@ -1151,6 +1300,8 @@ StrippedMangroveWood, StrippedOakLog, StrippedOakWood, + StrippedPaleOakLog, + StrippedPaleOakWood, StrippedSpruceLog, StrippedSpruceWood, StrippedWarpedHyphae, @@ -1166,9 +1317,12 @@ SweetBerries, TadpoleBucket, TadpoleSpawnEgg, + TallDryGrass, TallGrass, Target, Terracotta, + TestBlock, + TestInstanceBlock, TideArmorTrimSmithingTemplate, TintedGlass, TippedArrow, @@ -1200,8 +1354,10 @@ TuffWall, TurtleEgg, TurtleHelmet, + TurtleScute, TurtleSpawnEgg, TwistingVines, + Vault, VerdantFroglight, VexArmorTrimSmithingTemplate, VexSpawnEgg, @@ -1223,6 +1379,7 @@ WarpedPlanks, WarpedPressurePlate, WarpedRoots, + WarpedShelf, WarpedSign, WarpedSlab, WarpedStairs, @@ -1231,63 +1388,95 @@ WarpedWartBlock, WaterBucket, WaxedChiseledCopper, + WaxedCopperBars, WaxedCopperBlock, WaxedCopperBulb, + WaxedCopperChain, + WaxedCopperChest, WaxedCopperDoor, + WaxedCopperGolemStatue, WaxedCopperGrate, + WaxedCopperLantern, WaxedCopperTrapdoor, WaxedCutCopper, WaxedCutCopperSlab, WaxedCutCopperStairs, WaxedExposedChiseledCopper, WaxedExposedCopper, + WaxedExposedCopperBars, WaxedExposedCopperBulb, + WaxedExposedCopperChain, + WaxedExposedCopperChest, WaxedExposedCopperDoor, + WaxedExposedCopperGolemStatue, WaxedExposedCopperGrate, + WaxedExposedCopperLantern, WaxedExposedCopperTrapdoor, WaxedExposedCutCopper, WaxedExposedCutCopperSlab, WaxedExposedCutCopperStairs, + WaxedExposedLightningRod, + WaxedLightningRod, WaxedOxidizedChiseledCopper, WaxedOxidizedCopper, + WaxedOxidizedCopperBars, WaxedOxidizedCopperBulb, + WaxedOxidizedCopperChain, + WaxedOxidizedCopperChest, WaxedOxidizedCopperDoor, + WaxedOxidizedCopperGolemStatue, WaxedOxidizedCopperGrate, + WaxedOxidizedCopperLantern, WaxedOxidizedCopperTrapdoor, WaxedOxidizedCutCopper, WaxedOxidizedCutCopperSlab, WaxedOxidizedCutCopperStairs, + WaxedOxidizedLightningRod, WaxedWeatheredChiseledCopper, WaxedWeatheredCopper, + WaxedWeatheredCopperBars, WaxedWeatheredCopperBulb, + WaxedWeatheredCopperChain, + WaxedWeatheredCopperChest, WaxedWeatheredCopperDoor, + WaxedWeatheredCopperGolemStatue, WaxedWeatheredCopperGrate, + WaxedWeatheredCopperLantern, WaxedWeatheredCopperTrapdoor, WaxedWeatheredCutCopper, WaxedWeatheredCutCopperSlab, WaxedWeatheredCutCopperStairs, + WaxedWeatheredLightningRod, WayfinderArmorTrimSmithingTemplate, WeatheredChiseledCopper, WeatheredCopper, + WeatheredCopperBars, WeatheredCopperBulb, + WeatheredCopperChain, + WeatheredCopperChest, WeatheredCopperDoor, + WeatheredCopperGolemStatue, WeatheredCopperGrate, + WeatheredCopperLantern, WeatheredCopperTrapdoor, WeatheredCutCopper, WeatheredCutCopperSlab, WeatheredCutCopperStairs, + WeatheredLightningRod, WeepingVines, WetSponge, Wheat, WheatSeeds, WhiteBanner, WhiteBed, + WhiteBundle, WhiteCandle, WhiteCarpet, WhiteConcrete, WhiteConcretePowder, WhiteDye, WhiteGlazedTerracotta, + WhiteHarness, WhiteShulkerBox, WhiteStainedGlass, WhiteStainedGlassPane, @@ -1295,27 +1484,33 @@ WhiteTulip, WhiteWool, WildArmorTrimSmithingTemplate, + Wildflowers, + WindCharge, WitchSpawnEgg, WitherRose, WitherSkeletonSkull, WitherSkeletonSpawnEgg, WitherSpawnEgg, + WolfArmor, WolfSpawnEgg, WoodenAxe, WoodenHoe, WoodenPickaxe, + WoodenSpear, WoodenShovel, WoodenSword, WritableBook, WrittenBook, YellowBanner, YellowBed, + YellowBundle, YellowCandle, YellowCarpet, YellowConcrete, YellowConcretePowder, YellowDye, YellowGlazedTerracotta, + YellowHarness, YellowShulkerBox, YellowStainedGlass, YellowStainedGlassPane, @@ -1325,7 +1520,8 @@ ZombieHead, ZombieHorseSpawnEgg, ZombieSpawnEgg, + ZombieNautilusSpawnEgg, ZombieVillagerSpawnEgg, ZombifiedPiglinSpawnEgg, } -} \ No newline at end of file +} diff --git a/MinecraftClient/Inventory/ItemTypeExtensions.cs b/MinecraftClient/Inventory/ItemTypeExtensions.cs index 7f3e5f75..0aac00ba 100644 --- a/MinecraftClient/Inventory/ItemTypeExtensions.cs +++ b/MinecraftClient/Inventory/ItemTypeExtensions.cs @@ -48,16 +48,27 @@ namespace MinecraftClient.Inventory ItemType[] t = { ItemType.AcaciaBoat, + ItemType.AcaciaChestBoat, ItemType.AxolotlBucket, ItemType.BirchBoat, + ItemType.BirchChestBoat, ItemType.BlackBed, + ItemType.BlackBundle, + ItemType.BlackHarness, ItemType.BlackShulkerBox, ItemType.BlueBed, + ItemType.BlueBundle, + ItemType.BlueHarness, ItemType.BlueShulkerBox, ItemType.Bundle, ItemType.Bow, ItemType.BrownBed, + ItemType.BrownBundle, + ItemType.BrownHarness, ItemType.BrownShulkerBox, + ItemType.BordureIndentedBannerPattern, + ItemType.BambooChestRaft, + ItemType.BambooRaft, ItemType.Cake, ItemType.ChainmailBoots, ItemType.ChainmailChestplate, @@ -68,9 +79,26 @@ namespace MinecraftClient.Inventory ItemType.CommandBlockMinecart, ItemType.CreeperBannerPattern, ItemType.Crossbow, + ItemType.CopperAxe, + ItemType.CopperBoots, + ItemType.CopperChestplate, + ItemType.CopperHelmet, + ItemType.CopperHoe, + ItemType.CopperHorseArmor, + ItemType.CopperLeggings, + ItemType.CopperNautilusArmor, + ItemType.CopperPickaxe, + ItemType.CopperShovel, + ItemType.CopperSpear, + ItemType.CopperSword, ItemType.CyanBed, + ItemType.CyanBundle, + ItemType.CyanHarness, ItemType.CyanShulkerBox, + ItemType.CherryBoat, + ItemType.CherryChestBoat, ItemType.DarkOakBoat, + ItemType.DarkOakChestBoat, ItemType.DebugStick, ItemType.DiamondAxe, ItemType.DiamondBoots, @@ -79,16 +107,23 @@ namespace MinecraftClient.Inventory ItemType.DiamondHoe, ItemType.DiamondHorseArmor, ItemType.DiamondLeggings, + ItemType.DiamondNautilusArmor, ItemType.DiamondPickaxe, ItemType.DiamondShovel, + ItemType.DiamondSpear, ItemType.DiamondSword, ItemType.Elytra, ItemType.EnchantedBook, ItemType.FilledMap, ItemType.FishingRod, + ItemType.FieldMasonedBannerPattern, ItemType.FlintAndSteel, ItemType.FurnaceMinecart, + ItemType.FlowBannerPattern, + ItemType.FlowerBannerPattern, + ItemType.GoatHorn, ItemType.GlobeBannerPattern, + ItemType.GusterBannerPattern, ItemType.GoldenAxe, ItemType.GoldenBoots, ItemType.GoldenChestplate, @@ -96,14 +131,22 @@ namespace MinecraftClient.Inventory ItemType.GoldenHoe, ItemType.GoldenHorseArmor, ItemType.GoldenLeggings, + ItemType.GoldenNautilusArmor, ItemType.GoldenPickaxe, ItemType.GoldenShovel, + ItemType.GoldenSpear, ItemType.GoldenSword, ItemType.GrayBed, + ItemType.GrayBundle, + ItemType.GrayHarness, ItemType.GrayShulkerBox, ItemType.GreenBed, + ItemType.GreenBundle, + ItemType.GreenHarness, ItemType.GreenShulkerBox, ItemType.HopperMinecart, + ItemType.IronNautilusArmor, + ItemType.IronSpear, ItemType.IronAxe, ItemType.IronBoots, ItemType.IronChestplate, @@ -116,6 +159,7 @@ namespace MinecraftClient.Inventory ItemType.IronSword, ItemType.Jigsaw, ItemType.JungleBoat, + ItemType.JungleChestBoat, ItemType.LavaBucket, ItemType.LeatherBoots, ItemType.LeatherChestplate, @@ -123,42 +167,77 @@ namespace MinecraftClient.Inventory ItemType.LeatherHorseArmor, ItemType.LeatherLeggings, ItemType.LightBlueBed, + ItemType.LightBlueBundle, + ItemType.LightBlueHarness, ItemType.LightBlueShulkerBox, ItemType.LightGrayBed, + ItemType.LightGrayBundle, + ItemType.LightGrayHarness, ItemType.LightGrayShulkerBox, ItemType.LimeBed, + ItemType.LimeBundle, + ItemType.LimeHarness, ItemType.LimeShulkerBox, ItemType.LingeringPotion, + ItemType.Mace, ItemType.MagentaBed, + ItemType.MagentaBundle, + ItemType.MagentaHarness, ItemType.MagentaShulkerBox, ItemType.MilkBucket, ItemType.Minecart, + ItemType.MangroveBoat, + ItemType.MangroveChestBoat, ItemType.MojangBannerPattern, + ItemType.MusicDisc5, ItemType.MushroomStew, ItemType.MusicDisc11, ItemType.MusicDisc13, + ItemType.MusicDiscBlocks, ItemType.MusicDiscCat, ItemType.MusicDiscChirp, + ItemType.MusicDiscCreator, + ItemType.MusicDiscCreatorMusicBox, ItemType.MusicDiscFar, + ItemType.MusicDiscLavaChicken, ItemType.MusicDiscMall, ItemType.MusicDiscMellohi, ItemType.MusicDiscOtherside, + ItemType.MusicDiscPigstep, + ItemType.MusicDiscPrecipice, + ItemType.MusicDiscRelic, ItemType.MusicDiscStal, ItemType.MusicDiscStrad, + ItemType.MusicDiscTears, ItemType.MusicDiscWait, ItemType.MusicDiscWard, + ItemType.NetheriteHorseArmor, + ItemType.NetheriteNautilusArmor, + ItemType.NetheriteSpear, ItemType.OakBoat, + ItemType.OakChestBoat, ItemType.OrangeBed, + ItemType.OrangeBundle, + ItemType.OrangeHarness, ItemType.OrangeShulkerBox, + ItemType.PaleOakBoat, + ItemType.PaleOakChestBoat, ItemType.PinkBed, + ItemType.PinkBundle, + ItemType.PinkHarness, ItemType.PinkShulkerBox, ItemType.Potion, ItemType.PowderSnowBucket, ItemType.PufferfishBucket, + ItemType.PiglinBannerPattern, ItemType.PurpleBed, + ItemType.PurpleBundle, + ItemType.PurpleHarness, ItemType.PurpleShulkerBox, ItemType.RabbitStew, ItemType.RedBed, + ItemType.RedBundle, + ItemType.RedHarness, ItemType.RedShulkerBox, ItemType.Saddle, ItemType.SalmonBucket, @@ -168,7 +247,9 @@ namespace MinecraftClient.Inventory ItemType.SkullBannerPattern, ItemType.SplashPotion, ItemType.SpruceBoat, + ItemType.SpruceChestBoat, ItemType.Spyglass, + ItemType.StoneSpear, ItemType.StoneAxe, ItemType.StoneHoe, ItemType.StonePickaxe, @@ -183,7 +264,11 @@ namespace MinecraftClient.Inventory ItemType.TurtleHelmet, ItemType.WaterBucket, ItemType.WhiteBed, + ItemType.WhiteBundle, + ItemType.WhiteHarness, ItemType.WhiteShulkerBox, + ItemType.WolfArmor, + ItemType.WoodenSpear, ItemType.WoodenAxe, ItemType.WoodenHoe, ItemType.WoodenPickaxe, @@ -192,6 +277,8 @@ namespace MinecraftClient.Inventory ItemType.WritableBook, ItemType.WrittenBook, ItemType.YellowBed, + ItemType.YellowBundle, + ItemType.YellowHarness, ItemType.YellowShulkerBox }; return !t.Contains(m); @@ -220,6 +307,8 @@ namespace MinecraftClient.Inventory ItemType.YellowBanner, ItemType.ArmorStand, ItemType.Bucket, + ItemType.BlueEgg, + ItemType.BrownEgg, ItemType.Egg, ItemType.EnderEye, ItemType.HoneyBottle, diff --git a/MinecraftClient/Inventory/SuspiciousStewEffect.cs b/MinecraftClient/Inventory/SuspiciousStewEffect.cs new file mode 100644 index 00000000..7b1b5553 --- /dev/null +++ b/MinecraftClient/Inventory/SuspiciousStewEffect.cs @@ -0,0 +1,3 @@ +namespace MinecraftClient.Inventory; + +public record SuspiciousStewEffect(int TypeId, int Duration); \ No newline at end of file diff --git a/MinecraftClient/Inventory/TrimAssetOverride.cs b/MinecraftClient/Inventory/TrimAssetOverride.cs new file mode 100644 index 00000000..7047bde2 --- /dev/null +++ b/MinecraftClient/Inventory/TrimAssetOverride.cs @@ -0,0 +1,3 @@ +namespace MinecraftClient.Inventory; + +public record TrimAssetOverride(int ArmorMaterialType, string AssetName); \ No newline at end of file diff --git a/MinecraftClient/Inventory/VillagerInfo.cs b/MinecraftClient/Inventory/VillagerInfo.cs index c93781e8..426cd112 100644 --- a/MinecraftClient/Inventory/VillagerInfo.cs +++ b/MinecraftClient/Inventory/VillagerInfo.cs @@ -3,7 +3,7 @@ /// /// Properties of a villager /// - public class VillagerInfo + public record VillagerInfo { public int Level { get; set; } public int Experience { get; set; } diff --git a/MinecraftClient/Inventory/VillagerTrade.cs b/MinecraftClient/Inventory/VillagerTrade.cs index 70246cab..46cd13e9 100644 --- a/MinecraftClient/Inventory/VillagerTrade.cs +++ b/MinecraftClient/Inventory/VillagerTrade.cs @@ -3,31 +3,15 @@ /// /// Represents a trade of a villager /// - public class VillagerTrade - { - public Item InputItem1; - public Item OutputItem; - public Item? InputItem2; - public bool TradeDisabled; - public int NumberOfTradeUses; - public int MaximumNumberOfTradeUses; - public int Xp; - public int SpecialPrice; - public float PriceMultiplier; - public int Demand; - - public VillagerTrade(Item inputItem1, Item outputItem, Item? inputItem2, bool tradeDisabled, int numberOfTradeUses, int maximumNumberOfTradeUses, int xp, int specialPrice, float priceMultiplier, int demand) - { - InputItem1 = inputItem1; - OutputItem = outputItem; - InputItem2 = inputItem2; - TradeDisabled = tradeDisabled; - NumberOfTradeUses = numberOfTradeUses; - MaximumNumberOfTradeUses = maximumNumberOfTradeUses; - Xp = xp; - SpecialPrice = specialPrice; - PriceMultiplier = priceMultiplier; - Demand = demand; - } - } + public record VillagerTrade( + Item InputItem1, + Item OutputItem, + Item? InputItem2, + bool TradeDisabled, + int NumberOfTradeUses, + int MaximumNumberOfTradeUses, + int Xp, + int SpecialPrice, + float PriceMultiplier, + int Demand); } diff --git a/MinecraftClient/Json.cs b/MinecraftClient/Json.cs index 494e1142..08f5b24c 100644 --- a/MinecraftClient/Json.cs +++ b/MinecraftClient/Json.cs @@ -1,377 +1,92 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Nodes; -namespace MinecraftClient +namespace MinecraftClient; + +/// +/// JSON utilities backed by System.Text.Json. +/// +public static class Json +{ + private static readonly JsonSerializerOptions s_escapeOptions = new() + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + /// + /// Parse a JSON string into a mutable DOM. + /// Returns null for null, empty, or whitespace-only input. + /// Returns a wrapping the raw string when the input + /// is not valid JSON (e.g. a plain-text Minecraft MOTD or chat message). + /// + public static JsonNode? ParseJson(string? json) + { + if (string.IsNullOrWhiteSpace(json)) return null; + ReadOnlySpan text = json.AsSpan().TrimStart(); + if (!LooksLikeJson(text)) + return JsonValue.Create(json); + + try { return JsonNode.Parse(json); } + catch (JsonException) { return JsonValue.Create(json); } + } + + private static bool LooksLikeJson(ReadOnlySpan text) + { + if (text.IsEmpty) + return false; + + return text[0] switch + { + '{' or '"' => true, + '[' => LooksLikeJsonArray(text[1..]), + '-' => text.Length > 1 && char.IsAsciiDigit(text[1]), + >= '0' and <= '9' => true, + 't' or 'f' or 'n' => true, + _ => false + }; + } + + private static bool LooksLikeJsonArray(ReadOnlySpan text) + { + text = text.TrimStart(); + if (text.IsEmpty) + return false; + + return text[0] switch + { + ']' or '{' or '[' or '"' => true, + '-' => text.Length > 1 && char.IsAsciiDigit(text[1]), + >= '0' and <= '9' => true, + 't' or 'f' or 'n' => true, + _ => false + }; + } + + /// + /// Escape a string for embedding inside a JSON string literal. + /// Uses System.Text.Json serialization and strips the surrounding quotes. + /// + public static string EscapeString(string src) => + JsonSerializer.Serialize(src, s_escapeOptions)[1..^1]; +} + +/// +/// Extension helpers for that replicate the access patterns +/// of the former JSONData.StringValue property. +/// +public static class JsonNodeExtensions { /// - /// This class parses JSON data and returns an object describing that data. - /// Really lightweight JSON handling by ORelio - (c) 2013 - 2020 + /// Return the string representation of any JSON value. + /// Strings are returned without quotes; numbers, booleans, and null + /// are returned as their text representation. /// - public static class Json + public static string GetStringValue(this JsonNode? node) => node switch { - /// - /// Parse some JSON and return the corresponding JSON object - /// - public static JSONData ParseJson(string json) - { - int cursorpos = 0; - return String2Data(json, ref cursorpos); - } - - /// - /// The class storing unserialized JSON data - /// The data can be an object, an array or a string - /// - public class JSONData - { - public enum DataType - { - Object, - Array, - String - }; - - private readonly DataType type; - - public DataType Type - { - get { return type; } - } - - public Dictionary Properties; - public List DataArray; - public string StringValue; - - public JSONData(DataType datatype) - { - type = datatype; - Properties = new Dictionary(); - DataArray = new List(); - StringValue = String.Empty; - } - } - - /// - /// Parse a JSON string to build a JSON object - /// - /// String to parse - /// Cursor start (set to 0 for function init) - private static JSONData String2Data(string toparse, ref int cursorpos) - { - try - { - JSONData data; - SkipSpaces(toparse, ref cursorpos); - switch (toparse[cursorpos]) - { - //Object - case '{': - data = new JSONData(JSONData.DataType.Object); - cursorpos++; - SkipSpaces(toparse, ref cursorpos); - while (toparse[cursorpos] != '}') - { - if (toparse[cursorpos] == '"') - { - JSONData propertyname = String2Data(toparse, ref cursorpos); - if (toparse[cursorpos] == ':') - { - cursorpos++; - } - else - { - /* parse error ? */ - } - - JSONData propertyData = String2Data(toparse, ref cursorpos); - data.Properties[propertyname.StringValue] = propertyData; - } - else cursorpos++; - } - - cursorpos++; - break; - - //Array - case '[': - data = new JSONData(JSONData.DataType.Array); - cursorpos++; - SkipSpaces(toparse, ref cursorpos); - while (toparse[cursorpos] != ']') - { - if (toparse[cursorpos] == ',') - { - cursorpos++; - } - - JSONData arrayItem = String2Data(toparse, ref cursorpos); - data.DataArray.Add(arrayItem); - } - - cursorpos++; - break; - - //String - case '"': - data = new JSONData(JSONData.DataType.String); - cursorpos++; - while (toparse[cursorpos] != '"') - { - if (toparse[cursorpos] == '\\') - { - try //Unicode character \u0123 - { - if (toparse[cursorpos + 1] == 'u' - && IsHex(toparse[cursorpos + 2]) - && IsHex(toparse[cursorpos + 3]) - && IsHex(toparse[cursorpos + 4]) - && IsHex(toparse[cursorpos + 5])) - { - //"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string - data.StringValue += char.ConvertFromUtf32(int.Parse( - toparse.Substring(cursorpos + 2, 4), - System.Globalization.NumberStyles.HexNumber)); - cursorpos += 6; - continue; - } - else if (toparse[cursorpos + 1] == 'n') - { - data.StringValue += '\n'; - cursorpos += 2; - continue; - } - else if (toparse[cursorpos + 1] == 'r') - { - data.StringValue += '\r'; - cursorpos += 2; - continue; - } - else if (toparse[cursorpos + 1] == 't') - { - data.StringValue += '\t'; - cursorpos += 2; - continue; - } - else cursorpos++; //Normal character escapement \" - } - catch (IndexOutOfRangeException) - { - cursorpos++; - } // \u01 - catch (ArgumentOutOfRangeException) - { - cursorpos++; - } // Unicode index 0123 was invalid - } - - data.StringValue += toparse[cursorpos]; - cursorpos++; - } - - cursorpos++; - break; - - //Number - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - case '-': - data = new JSONData(JSONData.DataType.String); - StringBuilder sb = new(); - while ((toparse[cursorpos] >= '0' && toparse[cursorpos] <= '9') || toparse[cursorpos] == '.' || - toparse[cursorpos] == '-') - { - sb.Append(toparse[cursorpos]); - cursorpos++; - } - - data.StringValue = sb.ToString(); - break; - - //Boolean : true - case 't': - data = new JSONData(JSONData.DataType.String); - cursorpos++; - if (toparse[cursorpos] == 'r') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'u') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'e') - { - cursorpos++; - data.StringValue = "true"; - } - - break; - - //Boolean : false - case 'f': - data = new JSONData(JSONData.DataType.String); - cursorpos++; - if (toparse[cursorpos] == 'a') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'l') - { - cursorpos++; - } - - if (toparse[cursorpos] == 's') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'e') - { - cursorpos++; - data.StringValue = "false"; - } - - break; - - //Null field - case 'n': - data = new JSONData(JSONData.DataType.String); - cursorpos++; - if (toparse[cursorpos] == 'u') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'l') - { - cursorpos++; - } - - if (toparse[cursorpos] == 'l') - { - cursorpos++; - data.StringValue = "null"; - } - - break; - - //Unknown data - default: - cursorpos++; - return String2Data(toparse, ref cursorpos); - } - - SkipSpaces(toparse, ref cursorpos); - return data; - } - catch (IndexOutOfRangeException) - { - return new JSONData(JSONData.DataType.String); - } - } - - /// - /// Check if a char is an hexadecimal char (0-9 A-F a-f) - /// - /// Char to test - /// True if hexadecimal - private static bool IsHex(char c) - { - return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); - } - - /// - /// Advance the cursor to skip white spaces and line breaks - /// - /// String to parse - /// Cursor position to update - private static void SkipSpaces(string toparse, ref int cursorpos) - { - while (cursorpos < toparse.Length - && (char.IsWhiteSpace(toparse[cursorpos]) - || toparse[cursorpos] == '\r' - || toparse[cursorpos] == '\n')) - cursorpos++; - } - - // Original: https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs - private static bool NeedEscape(string src, int i) - { - var c = src[i]; - return c < 32 || c == '"' || c == '\\' - // Broken lead surrogate - || (c is >= '\uD800' and <= '\uDBFF' && - (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF')) - // Broken tail surrogate - || (c is >= '\uDC00' and <= '\uDFFF' && - (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF')) - // To produce valid JavaScript - || c == '\u2028' || c == '\u2029' - // Escape " tags - || (c == '/' && i > 0 && src[i - 1] == '<'); - } - - public static string EscapeString(string src) - { - var sb = new StringBuilder(); - var start = 0; - - for (var i = 0; i < src.Length; i++) - { - if (!NeedEscape(src, i)) continue; - sb.Append(src, start, i - start); - - switch (src[i]) - { - case '\b': - sb.Append("\\b"); - break; - case '\f': - sb.Append("\\f"); - break; - case '\n': - sb.Append("\\n"); - break; - case '\r': - sb.Append("\\r"); - break; - case '\t': - sb.Append("\\t"); - break; - case '\"': - sb.Append("\\\""); - break; - case '\\': - sb.Append("\\\\"); - break; - case '/': - sb.Append("\\/"); - break; - - default: - sb.Append("\\u"); - sb.Append(((int)src[i]).ToString("x04")); - break; - } - - start = i + 1; - } - - sb.Append(src, start, src.Length - start); - return sb.ToString(); - } - } -} \ No newline at end of file + null => "null", + JsonValue val when val.TryGetValue(out var s) => s, + _ => node.ToJsonString() + }; +} diff --git a/MinecraftClient/LegacyAchievementCatalog.cs b/MinecraftClient/LegacyAchievementCatalog.cs new file mode 100644 index 00000000..bce17f8d --- /dev/null +++ b/MinecraftClient/LegacyAchievementCatalog.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient +{ + internal static class LegacyAchievementCatalog + { + public static IReadOnlyList Ids { get; } = + [ + "achievement.openInventory", + "achievement.mineWood", + "achievement.buildWorkBench", + "achievement.buildPickaxe", + "achievement.buildFurnace", + "achievement.acquireIron", + "achievement.buildHoe", + "achievement.makeBread", + "achievement.bakeCake", + "achievement.buildBetterPickaxe", + "achievement.cookFish", + "achievement.onARail", + "achievement.buildSword", + "achievement.killEnemy", + "achievement.killCow", + "achievement.flyPig", + "achievement.snipeSkeleton", + "achievement.diamonds", + "achievement.diamondsToYou", + "achievement.portal", + "achievement.ghast", + "achievement.blazeRod", + "achievement.potion", + "achievement.theEnd", + "achievement.theEnd2", + "achievement.enchantments", + "achievement.overkill", + "achievement.bookcase", + "achievement.breedCow", + "achievement.spawnWither", + "achievement.killWither", + "achievement.fullBeacon", + "achievement.exploreAllBiomes", + "achievement.overpowered" + ]; + + private static readonly HashSet s_idSet = new(Ids, StringComparer.Ordinal); + + public static bool Contains(string id) + { + return s_idSet.Contains(id); + } + } +} diff --git a/MinecraftClient/Logger/FileLogLogger.cs b/MinecraftClient/Logger/FileLogLogger.cs index 9614a5c4..ab660bdf 100644 --- a/MinecraftClient/Logger/FileLogLogger.cs +++ b/MinecraftClient/Logger/FileLogLogger.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using MinecraftClient.Scripting; namespace MinecraftClient.Logger @@ -8,7 +9,7 @@ namespace MinecraftClient.Logger { private readonly string logFile; private readonly bool prependTimestamp; - private readonly object logFileLock = new(); + private readonly Lock logFileLock = new(); public FileLogLogger(string file, bool prependTimestamp = false) { @@ -71,7 +72,8 @@ namespace MinecraftClient.Logger { if (ShouldDisplay(FilterChannel.Chat, msg)) { - LogAndSave(msg); + ConsoleIO.WriteChatLineIfVisible(msg); + Save(msg); } else Debug("[Logger] One Chat message filtered: " + msg); } @@ -88,6 +90,17 @@ namespace MinecraftClient.Logger } } + public override void PacketDebug(string msg) + { + if (Settings.Config.Logging.PacketDebugMessages) + { + if (ShouldDisplay(FilterChannel.Debug, msg)) + { + LogAndSave("§8[DEBUG] " + msg); + } + } + } + public override void Error(string msg) { base.Error(msg); diff --git a/MinecraftClient/Logger/FilteredLogger.cs b/MinecraftClient/Logger/FilteredLogger.cs index 168e126b..a520434c 100644 --- a/MinecraftClient/Logger/FilteredLogger.cs +++ b/MinecraftClient/Logger/FilteredLogger.cs @@ -31,7 +31,7 @@ namespace MinecraftClient.Logger regexToUse = new(debug); break; } - if (regexToUse != null) + if (regexToUse is not null) { // IsMatch and white/blacklist result can be represented using XOR // e.g. matched(true) ^ blacklist(true) => shouldn't log(false) @@ -57,6 +57,17 @@ namespace MinecraftClient.Logger } } + public override void PacketDebug(string msg) + { + if (Settings.Config.Logging.PacketDebugMessages) + { + if (ShouldDisplay(FilterChannel.Debug, msg)) + { + Log("§8[DEBUG] " + msg); + } + } + } + public override void Info(string msg) { if (InfoEnabled) @@ -81,7 +92,7 @@ namespace MinecraftClient.Logger { if (ShouldDisplay(FilterChannel.Chat, msg)) { - Log(msg); + ConsoleIO.WriteChatLineIfVisible(msg); } else Debug("[Logger] One Chat message filtered: " + msg); } diff --git a/MinecraftClient/Logger/ILogger.cs b/MinecraftClient/Logger/ILogger.cs index ba3f143f..89a1b686 100644 --- a/MinecraftClient/Logger/ILogger.cs +++ b/MinecraftClient/Logger/ILogger.cs @@ -16,6 +16,10 @@ void Debug(string msg, params object[] args); void Debug(object msg); + void PacketDebug(string msg); + void PacketDebug(string msg, params object[] args); + void PacketDebug(object msg); + void Warn(string msg); void Warn(string msg, params object[] args); void Warn(object msg); diff --git a/MinecraftClient/Logger/LoggerBase.cs b/MinecraftClient/Logger/LoggerBase.cs index 8569bfc9..500c43b6 100644 --- a/MinecraftClient/Logger/LoggerBase.cs +++ b/MinecraftClient/Logger/LoggerBase.cs @@ -40,6 +40,18 @@ Debug(msg.ToString() ?? string.Empty); } + public abstract void PacketDebug(string msg); + + public void PacketDebug(string msg, params object[] args) + { + PacketDebug(string.Format(msg, args)); + } + + public void PacketDebug(object msg) + { + PacketDebug(msg.ToString() ?? string.Empty); + } + public abstract void Error(string msg); public void Error(string msg, params object[] args) diff --git a/MinecraftClient/Mapping/BlockHardness.cs b/MinecraftClient/Mapping/BlockHardness.cs new file mode 100644 index 00000000..97d1df94 --- /dev/null +++ b/MinecraftClient/Mapping/BlockHardness.cs @@ -0,0 +1,1582 @@ +using System.Collections.Frozen; +using System.Collections.Generic; + +namespace MinecraftClient.Mapping +{ + /// + /// Provides block hardness values and tool requirement data for mining calculations. + /// Data extracted from Minecraft 1.21.11 decompiled Blocks.java. + /// + public static class BlockHardness + { + /// + /// Default hardness for blocks not in the table. Uses stone-like hardness as a safe fallback. + /// + public const float DefaultHardness = 1.5f; + + /// + /// Get the hardness value for a block material. + /// Returns -1 for unbreakable blocks, 0 for instant-break blocks. + /// + public static float GetHardness(Material material) + { + if (HardnessTable.TryGetValue(material, out float hardness)) + return hardness; + return DefaultHardness; + } + + /// + /// Check whether a block requires the correct tool to get drops + /// and uses the 100 divisor instead of 30 when mined without the correct tool. + /// + public static bool RequiresCorrectTool(Material material) + { + return RequiresCorrectToolSet.Contains(material); + } + + private static readonly FrozenDictionary HardnessTable = new Dictionary + { + { Material.AcaciaButton, 0.5f }, + { Material.AcaciaDoor, 3.0f }, + { Material.AcaciaFence, 2.0f }, + { Material.AcaciaFenceGate, 2.0f }, + { Material.AcaciaHangingSign, 1.0f }, + { Material.AcaciaLeaves, 0.2f }, + { Material.AcaciaLog, 2.0f }, + { Material.AcaciaPlanks, 2.0f }, + { Material.AcaciaPressurePlate, 0.5f }, + { Material.AcaciaSapling, 0.0f }, + { Material.AcaciaShelf, 2.0f }, + { Material.AcaciaSign, 1.0f }, + { Material.AcaciaSlab, 2.0f }, + { Material.AcaciaStairs, 2.0f }, + { Material.AcaciaTrapdoor, 3.0f }, + { Material.AcaciaWallHangingSign, 1.0f }, + { Material.AcaciaWallSign, 1.0f }, + { Material.AcaciaWood, 2.0f }, + { Material.ActivatorRail, 0.7f }, + { Material.Air, 0.0f }, + { Material.Allium, 0.0f }, + { Material.AmethystBlock, 1.5f }, + { Material.AmethystCluster, 1.5f }, + { Material.AncientDebris, 30.0f }, + { Material.Andesite, 1.5f }, + { Material.AndesiteSlab, 1.5f }, + { Material.AndesiteStairs, 1.5f }, + { Material.AndesiteWall, 1.5f }, + { Material.Anvil, 5.0f }, + { Material.AttachedMelonStem, 0.0f }, + { Material.AttachedPumpkinStem, 0.0f }, + { Material.Azalea, 0.0f }, + { Material.AzaleaLeaves, 0.2f }, + { Material.AzureBluet, 0.0f }, + { Material.Bamboo, 0.0f }, + { Material.BambooBlock, 2.0f }, + { Material.BambooButton, 0.5f }, + { Material.BambooDoor, 3.0f }, + { Material.BambooFence, 2.0f }, + { Material.BambooFenceGate, 2.0f }, + { Material.BambooHangingSign, 1.0f }, + { Material.BambooMosaic, 2.0f }, + { Material.BambooMosaicSlab, 2.0f }, + { Material.BambooMosaicStairs, 2.0f }, + { Material.BambooPlanks, 2.0f }, + { Material.BambooPressurePlate, 0.5f }, + { Material.BambooSapling, 0.0f }, + { Material.BambooShelf, 2.0f }, + { Material.BambooSign, 1.0f }, + { Material.BambooSlab, 2.0f }, + { Material.BambooStairs, 2.0f }, + { Material.BambooTrapdoor, 3.0f }, + { Material.BambooWallHangingSign, 1.0f }, + { Material.BambooWallSign, 1.0f }, + { Material.Barrel, 2.5f }, + { Material.Barrier, -1.0f }, + { Material.Basalt, 1.25f }, + { Material.Beacon, 3.0f }, + { Material.Bedrock, -1.0f }, + { Material.BeeNest, 0.3f }, + { Material.Beehive, 0.6f }, + { Material.Beetroots, 0.0f }, + { Material.Bell, 5.0f }, + { Material.BigDripleaf, 0.1f }, + { Material.BigDripleafStem, 0.1f }, + { Material.BirchButton, 0.5f }, + { Material.BirchDoor, 3.0f }, + { Material.BirchFence, 2.0f }, + { Material.BirchFenceGate, 2.0f }, + { Material.BirchHangingSign, 1.0f }, + { Material.BirchLeaves, 0.2f }, + { Material.BirchLog, 2.0f }, + { Material.BirchPlanks, 2.0f }, + { Material.BirchPressurePlate, 0.5f }, + { Material.BirchSapling, 0.0f }, + { Material.BirchShelf, 2.0f }, + { Material.BirchSign, 1.0f }, + { Material.BirchSlab, 2.0f }, + { Material.BirchStairs, 2.0f }, + { Material.BirchTrapdoor, 3.0f }, + { Material.BirchWallHangingSign, 1.0f }, + { Material.BirchWallSign, 1.0f }, + { Material.BirchWood, 2.0f }, + { Material.BlackBanner, 1.0f }, + { Material.BlackBed, 0.2f }, + { Material.BlackCandle, 0.1f }, + { Material.BlackCandleCake, 0.5f }, + { Material.BlackCarpet, 0.1f }, + { Material.BlackConcrete, 1.8f }, + { Material.BlackConcretePowder, 0.5f }, + { Material.BlackGlazedTerracotta, 1.4f }, + { Material.BlackShulkerBox, 2.0f }, + { Material.BlackStainedGlass, 0.3f }, + { Material.BlackStainedGlassPane, 0.3f }, + { Material.BlackTerracotta, 1.25f }, + { Material.BlackWallBanner, 1.0f }, + { Material.BlackWool, 0.8f }, + { Material.Blackstone, 1.5f }, + { Material.BlackstoneSlab, 2.0f }, + { Material.BlackstoneStairs, 1.5f }, + { Material.BlackstoneWall, 1.5f }, + { Material.BlastFurnace, 3.5f }, + { Material.BlueBanner, 1.0f }, + { Material.BlueBed, 0.2f }, + { Material.BlueCandle, 0.1f }, + { Material.BlueCandleCake, 0.5f }, + { Material.BlueCarpet, 0.1f }, + { Material.BlueConcrete, 1.8f }, + { Material.BlueConcretePowder, 0.5f }, + { Material.BlueGlazedTerracotta, 1.4f }, + { Material.BlueIce, 2.8f }, + { Material.BlueOrchid, 0.0f }, + { Material.BlueShulkerBox, 2.0f }, + { Material.BlueStainedGlass, 0.3f }, + { Material.BlueStainedGlassPane, 0.3f }, + { Material.BlueTerracotta, 1.25f }, + { Material.BlueWallBanner, 1.0f }, + { Material.BlueWool, 0.8f }, + { Material.BoneBlock, 2.0f }, + { Material.Bookshelf, 1.5f }, + { Material.BrainCoral, 0.0f }, + { Material.BrainCoralBlock, 1.5f }, + { Material.BrainCoralFan, 0.0f }, + { Material.BrainCoralWallFan, 0.0f }, + { Material.BrewingStand, 0.5f }, + { Material.BrickSlab, 2.0f }, + { Material.BrickStairs, 2.0f }, + { Material.BrickWall, 2.0f }, + { Material.Bricks, 2.0f }, + { Material.BrownBanner, 1.0f }, + { Material.BrownBed, 0.2f }, + { Material.BrownCandle, 0.1f }, + { Material.BrownCandleCake, 0.5f }, + { Material.BrownCarpet, 0.1f }, + { Material.BrownConcrete, 1.8f }, + { Material.BrownConcretePowder, 0.5f }, + { Material.BrownGlazedTerracotta, 1.4f }, + { Material.BrownMushroom, 0.0f }, + { Material.BrownMushroomBlock, 0.2f }, + { Material.BrownShulkerBox, 2.0f }, + { Material.BrownStainedGlass, 0.3f }, + { Material.BrownStainedGlassPane, 0.3f }, + { Material.BrownTerracotta, 1.25f }, + { Material.BrownWallBanner, 1.0f }, + { Material.BrownWool, 0.8f }, + { Material.BubbleColumn, 0.0f }, + { Material.BubbleCoral, 0.0f }, + { Material.BubbleCoralBlock, 1.5f }, + { Material.BubbleCoralFan, 0.0f }, + { Material.BubbleCoralWallFan, 0.0f }, + { Material.BuddingAmethyst, 1.5f }, + { Material.Bush, 0.0f }, + { Material.Cactus, 0.4f }, + { Material.CactusFlower, 0.0f }, + { Material.Cake, 0.5f }, + { Material.Calcite, 0.75f }, + { Material.CalibratedSculkSensor, 1.5f }, + { Material.Campfire, 2.0f }, + { Material.Candle, 0.1f }, + { Material.CandleCake, 0.5f }, + { Material.Carrots, 0.0f }, + { Material.CartographyTable, 2.5f }, + { Material.CarvedPumpkin, 1.0f }, + { Material.Cauldron, 2.0f }, + { Material.CaveAir, 0.0f }, + { Material.CaveVines, 0.0f }, + { Material.CaveVinesPlant, 0.0f }, + { Material.ChainCommandBlock, -1.0f }, + { Material.CherryButton, 0.5f }, + { Material.CherryDoor, 3.0f }, + { Material.CherryFence, 2.0f }, + { Material.CherryFenceGate, 2.0f }, + { Material.CherryHangingSign, 1.0f }, + { Material.CherryLeaves, 0.2f }, + { Material.CherryLog, 2.0f }, + { Material.CherryPlanks, 2.0f }, + { Material.CherryPressurePlate, 0.5f }, + { Material.CherrySapling, 0.0f }, + { Material.CherryShelf, 2.0f }, + { Material.CherrySign, 1.0f }, + { Material.CherrySlab, 2.0f }, + { Material.CherryStairs, 2.0f }, + { Material.CherryTrapdoor, 3.0f }, + { Material.CherryWallHangingSign, 1.0f }, + { Material.CherryWallSign, 1.0f }, + { Material.CherryWood, 2.0f }, + { Material.Chest, 2.5f }, + { Material.ChippedAnvil, 5.0f }, + { Material.ChiseledBookshelf, 1.5f }, + { Material.ChiseledCopper, 3.0f }, + { Material.ChiseledDeepslate, 3.5f }, + { Material.ChiseledNetherBricks, 2.0f }, + { Material.ChiseledPolishedBlackstone, 1.5f }, + { Material.ChiseledQuartzBlock, 0.8f }, + { Material.ChiseledRedSandstone, 0.8f }, + { Material.ChiseledResinBricks, 1.5f }, + { Material.ChiseledSandstone, 0.8f }, + { Material.ChiseledStoneBricks, 1.5f }, + { Material.ChiseledTuff, 1.5f }, + { Material.ChiseledTuffBricks, 1.5f }, + { Material.ChorusFlower, 0.4f }, + { Material.ChorusPlant, 0.4f }, + { Material.Clay, 0.6f }, + { Material.ClosedEyeblossom, 0.0f }, + { Material.CoalBlock, 5.0f }, + { Material.CoalOre, 3.0f }, + { Material.CoarseDirt, 0.5f }, + { Material.CobbledDeepslate, 3.5f }, + { Material.CobbledDeepslateSlab, 3.5f }, + { Material.CobbledDeepslateStairs, 3.5f }, + { Material.CobbledDeepslateWall, 3.5f }, + { Material.Cobblestone, 2.0f }, + { Material.CobblestoneSlab, 2.0f }, + { Material.CobblestoneStairs, 2.0f }, + { Material.CobblestoneWall, 2.0f }, + { Material.Cobweb, 4.0f }, + { Material.Cocoa, 0.2f }, + { Material.CommandBlock, -1.0f }, + { Material.Comparator, 0.0f }, + { Material.Composter, 0.6f }, + { Material.Conduit, 3.0f }, + { Material.CopperBlock, 3.0f }, + { Material.CopperBulb, 3.0f }, + { Material.CopperChest, 3.0f }, + { Material.CopperDoor, 3.0f }, + { Material.CopperGolemStatue, 3.0f }, + { Material.CopperGrate, 3.0f }, + { Material.CopperOre, 3.0f }, + { Material.CopperTorch, 0.0f }, + { Material.CopperTrapdoor, 3.0f }, + { Material.CopperWallTorch, 0.0f }, + { Material.Cornflower, 0.0f }, + { Material.CrackedDeepslateBricks, 3.5f }, + { Material.CrackedDeepslateTiles, 3.5f }, + { Material.CrackedNetherBricks, 2.0f }, + { Material.CrackedPolishedBlackstoneBricks, 1.5f }, + { Material.CrackedStoneBricks, 1.5f }, + { Material.Crafter, 1.5f }, + { Material.CraftingTable, 2.5f }, + { Material.CreakingHeart, 10.0f }, + { Material.CreeperHead, 1.0f }, + { Material.CreeperWallHead, 1.0f }, + { Material.CrimsonButton, 0.5f }, + { Material.CrimsonDoor, 3.0f }, + { Material.CrimsonFence, 2.0f }, + { Material.CrimsonFenceGate, 2.0f }, + { Material.CrimsonFungus, 0.0f }, + { Material.CrimsonHangingSign, 1.0f }, + { Material.CrimsonHyphae, 2.0f }, + { Material.CrimsonNylium, 0.4f }, + { Material.CrimsonPlanks, 2.0f }, + { Material.CrimsonPressurePlate, 0.5f }, + { Material.CrimsonRoots, 0.0f }, + { Material.CrimsonShelf, 2.0f }, + { Material.CrimsonSign, 1.0f }, + { Material.CrimsonSlab, 2.0f }, + { Material.CrimsonStairs, 2.0f }, + { Material.CrimsonStem, 2.0f }, + { Material.CrimsonTrapdoor, 3.0f }, + { Material.CrimsonWallHangingSign, 1.0f }, + { Material.CrimsonWallSign, 1.0f }, + { Material.CryingObsidian, 50.0f }, + { Material.CutCopper, 3.0f }, + { Material.CutCopperSlab, 3.0f }, + { Material.CutCopperStairs, 3.0f }, + { Material.CutRedSandstone, 0.8f }, + { Material.CutRedSandstoneSlab, 2.0f }, + { Material.CutSandstone, 0.8f }, + { Material.CutSandstoneSlab, 2.0f }, + { Material.CyanBanner, 1.0f }, + { Material.CyanBed, 0.2f }, + { Material.CyanCandle, 0.1f }, + { Material.CyanCandleCake, 0.5f }, + { Material.CyanCarpet, 0.1f }, + { Material.CyanConcrete, 1.8f }, + { Material.CyanConcretePowder, 0.5f }, + { Material.CyanGlazedTerracotta, 1.4f }, + { Material.CyanShulkerBox, 2.0f }, + { Material.CyanStainedGlass, 0.3f }, + { Material.CyanStainedGlassPane, 0.3f }, + { Material.CyanTerracotta, 1.25f }, + { Material.CyanWallBanner, 1.0f }, + { Material.CyanWool, 0.8f }, + { Material.DamagedAnvil, 5.0f }, + { Material.Dandelion, 0.0f }, + { Material.DarkOakButton, 0.5f }, + { Material.DarkOakDoor, 3.0f }, + { Material.DarkOakFence, 2.0f }, + { Material.DarkOakFenceGate, 2.0f }, + { Material.DarkOakHangingSign, 1.0f }, + { Material.DarkOakLeaves, 0.2f }, + { Material.DarkOakLog, 2.0f }, + { Material.DarkOakPlanks, 2.0f }, + { Material.DarkOakPressurePlate, 0.5f }, + { Material.DarkOakSapling, 0.0f }, + { Material.DarkOakShelf, 2.0f }, + { Material.DarkOakSign, 1.0f }, + { Material.DarkOakSlab, 2.0f }, + { Material.DarkOakStairs, 2.0f }, + { Material.DarkOakTrapdoor, 3.0f }, + { Material.DarkOakWallHangingSign, 1.0f }, + { Material.DarkOakWallSign, 1.0f }, + { Material.DarkOakWood, 2.0f }, + { Material.DarkPrismarine, 1.5f }, + { Material.DarkPrismarineSlab, 1.5f }, + { Material.DarkPrismarineStairs, 1.5f }, + { Material.DaylightDetector, 0.2f }, + { Material.DeadBrainCoral, 0.0f }, + { Material.DeadBrainCoralBlock, 1.5f }, + { Material.DeadBrainCoralFan, 0.0f }, + { Material.DeadBrainCoralWallFan, 0.0f }, + { Material.DeadBubbleCoral, 0.0f }, + { Material.DeadBubbleCoralBlock, 1.5f }, + { Material.DeadBubbleCoralFan, 0.0f }, + { Material.DeadBubbleCoralWallFan, 0.0f }, + { Material.DeadBush, 0.0f }, + { Material.DeadFireCoral, 0.0f }, + { Material.DeadFireCoralBlock, 1.5f }, + { Material.DeadFireCoralFan, 0.0f }, + { Material.DeadFireCoralWallFan, 0.0f }, + { Material.DeadHornCoral, 0.0f }, + { Material.DeadHornCoralBlock, 1.5f }, + { Material.DeadHornCoralFan, 0.0f }, + { Material.DeadHornCoralWallFan, 0.0f }, + { Material.DeadTubeCoral, 0.0f }, + { Material.DeadTubeCoralBlock, 1.5f }, + { Material.DeadTubeCoralFan, 0.0f }, + { Material.DeadTubeCoralWallFan, 0.0f }, + { Material.DecoratedPot, 0.0f }, + { Material.Deepslate, 3.0f }, + { Material.DeepslateBrickSlab, 3.5f }, + { Material.DeepslateBrickStairs, 3.5f }, + { Material.DeepslateBrickWall, 3.5f }, + { Material.DeepslateBricks, 3.5f }, + { Material.DeepslateCoalOre, 4.5f }, + { Material.DeepslateCopperOre, 4.5f }, + { Material.DeepslateDiamondOre, 4.5f }, + { Material.DeepslateEmeraldOre, 4.5f }, + { Material.DeepslateGoldOre, 4.5f }, + { Material.DeepslateIronOre, 4.5f }, + { Material.DeepslateLapisOre, 4.5f }, + { Material.DeepslateRedstoneOre, 4.5f }, + { Material.DeepslateTileSlab, 3.5f }, + { Material.DeepslateTileStairs, 3.5f }, + { Material.DeepslateTileWall, 3.5f }, + { Material.DeepslateTiles, 3.5f }, + { Material.DetectorRail, 0.7f }, + { Material.DiamondBlock, 5.0f }, + { Material.DiamondOre, 3.0f }, + { Material.Diorite, 1.5f }, + { Material.DioriteSlab, 1.5f }, + { Material.DioriteStairs, 1.5f }, + { Material.DioriteWall, 1.5f }, + { Material.Dirt, 0.5f }, + { Material.DirtPath, 0.65f }, + { Material.Dispenser, 3.5f }, + { Material.DragonEgg, 3.0f }, + { Material.DragonHead, 1.0f }, + { Material.DragonWallHead, 1.0f }, + { Material.DriedGhast, 0.0f }, + { Material.DriedKelpBlock, 0.5f }, + { Material.DripstoneBlock, 1.5f }, + { Material.Dropper, 3.5f }, + { Material.EmeraldBlock, 5.0f }, + { Material.EmeraldOre, 3.0f }, + { Material.EnchantingTable, 5.0f }, + { Material.EndGateway, -1.0f }, + { Material.EndPortal, -1.0f }, + { Material.EndPortalFrame, -1.0f }, + { Material.EndRod, 0.0f }, + { Material.EndStone, 3.0f }, + { Material.EndStoneBrickSlab, 3.0f }, + { Material.EndStoneBrickStairs, 3.0f }, + { Material.EndStoneBrickWall, 3.0f }, + { Material.EndStoneBricks, 3.0f }, + { Material.EnderChest, 22.5f }, + { Material.ExposedChiseledCopper, 3.0f }, + { Material.ExposedCopper, 3.0f }, + { Material.ExposedCopperBulb, 3.0f }, + { Material.ExposedCopperChest, 3.0f }, + { Material.ExposedCopperDoor, 3.0f }, + { Material.ExposedCopperGolemStatue, 3.0f }, + { Material.ExposedCopperGrate, 3.0f }, + { Material.ExposedCopperTrapdoor, 3.0f }, + { Material.ExposedCutCopper, 3.0f }, + { Material.ExposedCutCopperSlab, 3.0f }, + { Material.ExposedCutCopperStairs, 3.0f }, + { Material.ExposedLightningRod, 3.0f }, + { Material.Farmland, 0.6f }, + { Material.Fern, 0.0f }, + { Material.Fire, 0.0f }, + { Material.FireCoral, 0.0f }, + { Material.FireCoralBlock, 1.5f }, + { Material.FireCoralFan, 0.0f }, + { Material.FireCoralWallFan, 0.0f }, + { Material.FireflyBush, 0.0f }, + { Material.FletchingTable, 2.5f }, + { Material.FlowerPot, 0.0f }, + { Material.FloweringAzalea, 0.0f }, + { Material.FloweringAzaleaLeaves, 0.2f }, + { Material.Frogspawn, 0.0f }, + { Material.FrostedIce, 0.5f }, + { Material.Furnace, 3.5f }, + { Material.GildedBlackstone, 1.5f }, + { Material.Glass, 0.3f }, + { Material.GlassPane, 0.3f }, + { Material.GlowLichen, 0.2f }, + { Material.Glowstone, 0.3f }, + { Material.GoldBlock, 3.0f }, + { Material.GoldOre, 3.0f }, + { Material.Granite, 1.5f }, + { Material.GraniteSlab, 1.5f }, + { Material.GraniteStairs, 1.5f }, + { Material.GraniteWall, 1.5f }, + { Material.GrassBlock, 0.6f }, + { Material.Gravel, 0.6f }, + { Material.GrayBanner, 1.0f }, + { Material.GrayBed, 0.2f }, + { Material.GrayCandle, 0.1f }, + { Material.GrayCandleCake, 0.5f }, + { Material.GrayCarpet, 0.1f }, + { Material.GrayConcrete, 1.8f }, + { Material.GrayConcretePowder, 0.5f }, + { Material.GrayGlazedTerracotta, 1.4f }, + { Material.GrayShulkerBox, 2.0f }, + { Material.GrayStainedGlass, 0.3f }, + { Material.GrayStainedGlassPane, 0.3f }, + { Material.GrayTerracotta, 1.25f }, + { Material.GrayWallBanner, 1.0f }, + { Material.GrayWool, 0.8f }, + { Material.GreenBanner, 1.0f }, + { Material.GreenBed, 0.2f }, + { Material.GreenCandle, 0.1f }, + { Material.GreenCandleCake, 0.5f }, + { Material.GreenCarpet, 0.1f }, + { Material.GreenConcrete, 1.8f }, + { Material.GreenConcretePowder, 0.5f }, + { Material.GreenGlazedTerracotta, 1.4f }, + { Material.GreenShulkerBox, 2.0f }, + { Material.GreenStainedGlass, 0.3f }, + { Material.GreenStainedGlassPane, 0.3f }, + { Material.GreenTerracotta, 1.25f }, + { Material.GreenWallBanner, 1.0f }, + { Material.GreenWool, 0.8f }, + { Material.Grindstone, 2.0f }, + { Material.HangingRoots, 0.0f }, + { Material.HayBlock, 0.5f }, + { Material.HeavyCore, 10.0f }, + { Material.HeavyWeightedPressurePlate, 0.5f }, + { Material.HoneyBlock, 0.0f }, + { Material.HoneycombBlock, 0.6f }, + { Material.Hopper, 3.0f }, + { Material.HornCoral, 0.0f }, + { Material.HornCoralBlock, 1.5f }, + { Material.HornCoralFan, 0.0f }, + { Material.HornCoralWallFan, 0.0f }, + { Material.Ice, 0.5f }, + { Material.InfestedChiseledStoneBricks, 0.0f }, + { Material.InfestedCobblestone, 0.0f }, + { Material.InfestedCrackedStoneBricks, 0.0f }, + { Material.InfestedDeepslate, 0.0f }, + { Material.InfestedMossyStoneBricks, 0.0f }, + { Material.InfestedStone, 0.0f }, + { Material.InfestedStoneBricks, 0.0f }, + { Material.IronBars, 5.0f }, + { Material.IronBlock, 5.0f }, + { Material.IronChain, 5.0f }, + { Material.IronDoor, 5.0f }, + { Material.IronOre, 3.0f }, + { Material.IronTrapdoor, 5.0f }, + { Material.JackOLantern, 1.0f }, + { Material.Jigsaw, -1.0f }, + { Material.Jukebox, 2.0f }, + { Material.JungleButton, 0.5f }, + { Material.JungleDoor, 3.0f }, + { Material.JungleFence, 2.0f }, + { Material.JungleFenceGate, 2.0f }, + { Material.JungleHangingSign, 1.0f }, + { Material.JungleLeaves, 0.2f }, + { Material.JungleLog, 2.0f }, + { Material.JunglePlanks, 2.0f }, + { Material.JunglePressurePlate, 0.5f }, + { Material.JungleSapling, 0.0f }, + { Material.JungleShelf, 2.0f }, + { Material.JungleSign, 1.0f }, + { Material.JungleSlab, 2.0f }, + { Material.JungleStairs, 2.0f }, + { Material.JungleTrapdoor, 3.0f }, + { Material.JungleWallHangingSign, 1.0f }, + { Material.JungleWallSign, 1.0f }, + { Material.JungleWood, 2.0f }, + { Material.Kelp, 0.0f }, + { Material.KelpPlant, 0.0f }, + { Material.Ladder, 0.4f }, + { Material.Lantern, 3.5f }, + { Material.LapisBlock, 3.0f }, + { Material.LapisOre, 3.0f }, + { Material.LargeAmethystBud, 1.5f }, + { Material.LargeFern, 0.0f }, + { Material.Lava, 100.0f }, + { Material.LavaCauldron, 2.0f }, + { Material.LeafLitter, 0.0f }, + { Material.Lectern, 2.5f }, + { Material.Lever, 0.5f }, + { Material.Light, -1.0f }, + { Material.LightBlueBanner, 1.0f }, + { Material.LightBlueBed, 0.2f }, + { Material.LightBlueCandle, 0.1f }, + { Material.LightBlueCandleCake, 0.5f }, + { Material.LightBlueCarpet, 0.1f }, + { Material.LightBlueConcrete, 1.8f }, + { Material.LightBlueConcretePowder, 0.5f }, + { Material.LightBlueGlazedTerracotta, 1.4f }, + { Material.LightBlueShulkerBox, 2.0f }, + { Material.LightBlueStainedGlass, 0.3f }, + { Material.LightBlueStainedGlassPane, 0.3f }, + { Material.LightBlueTerracotta, 1.25f }, + { Material.LightBlueWallBanner, 1.0f }, + { Material.LightBlueWool, 0.8f }, + { Material.LightGrayBanner, 1.0f }, + { Material.LightGrayBed, 0.2f }, + { Material.LightGrayCandle, 0.1f }, + { Material.LightGrayCandleCake, 0.5f }, + { Material.LightGrayCarpet, 0.1f }, + { Material.LightGrayConcrete, 1.8f }, + { Material.LightGrayConcretePowder, 0.5f }, + { Material.LightGrayGlazedTerracotta, 1.4f }, + { Material.LightGrayShulkerBox, 2.0f }, + { Material.LightGrayStainedGlass, 0.3f }, + { Material.LightGrayStainedGlassPane, 0.3f }, + { Material.LightGrayTerracotta, 1.25f }, + { Material.LightGrayWallBanner, 1.0f }, + { Material.LightGrayWool, 0.8f }, + { Material.LightWeightedPressurePlate, 0.5f }, + { Material.LightningRod, 3.0f }, + { Material.Lilac, 0.0f }, + { Material.LilyOfTheValley, 0.0f }, + { Material.LilyPad, 0.0f }, + { Material.LimeBanner, 1.0f }, + { Material.LimeBed, 0.2f }, + { Material.LimeCandle, 0.1f }, + { Material.LimeCandleCake, 0.5f }, + { Material.LimeCarpet, 0.1f }, + { Material.LimeConcrete, 1.8f }, + { Material.LimeConcretePowder, 0.5f }, + { Material.LimeGlazedTerracotta, 1.4f }, + { Material.LimeShulkerBox, 2.0f }, + { Material.LimeStainedGlass, 0.3f }, + { Material.LimeStainedGlassPane, 0.3f }, + { Material.LimeTerracotta, 1.25f }, + { Material.LimeWallBanner, 1.0f }, + { Material.LimeWool, 0.8f }, + { Material.Lodestone, 3.5f }, + { Material.Loom, 2.5f }, + { Material.MagentaBanner, 1.0f }, + { Material.MagentaBed, 0.2f }, + { Material.MagentaCandle, 0.1f }, + { Material.MagentaCandleCake, 0.5f }, + { Material.MagentaCarpet, 0.1f }, + { Material.MagentaConcrete, 1.8f }, + { Material.MagentaConcretePowder, 0.5f }, + { Material.MagentaGlazedTerracotta, 1.4f }, + { Material.MagentaShulkerBox, 2.0f }, + { Material.MagentaStainedGlass, 0.3f }, + { Material.MagentaStainedGlassPane, 0.3f }, + { Material.MagentaTerracotta, 1.25f }, + { Material.MagentaWallBanner, 1.0f }, + { Material.MagentaWool, 0.8f }, + { Material.MagmaBlock, 0.5f }, + { Material.MangroveButton, 0.5f }, + { Material.MangroveDoor, 3.0f }, + { Material.MangroveFence, 2.0f }, + { Material.MangroveFenceGate, 2.0f }, + { Material.MangroveHangingSign, 1.0f }, + { Material.MangroveLeaves, 0.2f }, + { Material.MangroveLog, 2.0f }, + { Material.MangrovePlanks, 2.0f }, + { Material.MangrovePressurePlate, 0.5f }, + { Material.MangrovePropagule, 0.0f }, + { Material.MangroveRoots, 0.7f }, + { Material.MangroveShelf, 2.0f }, + { Material.MangroveSign, 1.0f }, + { Material.MangroveSlab, 2.0f }, + { Material.MangroveStairs, 2.0f }, + { Material.MangroveTrapdoor, 3.0f }, + { Material.MangroveWallHangingSign, 1.0f }, + { Material.MangroveWallSign, 1.0f }, + { Material.MangroveWood, 2.0f }, + { Material.MediumAmethystBud, 1.5f }, + { Material.Melon, 1.0f }, + { Material.MelonStem, 0.0f }, + { Material.MossBlock, 0.1f }, + { Material.MossCarpet, 0.1f }, + { Material.MossyCobblestone, 2.0f }, + { Material.MossyCobblestoneSlab, 2.0f }, + { Material.MossyCobblestoneStairs, 2.0f }, + { Material.MossyCobblestoneWall, 2.0f }, + { Material.MossyStoneBrickSlab, 1.5f }, + { Material.MossyStoneBrickStairs, 1.5f }, + { Material.MossyStoneBrickWall, 1.5f }, + { Material.MossyStoneBricks, 1.5f }, + { Material.MovingPiston, -1.0f }, + { Material.Mud, 0.5f }, + { Material.MudBrickSlab, 1.5f }, + { Material.MudBrickStairs, 1.5f }, + { Material.MudBrickWall, 1.5f }, + { Material.MudBricks, 1.5f }, + { Material.MuddyMangroveRoots, 0.7f }, + { Material.MushroomStem, 0.2f }, + { Material.Mycelium, 0.6f }, + { Material.NetherBrickFence, 2.0f }, + { Material.NetherBrickSlab, 2.0f }, + { Material.NetherBrickStairs, 2.0f }, + { Material.NetherBrickWall, 2.0f }, + { Material.NetherBricks, 2.0f }, + { Material.NetherGoldOre, 3.0f }, + { Material.NetherPortal, -1.0f }, + { Material.NetherQuartzOre, 3.0f }, + { Material.NetherSprouts, 0.0f }, + { Material.NetherWart, 0.0f }, + { Material.NetherWartBlock, 1.0f }, + { Material.NetheriteBlock, 50.0f }, + { Material.Netherrack, 0.4f }, + { Material.NoteBlock, 0.8f }, + { Material.OakButton, 0.5f }, + { Material.OakDoor, 3.0f }, + { Material.OakFence, 2.0f }, + { Material.OakFenceGate, 2.0f }, + { Material.OakHangingSign, 1.0f }, + { Material.OakLeaves, 0.2f }, + { Material.OakLog, 2.0f }, + { Material.OakPlanks, 2.0f }, + { Material.OakPressurePlate, 0.5f }, + { Material.OakSapling, 0.0f }, + { Material.OakShelf, 2.0f }, + { Material.OakSign, 1.0f }, + { Material.OakSlab, 2.0f }, + { Material.OakStairs, 2.0f }, + { Material.OakTrapdoor, 3.0f }, + { Material.OakWallHangingSign, 1.0f }, + { Material.OakWallSign, 1.0f }, + { Material.OakWood, 2.0f }, + { Material.Observer, 3.0f }, + { Material.Obsidian, 50.0f }, + { Material.OchreFroglight, 0.3f }, + { Material.OpenEyeblossom, 0.0f }, + { Material.OrangeBanner, 1.0f }, + { Material.OrangeBed, 0.2f }, + { Material.OrangeCandle, 0.1f }, + { Material.OrangeCandleCake, 0.5f }, + { Material.OrangeCarpet, 0.1f }, + { Material.OrangeConcrete, 1.8f }, + { Material.OrangeConcretePowder, 0.5f }, + { Material.OrangeGlazedTerracotta, 1.4f }, + { Material.OrangeShulkerBox, 2.0f }, + { Material.OrangeStainedGlass, 0.3f }, + { Material.OrangeStainedGlassPane, 0.3f }, + { Material.OrangeTerracotta, 1.25f }, + { Material.OrangeTulip, 0.0f }, + { Material.OrangeWallBanner, 1.0f }, + { Material.OrangeWool, 0.8f }, + { Material.OxeyeDaisy, 0.0f }, + { Material.OxidizedChiseledCopper, 3.0f }, + { Material.OxidizedCopper, 3.0f }, + { Material.OxidizedCopperBulb, 3.0f }, + { Material.OxidizedCopperChest, 3.0f }, + { Material.OxidizedCopperDoor, 3.0f }, + { Material.OxidizedCopperGolemStatue, 3.0f }, + { Material.OxidizedCopperGrate, 3.0f }, + { Material.OxidizedCopperTrapdoor, 3.0f }, + { Material.OxidizedCutCopper, 3.0f }, + { Material.OxidizedCutCopperSlab, 3.0f }, + { Material.OxidizedCutCopperStairs, 3.0f }, + { Material.OxidizedLightningRod, 3.0f }, + { Material.PackedIce, 0.5f }, + { Material.PackedMud, 1.0f }, + { Material.PaleHangingMoss, 0.0f }, + { Material.PaleMossBlock, 0.1f }, + { Material.PaleMossCarpet, 0.1f }, + { Material.PaleOakButton, 0.5f }, + { Material.PaleOakDoor, 3.0f }, + { Material.PaleOakFence, 2.0f }, + { Material.PaleOakFenceGate, 2.0f }, + { Material.PaleOakHangingSign, 1.0f }, + { Material.PaleOakLeaves, 0.2f }, + { Material.PaleOakLog, 2.0f }, + { Material.PaleOakPlanks, 2.0f }, + { Material.PaleOakPressurePlate, 0.5f }, + { Material.PaleOakSapling, 0.0f }, + { Material.PaleOakShelf, 2.0f }, + { Material.PaleOakSign, 1.0f }, + { Material.PaleOakSlab, 2.0f }, + { Material.PaleOakStairs, 2.0f }, + { Material.PaleOakTrapdoor, 3.0f }, + { Material.PaleOakWallHangingSign, 1.0f }, + { Material.PaleOakWallSign, 1.0f }, + { Material.PaleOakWood, 2.0f }, + { Material.PearlescentFroglight, 0.3f }, + { Material.Peony, 0.0f }, + { Material.PetrifiedOakSlab, 2.0f }, + { Material.PiglinHead, 1.0f }, + { Material.PiglinWallHead, 1.0f }, + { Material.PinkBanner, 1.0f }, + { Material.PinkBed, 0.2f }, + { Material.PinkCandle, 0.1f }, + { Material.PinkCandleCake, 0.5f }, + { Material.PinkCarpet, 0.1f }, + { Material.PinkConcrete, 1.8f }, + { Material.PinkConcretePowder, 0.5f }, + { Material.PinkGlazedTerracotta, 1.4f }, + { Material.PinkPetals, 0.0f }, + { Material.PinkShulkerBox, 2.0f }, + { Material.PinkStainedGlass, 0.3f }, + { Material.PinkStainedGlassPane, 0.3f }, + { Material.PinkTerracotta, 1.25f }, + { Material.PinkTulip, 0.0f }, + { Material.PinkWallBanner, 1.0f }, + { Material.PinkWool, 0.8f }, + { Material.Piston, 0.0f }, + { Material.PistonHead, 1.5f }, + { Material.PitcherCrop, 0.0f }, + { Material.PitcherPlant, 0.0f }, + { Material.PlayerHead, 1.0f }, + { Material.PlayerWallHead, 1.0f }, + { Material.Podzol, 0.5f }, + { Material.PointedDripstone, 1.5f }, + { Material.PolishedAndesite, 1.5f }, + { Material.PolishedAndesiteSlab, 1.5f }, + { Material.PolishedAndesiteStairs, 1.5f }, + { Material.PolishedBasalt, 1.25f }, + { Material.PolishedBlackstone, 2.0f }, + { Material.PolishedBlackstoneBrickSlab, 2.0f }, + { Material.PolishedBlackstoneBrickStairs, 1.5f }, + { Material.PolishedBlackstoneBrickWall, 1.5f }, + { Material.PolishedBlackstoneBricks, 1.5f }, + { Material.PolishedBlackstoneButton, 0.5f }, + { Material.PolishedBlackstonePressurePlate, 0.5f }, + { Material.PolishedBlackstoneSlab, 2.0f }, + { Material.PolishedBlackstoneStairs, 2.0f }, + { Material.PolishedBlackstoneWall, 2.0f }, + { Material.PolishedDeepslate, 3.5f }, + { Material.PolishedDeepslateSlab, 3.5f }, + { Material.PolishedDeepslateStairs, 3.5f }, + { Material.PolishedDeepslateWall, 3.5f }, + { Material.PolishedDiorite, 1.5f }, + { Material.PolishedDioriteSlab, 1.5f }, + { Material.PolishedDioriteStairs, 1.5f }, + { Material.PolishedGranite, 1.5f }, + { Material.PolishedGraniteSlab, 1.5f }, + { Material.PolishedGraniteStairs, 1.5f }, + { Material.PolishedTuff, 1.5f }, + { Material.PolishedTuffSlab, 1.5f }, + { Material.PolishedTuffStairs, 1.5f }, + { Material.PolishedTuffWall, 1.5f }, + { Material.Poppy, 0.0f }, + { Material.Potatoes, 0.0f }, + { Material.PottedAcaciaSapling, 0.0f }, + { Material.PottedAllium, 0.0f }, + { Material.PottedAzaleaBush, 0.0f }, + { Material.PottedAzureBluet, 0.0f }, + { Material.PottedBamboo, 0.0f }, + { Material.PottedBirchSapling, 0.0f }, + { Material.PottedBlueOrchid, 0.0f }, + { Material.PottedBrownMushroom, 0.0f }, + { Material.PottedCactus, 0.0f }, + { Material.PottedCherrySapling, 0.0f }, + { Material.PottedClosedEyeblossom, 0.0f }, + { Material.PottedCornflower, 0.0f }, + { Material.PottedCrimsonFungus, 0.0f }, + { Material.PottedCrimsonRoots, 0.0f }, + { Material.PottedDandelion, 0.0f }, + { Material.PottedDarkOakSapling, 0.0f }, + { Material.PottedDeadBush, 0.0f }, + { Material.PottedFern, 0.0f }, + { Material.PottedFloweringAzaleaBush, 0.0f }, + { Material.PottedJungleSapling, 0.0f }, + { Material.PottedLilyOfTheValley, 0.0f }, + { Material.PottedMangrovePropagule, 0.0f }, + { Material.PottedOakSapling, 0.0f }, + { Material.PottedOpenEyeblossom, 0.0f }, + { Material.PottedOrangeTulip, 0.0f }, + { Material.PottedOxeyeDaisy, 0.0f }, + { Material.PottedPaleOakSapling, 0.0f }, + { Material.PottedPinkTulip, 0.0f }, + { Material.PottedPoppy, 0.0f }, + { Material.PottedRedMushroom, 0.0f }, + { Material.PottedRedTulip, 0.0f }, + { Material.PottedSpruceSapling, 0.0f }, + { Material.PottedTorchflower, 0.0f }, + { Material.PottedWarpedFungus, 0.0f }, + { Material.PottedWarpedRoots, 0.0f }, + { Material.PottedWhiteTulip, 0.0f }, + { Material.PottedWitherRose, 0.0f }, + { Material.PowderSnow, 0.25f }, + { Material.PowderSnowCauldron, 2.0f }, + { Material.PoweredRail, 0.7f }, + { Material.Prismarine, 1.5f }, + { Material.PrismarineBrickSlab, 1.5f }, + { Material.PrismarineBrickStairs, 1.5f }, + { Material.PrismarineBricks, 1.5f }, + { Material.PrismarineSlab, 1.5f }, + { Material.PrismarineStairs, 1.5f }, + { Material.PrismarineWall, 1.5f }, + { Material.Pumpkin, 1.0f }, + { Material.PumpkinStem, 0.0f }, + { Material.PurpleBanner, 1.0f }, + { Material.PurpleBed, 0.2f }, + { Material.PurpleCandle, 0.1f }, + { Material.PurpleCandleCake, 0.5f }, + { Material.PurpleCarpet, 0.1f }, + { Material.PurpleConcrete, 1.8f }, + { Material.PurpleConcretePowder, 0.5f }, + { Material.PurpleGlazedTerracotta, 1.4f }, + { Material.PurpleShulkerBox, 2.0f }, + { Material.PurpleStainedGlass, 0.3f }, + { Material.PurpleStainedGlassPane, 0.3f }, + { Material.PurpleTerracotta, 1.25f }, + { Material.PurpleWallBanner, 1.0f }, + { Material.PurpleWool, 0.8f }, + { Material.PurpurBlock, 1.5f }, + { Material.PurpurPillar, 1.5f }, + { Material.PurpurSlab, 2.0f }, + { Material.PurpurStairs, 1.5f }, + { Material.QuartzBlock, 0.8f }, + { Material.QuartzBricks, 0.8f }, + { Material.QuartzPillar, 0.8f }, + { Material.QuartzSlab, 2.0f }, + { Material.QuartzStairs, 0.8f }, + { Material.Rail, 0.7f }, + { Material.RawCopperBlock, 5.0f }, + { Material.RawGoldBlock, 5.0f }, + { Material.RawIronBlock, 5.0f }, + { Material.RedBanner, 1.0f }, + { Material.RedBed, 0.2f }, + { Material.RedCandle, 0.1f }, + { Material.RedCandleCake, 0.5f }, + { Material.RedCarpet, 0.1f }, + { Material.RedConcrete, 1.8f }, + { Material.RedConcretePowder, 0.5f }, + { Material.RedGlazedTerracotta, 1.4f }, + { Material.RedMushroom, 0.0f }, + { Material.RedMushroomBlock, 0.2f }, + { Material.RedNetherBrickSlab, 2.0f }, + { Material.RedNetherBrickStairs, 2.0f }, + { Material.RedNetherBrickWall, 2.0f }, + { Material.RedNetherBricks, 2.0f }, + { Material.RedSand, 0.5f }, + { Material.RedSandstone, 0.8f }, + { Material.RedSandstoneSlab, 2.0f }, + { Material.RedSandstoneStairs, 0.8f }, + { Material.RedSandstoneWall, 0.8f }, + { Material.RedShulkerBox, 2.0f }, + { Material.RedStainedGlass, 0.3f }, + { Material.RedStainedGlassPane, 0.3f }, + { Material.RedTerracotta, 1.25f }, + { Material.RedTulip, 0.0f }, + { Material.RedWallBanner, 1.0f }, + { Material.RedWool, 0.8f }, + { Material.RedstoneBlock, 5.0f }, + { Material.RedstoneLamp, 0.3f }, + { Material.RedstoneOre, 3.0f }, + { Material.RedstoneTorch, 0.0f }, + { Material.RedstoneWallTorch, 0.0f }, + { Material.RedstoneWire, 0.0f }, + { Material.ReinforcedDeepslate, 55.0f }, + { Material.Repeater, 0.0f }, + { Material.RepeatingCommandBlock, -1.0f }, + { Material.ResinBlock, 0.0f }, + { Material.ResinBrickSlab, 1.5f }, + { Material.ResinBrickStairs, 1.5f }, + { Material.ResinBrickWall, 1.5f }, + { Material.ResinBricks, 1.5f }, + { Material.ResinClump, 0.0f }, + { Material.RespawnAnchor, 50.0f }, + { Material.RootedDirt, 0.5f }, + { Material.RoseBush, 0.0f }, + { Material.Sand, 0.5f }, + { Material.Sandstone, 0.8f }, + { Material.SandstoneSlab, 2.0f }, + { Material.SandstoneStairs, 0.8f }, + { Material.SandstoneWall, 0.8f }, + { Material.Scaffolding, 0.0f }, + { Material.Sculk, 0.2f }, + { Material.SculkCatalyst, 3.0f }, + { Material.SculkSensor, 1.5f }, + { Material.SculkShrieker, 3.0f }, + { Material.SculkVein, 0.2f }, + { Material.SeaLantern, 0.3f }, + { Material.SeaPickle, 0.0f }, + { Material.Seagrass, 0.0f }, + { Material.ShortDryGrass, 0.0f }, + { Material.ShortGrass, 0.0f }, + { Material.Shroomlight, 1.0f }, + { Material.ShulkerBox, 2.0f }, + { Material.SkeletonSkull, 1.0f }, + { Material.SkeletonWallSkull, 1.0f }, + { Material.SlimeBlock, 0.0f }, + { Material.SmallAmethystBud, 1.5f }, + { Material.SmallDripleaf, 0.0f }, + { Material.SmithingTable, 2.5f }, + { Material.Smoker, 3.5f }, + { Material.SmoothBasalt, 1.25f }, + { Material.SmoothQuartz, 2.0f }, + { Material.SmoothQuartzSlab, 2.0f }, + { Material.SmoothQuartzStairs, 2.0f }, + { Material.SmoothRedSandstone, 2.0f }, + { Material.SmoothRedSandstoneSlab, 2.0f }, + { Material.SmoothRedSandstoneStairs, 2.0f }, + { Material.SmoothSandstone, 2.0f }, + { Material.SmoothSandstoneSlab, 2.0f }, + { Material.SmoothSandstoneStairs, 2.0f }, + { Material.SmoothStone, 2.0f }, + { Material.SmoothStoneSlab, 2.0f }, + { Material.SnifferEgg, 0.5f }, + { Material.Snow, 0.1f }, + { Material.SnowBlock, 0.2f }, + { Material.SoulCampfire, 2.0f }, + { Material.SoulFire, 0.0f }, + { Material.SoulLantern, 3.5f }, + { Material.SoulSand, 0.5f }, + { Material.SoulSoil, 0.5f }, + { Material.SoulTorch, 0.0f }, + { Material.SoulWallTorch, 0.0f }, + { Material.Spawner, 5.0f }, + { Material.Sponge, 0.6f }, + { Material.SporeBlossom, 0.0f }, + { Material.SpruceButton, 0.5f }, + { Material.SpruceDoor, 3.0f }, + { Material.SpruceFence, 2.0f }, + { Material.SpruceFenceGate, 2.0f }, + { Material.SpruceHangingSign, 1.0f }, + { Material.SpruceLeaves, 0.2f }, + { Material.SpruceLog, 2.0f }, + { Material.SprucePlanks, 2.0f }, + { Material.SprucePressurePlate, 0.5f }, + { Material.SpruceSapling, 0.0f }, + { Material.SpruceShelf, 2.0f }, + { Material.SpruceSign, 1.0f }, + { Material.SpruceSlab, 2.0f }, + { Material.SpruceStairs, 2.0f }, + { Material.SpruceTrapdoor, 3.0f }, + { Material.SpruceWallHangingSign, 1.0f }, + { Material.SpruceWallSign, 1.0f }, + { Material.SpruceWood, 2.0f }, + { Material.StickyPiston, 0.0f }, + { Material.Stone, 1.5f }, + { Material.StoneBrickSlab, 2.0f }, + { Material.StoneBrickStairs, 1.5f }, + { Material.StoneBrickWall, 1.5f }, + { Material.StoneBricks, 1.5f }, + { Material.StoneButton, 0.5f }, + { Material.StonePressurePlate, 0.5f }, + { Material.StoneSlab, 2.0f }, + { Material.StoneStairs, 1.5f }, + { Material.Stonecutter, 3.5f }, + { Material.StrippedAcaciaLog, 2.0f }, + { Material.StrippedAcaciaWood, 2.0f }, + { Material.StrippedBambooBlock, 2.0f }, + { Material.StrippedBirchLog, 2.0f }, + { Material.StrippedBirchWood, 2.0f }, + { Material.StrippedCherryLog, 2.0f }, + { Material.StrippedCherryWood, 2.0f }, + { Material.StrippedCrimsonHyphae, 2.0f }, + { Material.StrippedCrimsonStem, 2.0f }, + { Material.StrippedDarkOakLog, 2.0f }, + { Material.StrippedDarkOakWood, 2.0f }, + { Material.StrippedJungleLog, 2.0f }, + { Material.StrippedJungleWood, 2.0f }, + { Material.StrippedMangroveLog, 2.0f }, + { Material.StrippedMangroveWood, 2.0f }, + { Material.StrippedOakLog, 2.0f }, + { Material.StrippedOakWood, 2.0f }, + { Material.StrippedPaleOakLog, 2.0f }, + { Material.StrippedPaleOakWood, 2.0f }, + { Material.StrippedSpruceLog, 2.0f }, + { Material.StrippedSpruceWood, 2.0f }, + { Material.StrippedWarpedHyphae, 2.0f }, + { Material.StrippedWarpedStem, 2.0f }, + { Material.StructureBlock, -1.0f }, + { Material.StructureVoid, 0.0f }, + { Material.SugarCane, 0.0f }, + { Material.Sunflower, 0.0f }, + { Material.SuspiciousGravel, 0.25f }, + { Material.SuspiciousSand, 0.25f }, + { Material.SweetBerryBush, 0.0f }, + { Material.TallDryGrass, 0.0f }, + { Material.TallGrass, 0.0f }, + { Material.TallSeagrass, 0.0f }, + { Material.Target, 0.5f }, + { Material.Terracotta, 1.25f }, + { Material.TestBlock, -1.0f }, + { Material.TestInstanceBlock, -1.0f }, + { Material.TintedGlass, 0.3f }, + { Material.Tnt, 0.0f }, + { Material.Torch, 0.0f }, + { Material.Torchflower, 0.0f }, + { Material.TorchflowerCrop, 0.0f }, + { Material.TrappedChest, 2.5f }, + { Material.TrialSpawner, 50.0f }, + { Material.Tripwire, 0.0f }, + { Material.TripwireHook, 0.0f }, + { Material.TubeCoral, 0.0f }, + { Material.TubeCoralBlock, 1.5f }, + { Material.TubeCoralFan, 0.0f }, + { Material.TubeCoralWallFan, 0.0f }, + { Material.Tuff, 1.5f }, + { Material.TuffBrickSlab, 1.5f }, + { Material.TuffBrickStairs, 1.5f }, + { Material.TuffBrickWall, 1.5f }, + { Material.TuffBricks, 1.5f }, + { Material.TuffSlab, 1.5f }, + { Material.TuffStairs, 1.5f }, + { Material.TuffWall, 1.5f }, + { Material.TurtleEgg, 0.5f }, + { Material.TwistingVines, 0.0f }, + { Material.TwistingVinesPlant, 0.0f }, + { Material.Vault, 50.0f }, + { Material.VerdantFroglight, 0.3f }, + { Material.Vine, 0.2f }, + { Material.VoidAir, 0.0f }, + { Material.WallTorch, 0.0f }, + { Material.WarpedButton, 0.5f }, + { Material.WarpedDoor, 3.0f }, + { Material.WarpedFence, 2.0f }, + { Material.WarpedFenceGate, 2.0f }, + { Material.WarpedFungus, 0.0f }, + { Material.WarpedHangingSign, 1.0f }, + { Material.WarpedHyphae, 2.0f }, + { Material.WarpedNylium, 0.4f }, + { Material.WarpedPlanks, 2.0f }, + { Material.WarpedPressurePlate, 0.5f }, + { Material.WarpedRoots, 0.0f }, + { Material.WarpedShelf, 2.0f }, + { Material.WarpedSign, 1.0f }, + { Material.WarpedSlab, 2.0f }, + { Material.WarpedStairs, 2.0f }, + { Material.WarpedStem, 2.0f }, + { Material.WarpedTrapdoor, 3.0f }, + { Material.WarpedWallHangingSign, 1.0f }, + { Material.WarpedWallSign, 1.0f }, + { Material.WarpedWartBlock, 1.0f }, + { Material.Water, 100.0f }, + { Material.WaterCauldron, 2.0f }, + { Material.WaxedChiseledCopper, 3.0f }, + { Material.WaxedCopperBlock, 3.0f }, + { Material.WaxedCopperBulb, 3.0f }, + { Material.WaxedCopperChest, 3.0f }, + { Material.WaxedCopperDoor, 3.0f }, + { Material.WaxedCopperGolemStatue, 3.0f }, + { Material.WaxedCopperGrate, 3.0f }, + { Material.WaxedCopperTrapdoor, 3.0f }, + { Material.WaxedCutCopper, 3.0f }, + { Material.WaxedCutCopperSlab, 3.0f }, + { Material.WaxedCutCopperStairs, 3.0f }, + { Material.WaxedExposedChiseledCopper, 3.0f }, + { Material.WaxedExposedCopper, 3.0f }, + { Material.WaxedExposedCopperBulb, 3.0f }, + { Material.WaxedExposedCopperChest, 3.0f }, + { Material.WaxedExposedCopperDoor, 3.0f }, + { Material.WaxedExposedCopperGolemStatue, 3.0f }, + { Material.WaxedExposedCopperGrate, 3.0f }, + { Material.WaxedExposedCopperTrapdoor, 3.0f }, + { Material.WaxedExposedCutCopper, 3.0f }, + { Material.WaxedExposedCutCopperSlab, 3.0f }, + { Material.WaxedExposedCutCopperStairs, 3.0f }, + { Material.WaxedExposedLightningRod, 3.0f }, + { Material.WaxedLightningRod, 3.0f }, + { Material.WaxedOxidizedChiseledCopper, 3.0f }, + { Material.WaxedOxidizedCopper, 3.0f }, + { Material.WaxedOxidizedCopperBulb, 3.0f }, + { Material.WaxedOxidizedCopperChest, 3.0f }, + { Material.WaxedOxidizedCopperDoor, 3.0f }, + { Material.WaxedOxidizedCopperGolemStatue, 3.0f }, + { Material.WaxedOxidizedCopperGrate, 3.0f }, + { Material.WaxedOxidizedCopperTrapdoor, 3.0f }, + { Material.WaxedOxidizedCutCopper, 3.0f }, + { Material.WaxedOxidizedCutCopperSlab, 3.0f }, + { Material.WaxedOxidizedCutCopperStairs, 3.0f }, + { Material.WaxedOxidizedLightningRod, 3.0f }, + { Material.WaxedWeatheredChiseledCopper, 3.0f }, + { Material.WaxedWeatheredCopper, 3.0f }, + { Material.WaxedWeatheredCopperBulb, 3.0f }, + { Material.WaxedWeatheredCopperChest, 3.0f }, + { Material.WaxedWeatheredCopperDoor, 3.0f }, + { Material.WaxedWeatheredCopperGolemStatue, 3.0f }, + { Material.WaxedWeatheredCopperGrate, 3.0f }, + { Material.WaxedWeatheredCopperTrapdoor, 3.0f }, + { Material.WaxedWeatheredCutCopper, 3.0f }, + { Material.WaxedWeatheredCutCopperSlab, 3.0f }, + { Material.WaxedWeatheredCutCopperStairs, 3.0f }, + { Material.WaxedWeatheredLightningRod, 3.0f }, + { Material.WeatheredChiseledCopper, 3.0f }, + { Material.WeatheredCopper, 3.0f }, + { Material.WeatheredCopperBulb, 3.0f }, + { Material.WeatheredCopperChest, 3.0f }, + { Material.WeatheredCopperDoor, 3.0f }, + { Material.WeatheredCopperGolemStatue, 3.0f }, + { Material.WeatheredCopperGrate, 3.0f }, + { Material.WeatheredCopperTrapdoor, 3.0f }, + { Material.WeatheredCutCopper, 3.0f }, + { Material.WeatheredCutCopperSlab, 3.0f }, + { Material.WeatheredCutCopperStairs, 3.0f }, + { Material.WeatheredLightningRod, 3.0f }, + { Material.WeepingVines, 0.0f }, + { Material.WeepingVinesPlant, 0.0f }, + { Material.WetSponge, 0.6f }, + { Material.Wheat, 0.0f }, + { Material.WhiteBanner, 1.0f }, + { Material.WhiteBed, 0.2f }, + { Material.WhiteCandle, 0.1f }, + { Material.WhiteCandleCake, 0.5f }, + { Material.WhiteCarpet, 0.1f }, + { Material.WhiteConcrete, 1.8f }, + { Material.WhiteConcretePowder, 0.5f }, + { Material.WhiteGlazedTerracotta, 1.4f }, + { Material.WhiteShulkerBox, 2.0f }, + { Material.WhiteStainedGlass, 0.3f }, + { Material.WhiteStainedGlassPane, 0.3f }, + { Material.WhiteTerracotta, 1.25f }, + { Material.WhiteTulip, 0.0f }, + { Material.WhiteWallBanner, 1.0f }, + { Material.WhiteWool, 0.8f }, + { Material.Wildflowers, 0.0f }, + { Material.WitherRose, 0.0f }, + { Material.WitherSkeletonSkull, 1.0f }, + { Material.WitherSkeletonWallSkull, 1.0f }, + { Material.YellowBanner, 1.0f }, + { Material.YellowBed, 0.2f }, + { Material.YellowCandle, 0.1f }, + { Material.YellowCandleCake, 0.5f }, + { Material.YellowCarpet, 0.1f }, + { Material.YellowConcrete, 1.8f }, + { Material.YellowConcretePowder, 0.5f }, + { Material.YellowGlazedTerracotta, 1.4f }, + { Material.YellowShulkerBox, 2.0f }, + { Material.YellowStainedGlass, 0.3f }, + { Material.YellowStainedGlassPane, 0.3f }, + { Material.YellowTerracotta, 1.25f }, + { Material.YellowWallBanner, 1.0f }, + { Material.YellowWool, 0.8f }, + { Material.ZombieHead, 1.0f }, + { Material.ZombieWallHead, 1.0f }, + }.ToFrozenDictionary(); + + private static readonly FrozenSet RequiresCorrectToolSet = new HashSet + { + Material.AmethystBlock, + Material.AncientDebris, + Material.Andesite, + Material.AndesiteSlab, + Material.AndesiteStairs, + Material.AndesiteWall, + Material.Anvil, + Material.Basalt, + Material.BlackConcrete, + Material.BlackGlazedTerracotta, + Material.BlackTerracotta, + Material.Blackstone, + Material.BlackstoneSlab, + Material.BlackstoneStairs, + Material.BlackstoneWall, + Material.BlastFurnace, + Material.BlueConcrete, + Material.BlueGlazedTerracotta, + Material.BlueTerracotta, + Material.BoneBlock, + Material.BrainCoralBlock, + Material.BrickSlab, + Material.BrickStairs, + Material.BrickWall, + Material.Bricks, + Material.BrownConcrete, + Material.BrownGlazedTerracotta, + Material.BrownTerracotta, + Material.BubbleCoralBlock, + Material.BuddingAmethyst, + Material.Calcite, + Material.Cauldron, + Material.ChainCommandBlock, + Material.ChippedAnvil, + Material.ChiseledCopper, + Material.ChiseledDeepslate, + Material.ChiseledNetherBricks, + Material.ChiseledPolishedBlackstone, + Material.ChiseledQuartzBlock, + Material.ChiseledRedSandstone, + Material.ChiseledResinBricks, + Material.ChiseledSandstone, + Material.ChiseledStoneBricks, + Material.ChiseledTuff, + Material.ChiseledTuffBricks, + Material.CoalBlock, + Material.CoalOre, + Material.CobbledDeepslate, + Material.CobbledDeepslateSlab, + Material.CobbledDeepslateStairs, + Material.CobbledDeepslateWall, + Material.Cobblestone, + Material.CobblestoneSlab, + Material.CobblestoneStairs, + Material.CobblestoneWall, + Material.Cobweb, + Material.CommandBlock, + Material.CopperBlock, + Material.CopperBulb, + Material.CopperChest, + Material.CopperGrate, + Material.CopperOre, + Material.CopperTrapdoor, + Material.CrackedDeepslateBricks, + Material.CrackedDeepslateTiles, + Material.CrackedNetherBricks, + Material.CrackedPolishedBlackstoneBricks, + Material.CrackedStoneBricks, + Material.CrimsonNylium, + Material.CryingObsidian, + Material.CutCopper, + Material.CutCopperSlab, + Material.CutCopperStairs, + Material.CutRedSandstone, + Material.CutRedSandstoneSlab, + Material.CutSandstone, + Material.CutSandstoneSlab, + Material.CyanConcrete, + Material.CyanGlazedTerracotta, + Material.CyanTerracotta, + Material.DamagedAnvil, + Material.DarkPrismarine, + Material.DarkPrismarineSlab, + Material.DarkPrismarineStairs, + Material.DeadBrainCoral, + Material.DeadBrainCoralBlock, + Material.DeadBrainCoralFan, + Material.DeadBrainCoralWallFan, + Material.DeadBubbleCoral, + Material.DeadBubbleCoralBlock, + Material.DeadBubbleCoralFan, + Material.DeadBubbleCoralWallFan, + Material.DeadFireCoral, + Material.DeadFireCoralBlock, + Material.DeadFireCoralFan, + Material.DeadFireCoralWallFan, + Material.DeadHornCoral, + Material.DeadHornCoralBlock, + Material.DeadHornCoralFan, + Material.DeadHornCoralWallFan, + Material.DeadTubeCoral, + Material.DeadTubeCoralBlock, + Material.DeadTubeCoralFan, + Material.DeadTubeCoralWallFan, + Material.Deepslate, + Material.DeepslateBrickSlab, + Material.DeepslateBrickStairs, + Material.DeepslateBrickWall, + Material.DeepslateBricks, + Material.DeepslateCoalOre, + Material.DeepslateCopperOre, + Material.DeepslateDiamondOre, + Material.DeepslateEmeraldOre, + Material.DeepslateGoldOre, + Material.DeepslateIronOre, + Material.DeepslateLapisOre, + Material.DeepslateRedstoneOre, + Material.DeepslateTileSlab, + Material.DeepslateTileStairs, + Material.DeepslateTileWall, + Material.DeepslateTiles, + Material.DiamondBlock, + Material.DiamondOre, + Material.Diorite, + Material.DioriteSlab, + Material.DioriteStairs, + Material.DioriteWall, + Material.Dispenser, + Material.DripstoneBlock, + Material.Dropper, + Material.EmeraldBlock, + Material.EmeraldOre, + Material.EnchantingTable, + Material.EndStone, + Material.EndStoneBrickSlab, + Material.EndStoneBrickStairs, + Material.EndStoneBrickWall, + Material.EndStoneBricks, + Material.ExposedChiseledCopper, + Material.ExposedCopper, + Material.ExposedCopperBulb, + Material.ExposedCopperChest, + Material.ExposedCopperGrate, + Material.ExposedCopperTrapdoor, + Material.ExposedCutCopper, + Material.ExposedCutCopperSlab, + Material.ExposedCutCopperStairs, + Material.ExposedLightningRod, + Material.FireCoralBlock, + Material.Furnace, + Material.GildedBlackstone, + Material.GoldBlock, + Material.GoldOre, + Material.Granite, + Material.GraniteSlab, + Material.GraniteStairs, + Material.GraniteWall, + Material.GrayConcrete, + Material.GrayGlazedTerracotta, + Material.GrayTerracotta, + Material.GreenConcrete, + Material.GreenGlazedTerracotta, + Material.GreenTerracotta, + Material.Grindstone, + Material.Hopper, + Material.HornCoralBlock, + Material.IronBars, + Material.IronBlock, + Material.IronChain, + Material.IronOre, + Material.IronTrapdoor, + Material.Jigsaw, + Material.LapisBlock, + Material.LapisOre, + Material.LavaCauldron, + Material.LightBlueConcrete, + Material.LightBlueGlazedTerracotta, + Material.LightBlueTerracotta, + Material.LightGrayConcrete, + Material.LightGrayGlazedTerracotta, + Material.LightGrayTerracotta, + Material.LightningRod, + Material.LimeConcrete, + Material.LimeGlazedTerracotta, + Material.LimeTerracotta, + Material.Lodestone, + Material.MagentaConcrete, + Material.MagentaGlazedTerracotta, + Material.MagentaTerracotta, + Material.MagmaBlock, + Material.MossyCobblestone, + Material.MossyCobblestoneSlab, + Material.MossyCobblestoneStairs, + Material.MossyCobblestoneWall, + Material.MossyStoneBrickSlab, + Material.MossyStoneBrickStairs, + Material.MossyStoneBrickWall, + Material.MossyStoneBricks, + Material.MudBrickSlab, + Material.MudBrickStairs, + Material.MudBrickWall, + Material.MudBricks, + Material.NetherBrickFence, + Material.NetherBrickSlab, + Material.NetherBrickStairs, + Material.NetherBrickWall, + Material.NetherBricks, + Material.NetherGoldOre, + Material.NetherQuartzOre, + Material.NetheriteBlock, + Material.Netherrack, + Material.Observer, + Material.Obsidian, + Material.OrangeConcrete, + Material.OrangeGlazedTerracotta, + Material.OrangeTerracotta, + Material.OxidizedChiseledCopper, + Material.OxidizedCopper, + Material.OxidizedCopperBulb, + Material.OxidizedCopperChest, + Material.OxidizedCopperGrate, + Material.OxidizedCopperTrapdoor, + Material.OxidizedCutCopper, + Material.OxidizedCutCopperSlab, + Material.OxidizedCutCopperStairs, + Material.OxidizedLightningRod, + Material.PetrifiedOakSlab, + Material.PinkConcrete, + Material.PinkGlazedTerracotta, + Material.PinkTerracotta, + Material.PolishedAndesite, + Material.PolishedAndesiteSlab, + Material.PolishedAndesiteStairs, + Material.PolishedBasalt, + Material.PolishedBlackstone, + Material.PolishedBlackstoneBrickSlab, + Material.PolishedBlackstoneBrickStairs, + Material.PolishedBlackstoneBrickWall, + Material.PolishedBlackstoneBricks, + Material.PolishedBlackstoneSlab, + Material.PolishedBlackstoneStairs, + Material.PolishedBlackstoneWall, + Material.PolishedDeepslate, + Material.PolishedDeepslateSlab, + Material.PolishedDeepslateStairs, + Material.PolishedDeepslateWall, + Material.PolishedDiorite, + Material.PolishedDioriteSlab, + Material.PolishedDioriteStairs, + Material.PolishedGranite, + Material.PolishedGraniteSlab, + Material.PolishedGraniteStairs, + Material.PolishedTuff, + Material.PolishedTuffSlab, + Material.PolishedTuffStairs, + Material.PolishedTuffWall, + Material.PowderSnowCauldron, + Material.Prismarine, + Material.PrismarineBrickSlab, + Material.PrismarineBrickStairs, + Material.PrismarineBricks, + Material.PrismarineSlab, + Material.PrismarineStairs, + Material.PrismarineWall, + Material.PurpleConcrete, + Material.PurpleGlazedTerracotta, + Material.PurpleTerracotta, + Material.PurpurBlock, + Material.PurpurPillar, + Material.PurpurSlab, + Material.PurpurStairs, + Material.QuartzBlock, + Material.QuartzBricks, + Material.QuartzPillar, + Material.QuartzSlab, + Material.QuartzStairs, + Material.RawCopperBlock, + Material.RawGoldBlock, + Material.RawIronBlock, + Material.RedConcrete, + Material.RedGlazedTerracotta, + Material.RedNetherBrickSlab, + Material.RedNetherBrickStairs, + Material.RedNetherBrickWall, + Material.RedNetherBricks, + Material.RedSandstone, + Material.RedSandstoneSlab, + Material.RedSandstoneStairs, + Material.RedSandstoneWall, + Material.RedTerracotta, + Material.RedstoneBlock, + Material.RedstoneOre, + Material.RepeatingCommandBlock, + Material.ResinBrickSlab, + Material.ResinBrickStairs, + Material.ResinBrickWall, + Material.ResinBricks, + Material.RespawnAnchor, + Material.Sandstone, + Material.SandstoneSlab, + Material.SandstoneStairs, + Material.SandstoneWall, + Material.Smoker, + Material.SmoothBasalt, + Material.SmoothQuartz, + Material.SmoothQuartzSlab, + Material.SmoothQuartzStairs, + Material.SmoothRedSandstone, + Material.SmoothRedSandstoneSlab, + Material.SmoothRedSandstoneStairs, + Material.SmoothSandstone, + Material.SmoothSandstoneSlab, + Material.SmoothSandstoneStairs, + Material.SmoothStone, + Material.SmoothStoneSlab, + Material.Snow, + Material.SnowBlock, + Material.Spawner, + Material.Stone, + Material.StoneBrickSlab, + Material.StoneBrickStairs, + Material.StoneBrickWall, + Material.StoneBricks, + Material.StoneSlab, + Material.StoneStairs, + Material.Stonecutter, + Material.StructureBlock, + Material.Terracotta, + Material.TubeCoralBlock, + Material.Tuff, + Material.TuffBrickSlab, + Material.TuffBrickStairs, + Material.TuffBrickWall, + Material.TuffBricks, + Material.TuffSlab, + Material.TuffStairs, + Material.TuffWall, + Material.WarpedNylium, + Material.WaterCauldron, + Material.WaxedChiseledCopper, + Material.WaxedCopperBlock, + Material.WaxedCopperBulb, + Material.WaxedCopperChest, + Material.WaxedCopperGrate, + Material.WaxedCopperTrapdoor, + Material.WaxedCutCopper, + Material.WaxedCutCopperSlab, + Material.WaxedCutCopperStairs, + Material.WaxedExposedChiseledCopper, + Material.WaxedExposedCopper, + Material.WaxedExposedCopperBulb, + Material.WaxedExposedCopperChest, + Material.WaxedExposedCopperGrate, + Material.WaxedExposedCopperTrapdoor, + Material.WaxedExposedCutCopper, + Material.WaxedExposedCutCopperSlab, + Material.WaxedExposedCutCopperStairs, + Material.WaxedExposedLightningRod, + Material.WaxedLightningRod, + Material.WaxedOxidizedChiseledCopper, + Material.WaxedOxidizedCopper, + Material.WaxedOxidizedCopperBulb, + Material.WaxedOxidizedCopperChest, + Material.WaxedOxidizedCopperGrate, + Material.WaxedOxidizedCopperTrapdoor, + Material.WaxedOxidizedCutCopper, + Material.WaxedOxidizedCutCopperSlab, + Material.WaxedOxidizedCutCopperStairs, + Material.WaxedOxidizedLightningRod, + Material.WaxedWeatheredChiseledCopper, + Material.WaxedWeatheredCopper, + Material.WaxedWeatheredCopperBulb, + Material.WaxedWeatheredCopperChest, + Material.WaxedWeatheredCopperGrate, + Material.WaxedWeatheredCopperTrapdoor, + Material.WaxedWeatheredCutCopper, + Material.WaxedWeatheredCutCopperSlab, + Material.WaxedWeatheredCutCopperStairs, + Material.WaxedWeatheredLightningRod, + Material.WeatheredChiseledCopper, + Material.WeatheredCopper, + Material.WeatheredCopperBulb, + Material.WeatheredCopperChest, + Material.WeatheredCopperGrate, + Material.WeatheredCopperTrapdoor, + Material.WeatheredCutCopper, + Material.WeatheredCutCopperSlab, + Material.WeatheredCutCopperStairs, + Material.WeatheredLightningRod, + Material.WhiteConcrete, + Material.WhiteGlazedTerracotta, + Material.WhiteTerracotta, + Material.YellowConcrete, + Material.YellowGlazedTerracotta, + Material.YellowTerracotta, + }.ToFrozenSet(); + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/BlockPalette120.cs b/MinecraftClient/Mapping/BlockPalettes/BlockPalette120.cs index 18691621..f6a36358 100644 --- a/MinecraftClient/Mapping/BlockPalettes/BlockPalette120.cs +++ b/MinecraftClient/Mapping/BlockPalettes/BlockPalette120.cs @@ -641,7 +641,7 @@ namespace MinecraftClient.Mapping.BlockPalettes materials[i] = Material.GraniteStairs; for (int i = 15315; i <= 15638; i++) materials[i] = Material.GraniteWall; - materials[2005] = Material.Grass; + materials[2005] = Material.ShortGrass; for (int i = 8; i <= 9; i++) materials[i] = Material.GrassBlock; materials[118] = Material.Gravel; diff --git a/MinecraftClient/Mapping/BlockPalettes/BlockPaletteGenerator.cs b/MinecraftClient/Mapping/BlockPalettes/BlockPaletteGenerator.cs index a1f7b307..11f7f5d2 100644 --- a/MinecraftClient/Mapping/BlockPalettes/BlockPaletteGenerator.cs +++ b/MinecraftClient/Mapping/BlockPalettes/BlockPaletteGenerator.cs @@ -51,8 +51,8 @@ namespace MinecraftClient.Mapping.BlockPalettes HashSet knownStates = new(); Dictionary> blocks = new(); - Json.JSONData palette = Json.ParseJson(File.ReadAllText(blocksJsonFile, Encoding.UTF8)); - foreach (KeyValuePair item in palette.Properties) + var palette = Json.ParseJson(File.ReadAllText(blocksJsonFile, Encoding.UTF8))!.AsObject(); + foreach (var item in palette) { //minecraft:item_name => ItemName string blockType = String.Concat( @@ -65,9 +65,9 @@ namespace MinecraftClient.Mapping.BlockPalettes throw new InvalidDataException("Duplicate block type " + blockType + "!?"); blocks[blockType] = new HashSet(); - foreach (Json.JSONData state in item.Value.Properties["states"].DataArray) + foreach (var state in item.Value!["states"]!.AsArray()) { - int id = int.Parse(state.Properties["id"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture); + int id = int.Parse(state!["id"].GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); if (knownStates.Contains(id)) throw new InvalidDataException("Duplicate state id " + id + "!?"); @@ -137,7 +137,7 @@ namespace MinecraftClient.Mapping.BlockPalettes File.WriteAllLines(outputPalettePath, outFile); - if (outputEnum != null) + if (outputEnum is not null) { outFile = new List(); outFile.AddRange(new[] { diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette112.cs b/MinecraftClient/Mapping/BlockPalettes/Palette112.cs index 023d1c05..0a9aa947 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette112.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette112.cs @@ -43,7 +43,7 @@ namespace MinecraftClient.Mapping.BlockPalettes { 28, Material.DetectorRail }, { 29, Material.StickyPiston }, // PistonStickyBase { 30, Material.Cobweb }, // Web - { 31, Material.Grass }, // LongGrass + { 31, Material.TallGrass }, // LongGrass { 32, Material.DeadBush }, { 33, Material.Piston }, // PistonBase { 34, Material.PistonHead }, // PistonExtension @@ -183,6 +183,7 @@ namespace MinecraftClient.Mapping.BlockPalettes { 173, Material.CoalBlock }, { 174, Material.PackedIce }, { 175, Material.TallGrass }, // DoublePlant + { 207, Material.Beetroots }, // BeetrootBlock }; protected override Dictionary GetDict() diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette113.cs b/MinecraftClient/Mapping/BlockPalettes/Palette113.cs index 847728bd..239df049 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette113.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette113.cs @@ -167,7 +167,7 @@ namespace MinecraftClient.Mapping.BlockPalettes for (int i = 1028; i <= 1039; i++) materials[i] = Material.StickyPiston; materials[1040] = Material.Cobweb; - materials[1041] = Material.Grass; + materials[1041] = Material.ShortGrass; materials[1042] = Material.Fern; materials[1043] = Material.DeadBush; materials[1044] = Material.Seagrass; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette114.cs b/MinecraftClient/Mapping/BlockPalettes/Palette114.cs index 97c12231..4e51ffea 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette114.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette114.cs @@ -167,7 +167,7 @@ namespace MinecraftClient.Mapping.BlockPalettes for (int i = 1328; i <= 1339; i++) materials[i] = Material.StickyPiston; materials[1340] = Material.Cobweb; - materials[1341] = Material.Grass; + materials[1341] = Material.ShortGrass; materials[1342] = Material.Fern; materials[1343] = Material.DeadBush; materials[1344] = Material.Seagrass; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette115.cs b/MinecraftClient/Mapping/BlockPalettes/Palette115.cs index aabe901b..449b95be 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette115.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette115.cs @@ -167,7 +167,7 @@ namespace MinecraftClient.Mapping.BlockPalettes for (int i = 1328; i <= 1339; i++) materials[i] = Material.StickyPiston; materials[1340] = Material.Cobweb; - materials[1341] = Material.Grass; + materials[1341] = Material.ShortGrass; materials[1342] = Material.Fern; materials[1343] = Material.DeadBush; materials[1344] = Material.Seagrass; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette116.cs b/MinecraftClient/Mapping/BlockPalettes/Palette116.cs index abc31d57..8927218f 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette116.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette116.cs @@ -164,7 +164,7 @@ namespace MinecraftClient.Mapping.BlockPalettes for (int i = 1329; i <= 1340; i++) materials[i] = Material.StickyPiston; materials[1341] = Material.Cobweb; - materials[1342] = Material.Grass; + materials[1342] = Material.ShortGrass; materials[1343] = Material.Fern; materials[1344] = Material.DeadBush; materials[1345] = Material.Seagrass; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette117.cs b/MinecraftClient/Mapping/BlockPalettes/Palette117.cs index 34cccd75..154c62cc 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette117.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette117.cs @@ -172,7 +172,7 @@ namespace MinecraftClient.Mapping.BlockPalettes for (int i = 1385; i <= 1396; i++) materials[i] = Material.StickyPiston; materials[1397] = Material.Cobweb; - materials[1398] = Material.Grass; + materials[1398] = Material.ShortGrass; materials[1399] = Material.Fern; materials[1400] = Material.DeadBush; materials[1401] = Material.Seagrass; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette119.cs b/MinecraftClient/Mapping/BlockPalettes/Palette119.cs index d8e9d760..548df06f 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette119.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette119.cs @@ -554,7 +554,7 @@ namespace MinecraftClient.Mapping.BlockPalettes materials[i] = Material.GraniteStairs; for (int i = 13044; i <= 13367; i++) materials[i] = Material.GraniteWall; - materials[1596] = Material.Grass; + materials[1596] = Material.ShortGrass; for (int i = 8; i <= 9; i++) materials[i] = Material.GrassBlock; materials[109] = Material.Gravel; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs index eb1e61fb..cb15a64f 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs @@ -604,7 +604,7 @@ namespace MinecraftClient.Mapping.BlockPalettes materials[i] = Material.GraniteStairs; for (int i = 14828; i <= 15151; i++) materials[i] = Material.GraniteWall; - materials[1954] = Material.Grass; + materials[1954] = Material.ShortGrass; for (int i = 8; i <= 9; i++) materials[i] = Material.GrassBlock; materials[111] = Material.Gravel; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1194.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1194.cs index 83c7ff29..c5a45ef4 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette1194.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1194.cs @@ -639,7 +639,7 @@ namespace MinecraftClient.Mapping.BlockPalettes materials[i] = Material.GraniteStairs; for (int i = 15297; i <= 15620; i++) materials[i] = Material.GraniteWall; - materials[2001] = Material.Grass; + materials[2001] = Material.ShortGrass; for (int i = 8; i <= 9; i++) materials[i] = Material.GrassBlock; materials[118] = Material.Gravel; diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1206.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1206.cs new file mode 100644 index 00000000..070d6dbb --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1206.cs @@ -0,0 +1,1766 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1206 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1206() + { + for (int i = 8707; i <= 8730; i++) + materials[i] = Material.AcaciaButton; + for (int i = 12014; i <= 12077; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 11662; i <= 11693; i++) + materials[i] = Material.AcaciaFence; + for (int i = 11406; i <= 11437; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 5026; i <= 5089; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 349; i <= 376; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 142; i <= 144; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5724; i <= 5725; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 33; i <= 34; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4398; i <= 4429; i++) + materials[i] = Material.AcaciaSign; + for (int i = 11186; i <= 11191; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 9884; i <= 9963; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6217; i <= 6280; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5562; i <= 5569; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4786; i <= 4793; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 201; i <= 203; i++) + materials[i] = Material.AcaciaWood; + for (int i = 9320; i <= 9343; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2079] = Material.Allium; + materials[21031] = Material.AmethystBlock; + for (int i = 21033; i <= 21044; i++) + materials[i] = Material.AmethystCluster; + materials[19448] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 14136; i <= 14141; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 13762; i <= 13841; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 16752; i <= 17075; i++) + materials[i] = Material.AndesiteWall; + for (int i = 9107; i <= 9110; i++) + materials[i] = Material.Anvil; + for (int i = 6817; i <= 6820; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 6813; i <= 6816; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[24824] = Material.Azalea; + for (int i = 461; i <= 488; i++) + materials[i] = Material.AzaleaLeaves; + materials[2080] = Material.AzureBluet; + for (int i = 12945; i <= 12956; i++) + materials[i] = Material.Bamboo; + for (int i = 159; i <= 161; i++) + materials[i] = Material.BambooBlock; + for (int i = 8803; i <= 8826; i++) + materials[i] = Material.BambooButton; + for (int i = 12270; i <= 12333; i++) + materials[i] = Material.BambooDoor; + for (int i = 11790; i <= 11821; i++) + materials[i] = Material.BambooFence; + for (int i = 11534; i <= 11565; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5474; i <= 5537; i++) + materials[i] = Material.BambooHangingSign; + materials[24] = Material.BambooMosaic; + for (int i = 11216; i <= 11221; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 10284; i <= 10363; i++) + materials[i] = Material.BambooMosaicStairs; + materials[23] = Material.BambooPlanks; + for (int i = 5732; i <= 5733; i++) + materials[i] = Material.BambooPressurePlate; + materials[12944] = Material.BambooSapling; + for (int i = 4558; i <= 4589; i++) + materials[i] = Material.BambooSign; + for (int i = 11210; i <= 11215; i++) + materials[i] = Material.BambooSlab; + for (int i = 10204; i <= 10283; i++) + materials[i] = Material.BambooStairs; + for (int i = 6473; i <= 6536; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5618; i <= 5625; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4826; i <= 4833; i++) + materials[i] = Material.BambooWallSign; + for (int i = 18408; i <= 18419; i++) + materials[i] = Material.Barrel; + for (int i = 10365; i <= 10366; i++) + materials[i] = Material.Barrier; + for (int i = 5852; i <= 5854; i++) + materials[i] = Material.Basalt; + materials[7918] = Material.Beacon; + materials[79] = Material.Bedrock; + for (int i = 19397; i <= 19420; i++) + materials[i] = Material.BeeNest; + for (int i = 19421; i <= 19444; i++) + materials[i] = Material.Beehive; + for (int i = 12509; i <= 12512; i++) + materials[i] = Material.Beetroots; + for (int i = 18471; i <= 18502; i++) + materials[i] = Material.Bell; + for (int i = 24844; i <= 24875; i++) + materials[i] = Material.BigDripleaf; + for (int i = 24876; i <= 24883; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 8659; i <= 8682; i++) + materials[i] = Material.BirchButton; + for (int i = 11886; i <= 11949; i++) + materials[i] = Material.BirchDoor; + for (int i = 11598; i <= 11629; i++) + materials[i] = Material.BirchFence; + for (int i = 11342; i <= 11373; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 4962; i <= 5025; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 293; i <= 320; i++) + materials[i] = Material.BirchLeaves; + for (int i = 136; i <= 138; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5720; i <= 5721; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 29; i <= 30; i++) + materials[i] = Material.BirchSapling; + for (int i = 4366; i <= 4397; i++) + materials[i] = Material.BirchSign; + for (int i = 11174; i <= 11179; i++) + materials[i] = Material.BirchSlab; + for (int i = 7746; i <= 7825; i++) + materials[i] = Material.BirchStairs; + for (int i = 6089; i <= 6152; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5554; i <= 5561; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4778; i <= 4785; i++) + materials[i] = Material.BirchWallSign; + for (int i = 195; i <= 197; i++) + materials[i] = Material.BirchWood; + for (int i = 10999; i <= 11014; i++) + materials[i] = Material.BlackBanner; + for (int i = 1928; i <= 1943; i++) + materials[i] = Material.BlackBed; + for (int i = 20981; i <= 20996; i++) + materials[i] = Material.BlackCandle; + for (int i = 21029; i <= 21030; i++) + materials[i] = Material.BlackCandleCake; + materials[10743] = Material.BlackCarpet; + materials[12743] = Material.BlackConcrete; + materials[12759] = Material.BlackConcretePowder; + for (int i = 12724; i <= 12727; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 12658; i <= 12663; i++) + materials[i] = Material.BlackShulkerBox; + materials[5960] = Material.BlackStainedGlass; + for (int i = 9852; i <= 9883; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[9371] = Material.BlackTerracotta; + for (int i = 11075; i <= 11078; i++) + materials[i] = Material.BlackWallBanner; + materials[2062] = Material.BlackWool; + materials[19460] = Material.Blackstone; + for (int i = 19865; i <= 19870; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 19461; i <= 19540; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 19541; i <= 19864; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 18428; i <= 18435; i++) + materials[i] = Material.BlastFurnace; + for (int i = 10935; i <= 10950; i++) + materials[i] = Material.BlueBanner; + for (int i = 1864; i <= 1879; i++) + materials[i] = Material.BlueBed; + for (int i = 20917; i <= 20932; i++) + materials[i] = Material.BlueCandle; + for (int i = 21021; i <= 21022; i++) + materials[i] = Material.BlueCandleCake; + materials[10739] = Material.BlueCarpet; + materials[12739] = Material.BlueConcrete; + materials[12755] = Material.BlueConcretePowder; + for (int i = 12708; i <= 12711; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[12941] = Material.BlueIce; + materials[2078] = Material.BlueOrchid; + for (int i = 12634; i <= 12639; i++) + materials[i] = Material.BlueShulkerBox; + materials[5956] = Material.BlueStainedGlass; + for (int i = 9724; i <= 9755; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[9367] = Material.BlueTerracotta; + for (int i = 11059; i <= 11062; i++) + materials[i] = Material.BlueWallBanner; + materials[2058] = Material.BlueWool; + for (int i = 12546; i <= 12548; i++) + materials[i] = Material.BoneBlock; + materials[2096] = Material.Bookshelf; + for (int i = 12825; i <= 12826; i++) + materials[i] = Material.BrainCoral; + materials[12809] = Material.BrainCoralBlock; + for (int i = 12845; i <= 12846; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 12901; i <= 12908; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 7390; i <= 7397; i++) + materials[i] = Material.BrewingStand; + for (int i = 11258; i <= 11263; i++) + materials[i] = Material.BrickSlab; + for (int i = 7029; i <= 7108; i++) + materials[i] = Material.BrickStairs; + for (int i = 14160; i <= 14483; i++) + materials[i] = Material.BrickWall; + materials[2093] = Material.Bricks; + for (int i = 10951; i <= 10966; i++) + materials[i] = Material.BrownBanner; + for (int i = 1880; i <= 1895; i++) + materials[i] = Material.BrownBed; + for (int i = 20933; i <= 20948; i++) + materials[i] = Material.BrownCandle; + for (int i = 21023; i <= 21024; i++) + materials[i] = Material.BrownCandleCake; + materials[10740] = Material.BrownCarpet; + materials[12740] = Material.BrownConcrete; + materials[12756] = Material.BrownConcretePowder; + for (int i = 12712; i <= 12715; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2089] = Material.BrownMushroom; + for (int i = 6549; i <= 6612; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 12640; i <= 12645; i++) + materials[i] = Material.BrownShulkerBox; + materials[5957] = Material.BrownStainedGlass; + for (int i = 9756; i <= 9787; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[9368] = Material.BrownTerracotta; + for (int i = 11063; i <= 11066; i++) + materials[i] = Material.BrownWallBanner; + materials[2059] = Material.BrownWool; + for (int i = 12960; i <= 12961; i++) + materials[i] = Material.BubbleColumn; + for (int i = 12827; i <= 12828; i++) + materials[i] = Material.BubbleCoral; + materials[12810] = Material.BubbleCoralBlock; + for (int i = 12847; i <= 12848; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 12909; i <= 12916; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[21032] = Material.BuddingAmethyst; + for (int i = 5782; i <= 5797; i++) + materials[i] = Material.Cactus; + for (int i = 5874; i <= 5880; i++) + materials[i] = Material.Cake; + materials[22316] = Material.Calcite; + for (int i = 22415; i <= 22798; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 18511; i <= 18542; i++) + materials[i] = Material.Campfire; + for (int i = 20725; i <= 20740; i++) + materials[i] = Material.Candle; + for (int i = 20997; i <= 20998; i++) + materials[i] = Material.CandleCake; + for (int i = 8595; i <= 8602; i++) + materials[i] = Material.Carrots; + materials[18436] = Material.CartographyTable; + for (int i = 5866; i <= 5869; i++) + materials[i] = Material.CarvedPumpkin; + materials[7398] = Material.Cauldron; + materials[12959] = Material.CaveAir; + for (int i = 24769; i <= 24820; i++) + materials[i] = Material.CaveVines; + for (int i = 24821; i <= 24822; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 6773; i <= 6778; i++) + materials[i] = Material.Chain; + for (int i = 12527; i <= 12538; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 8731; i <= 8754; i++) + materials[i] = Material.CherryButton; + for (int i = 12078; i <= 12141; i++) + materials[i] = Material.CherryDoor; + for (int i = 11694; i <= 11725; i++) + materials[i] = Material.CherryFence; + for (int i = 11438; i <= 11469; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 5090; i <= 5153; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 377; i <= 404; i++) + materials[i] = Material.CherryLeaves; + for (int i = 145; i <= 147; i++) + materials[i] = Material.CherryLog; + materials[20] = Material.CherryPlanks; + for (int i = 5726; i <= 5727; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 35; i <= 36; i++) + materials[i] = Material.CherrySapling; + for (int i = 4430; i <= 4461; i++) + materials[i] = Material.CherrySign; + for (int i = 11192; i <= 11197; i++) + materials[i] = Material.CherrySlab; + for (int i = 9964; i <= 10043; i++) + materials[i] = Material.CherryStairs; + for (int i = 6281; i <= 6344; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 5570; i <= 5577; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 4794; i <= 4801; i++) + materials[i] = Material.CherryWallSign; + for (int i = 204; i <= 206; i++) + materials[i] = Material.CherryWood; + for (int i = 2954; i <= 2977; i++) + materials[i] = Material.Chest; + for (int i = 9111; i <= 9114; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2097; i <= 2352; i++) + materials[i] = Material.ChiseledBookshelf; + materials[22951] = Material.ChiseledCopper; + materials[26551] = Material.ChiseledDeepslate; + materials[20722] = Material.ChiseledNetherBricks; + materials[19874] = Material.ChiseledPolishedBlackstone; + materials[9236] = Material.ChiseledQuartzBlock; + materials[11080] = Material.ChiseledRedSandstone; + materials[536] = Material.ChiseledSandstone; + materials[6540] = Material.ChiseledStoneBricks; + materials[21903] = Material.ChiseledTuff; + materials[22315] = Material.ChiseledTuffBricks; + for (int i = 12404; i <= 12409; i++) + materials[i] = Material.ChorusFlower; + for (int i = 12340; i <= 12403; i++) + materials[i] = Material.ChorusPlant; + materials[5798] = Material.Clay; + materials[10745] = Material.CoalBlock; + materials[127] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[24907] = Material.CobbledDeepslate; + for (int i = 24988; i <= 24993; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 24908; i <= 24987; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 24994; i <= 25317; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 11252; i <= 11257; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4682; i <= 4761; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 7919; i <= 8242; i++) + materials[i] = Material.CobblestoneWall; + materials[2004] = Material.Cobweb; + for (int i = 7419; i <= 7430; i++) + materials[i] = Material.Cocoa; + for (int i = 7906; i <= 7917; i++) + materials[i] = Material.CommandBlock; + for (int i = 9175; i <= 9190; i++) + materials[i] = Material.Comparator; + for (int i = 19372; i <= 19380; i++) + materials[i] = Material.Composter; + for (int i = 12942; i <= 12943; i++) + materials[i] = Material.Conduit; + materials[22938] = Material.CopperBlock; + for (int i = 24692; i <= 24695; i++) + materials[i] = Material.CopperBulb; + for (int i = 23652; i <= 23715; i++) + materials[i] = Material.CopperDoor; + for (int i = 24676; i <= 24677; i++) + materials[i] = Material.CopperGrate; + materials[22942] = Material.CopperOre; + for (int i = 24164; i <= 24227; i++) + materials[i] = Material.CopperTrapdoor; + materials[2086] = Material.Cornflower; + materials[26552] = Material.CrackedDeepslateBricks; + materials[26553] = Material.CrackedDeepslateTiles; + materials[20723] = Material.CrackedNetherBricks; + materials[19873] = Material.CrackedPolishedBlackstoneBricks; + materials[6539] = Material.CrackedStoneBricks; + for (int i = 26590; i <= 26637; i++) + materials[i] = Material.Crafter; + materials[4277] = Material.CraftingTable; + for (int i = 8987; i <= 9018; i++) + materials[i] = Material.CreeperHead; + for (int i = 9019; i <= 9026; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 19100; i <= 19123; i++) + materials[i] = Material.CrimsonButton; + for (int i = 19148; i <= 19211; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 18684; i <= 18715; i++) + materials[i] = Material.CrimsonFence; + for (int i = 18876; i <= 18907; i++) + materials[i] = Material.CrimsonFenceGate; + materials[18609] = Material.CrimsonFungus; + for (int i = 5282; i <= 5345; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 18602; i <= 18604; i++) + materials[i] = Material.CrimsonHyphae; + materials[18608] = Material.CrimsonNylium; + materials[18666] = Material.CrimsonPlanks; + for (int i = 18680; i <= 18681; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[18665] = Material.CrimsonRoots; + for (int i = 19276; i <= 19307; i++) + materials[i] = Material.CrimsonSign; + for (int i = 18668; i <= 18673; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 18940; i <= 19019; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 18596; i <= 18598; i++) + materials[i] = Material.CrimsonStem; + for (int i = 18748; i <= 18811; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5602; i <= 5609; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 19340; i <= 19347; i++) + materials[i] = Material.CrimsonWallSign; + materials[19449] = Material.CryingObsidian; + materials[22947] = Material.CutCopper; + for (int i = 23294; i <= 23299; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 23196; i <= 23275; i++) + materials[i] = Material.CutCopperStairs; + materials[11081] = Material.CutRedSandstone; + for (int i = 11294; i <= 11299; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[537] = Material.CutSandstone; + for (int i = 11240; i <= 11245; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 10903; i <= 10918; i++) + materials[i] = Material.CyanBanner; + for (int i = 1832; i <= 1847; i++) + materials[i] = Material.CyanBed; + for (int i = 20885; i <= 20900; i++) + materials[i] = Material.CyanCandle; + for (int i = 21017; i <= 21018; i++) + materials[i] = Material.CyanCandleCake; + materials[10737] = Material.CyanCarpet; + materials[12737] = Material.CyanConcrete; + materials[12753] = Material.CyanConcretePowder; + for (int i = 12700; i <= 12703; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 12622; i <= 12627; i++) + materials[i] = Material.CyanShulkerBox; + materials[5954] = Material.CyanStainedGlass; + for (int i = 9660; i <= 9691; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[9365] = Material.CyanTerracotta; + for (int i = 11051; i <= 11054; i++) + materials[i] = Material.CyanWallBanner; + materials[2056] = Material.CyanWool; + for (int i = 9115; i <= 9118; i++) + materials[i] = Material.DamagedAnvil; + materials[2075] = Material.Dandelion; + for (int i = 8755; i <= 8778; i++) + materials[i] = Material.DarkOakButton; + for (int i = 12142; i <= 12205; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 11726; i <= 11757; i++) + materials[i] = Material.DarkOakFence; + for (int i = 11470; i <= 11501; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5218; i <= 5281; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 405; i <= 432; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 148; i <= 150; i++) + materials[i] = Material.DarkOakLog; + materials[21] = Material.DarkOakPlanks; + for (int i = 5728; i <= 5729; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 37; i <= 38; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4494; i <= 4525; i++) + materials[i] = Material.DarkOakSign; + for (int i = 11198; i <= 11203; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 10044; i <= 10123; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6345; i <= 6408; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5586; i <= 5593; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4810; i <= 4817; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 207; i <= 209; i++) + materials[i] = Material.DarkOakWood; + materials[10465] = Material.DarkPrismarine; + for (int i = 10718; i <= 10723; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 10626; i <= 10705; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 9191; i <= 9222; i++) + materials[i] = Material.DaylightDetector; + for (int i = 12815; i <= 12816; i++) + materials[i] = Material.DeadBrainCoral; + materials[12804] = Material.DeadBrainCoralBlock; + for (int i = 12835; i <= 12836; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 12861; i <= 12868; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 12817; i <= 12818; i++) + materials[i] = Material.DeadBubbleCoral; + materials[12805] = Material.DeadBubbleCoralBlock; + for (int i = 12837; i <= 12838; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 12869; i <= 12876; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[2007] = Material.DeadBush; + for (int i = 12819; i <= 12820; i++) + materials[i] = Material.DeadFireCoral; + materials[12806] = Material.DeadFireCoralBlock; + for (int i = 12839; i <= 12840; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 12877; i <= 12884; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 12821; i <= 12822; i++) + materials[i] = Material.DeadHornCoral; + materials[12807] = Material.DeadHornCoralBlock; + for (int i = 12841; i <= 12842; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 12885; i <= 12892; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 12813; i <= 12814; i++) + materials[i] = Material.DeadTubeCoral; + materials[12803] = Material.DeadTubeCoralBlock; + for (int i = 12833; i <= 12834; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 12853; i <= 12860; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 26574; i <= 26589; i++) + materials[i] = Material.DecoratedPot; + for (int i = 24904; i <= 24906; i++) + materials[i] = Material.Deepslate; + for (int i = 26221; i <= 26226; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 26141; i <= 26220; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 26227; i <= 26550; i++) + materials[i] = Material.DeepslateBrickWall; + materials[26140] = Material.DeepslateBricks; + materials[128] = Material.DeepslateCoalOre; + materials[22943] = Material.DeepslateCopperOre; + materials[4275] = Material.DeepslateDiamondOre; + materials[7512] = Material.DeepslateEmeraldOre; + materials[124] = Material.DeepslateGoldOre; + materials[126] = Material.DeepslateIronOre; + materials[521] = Material.DeepslateLapisOre; + for (int i = 5736; i <= 5737; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 25810; i <= 25815; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 25730; i <= 25809; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 25816; i <= 26139; i++) + materials[i] = Material.DeepslateTileWall; + materials[25729] = Material.DeepslateTiles; + for (int i = 1968; i <= 1991; i++) + materials[i] = Material.DetectorRail; + materials[4276] = Material.DiamondBlock; + materials[4274] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 14154; i <= 14159; i++) + materials[i] = Material.DioriteSlab; + for (int i = 14002; i <= 14081; i++) + materials[i] = Material.DioriteStairs; + for (int i = 18048; i <= 18371; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[12513] = Material.DirtPath; + for (int i = 523; i <= 534; i++) + materials[i] = Material.Dispenser; + materials[7416] = Material.DragonEgg; + for (int i = 9027; i <= 9058; i++) + materials[i] = Material.DragonHead; + for (int i = 9059; i <= 9066; i++) + materials[i] = Material.DragonWallHead; + materials[12787] = Material.DriedKelpBlock; + materials[24768] = Material.DripstoneBlock; + for (int i = 9344; i <= 9355; i++) + materials[i] = Material.Dropper; + materials[7665] = Material.EmeraldBlock; + materials[7511] = Material.EmeraldOre; + materials[7389] = Material.EnchantingTable; + materials[12514] = Material.EndGateway; + materials[7406] = Material.EndPortal; + for (int i = 7407; i <= 7414; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 12334; i <= 12339; i++) + materials[i] = Material.EndRod; + materials[7415] = Material.EndStone; + for (int i = 14112; i <= 14117; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 13362; i <= 13441; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 17724; i <= 18047; i++) + materials[i] = Material.EndStoneBrickWall; + materials[12494] = Material.EndStoneBricks; + for (int i = 7513; i <= 7520; i++) + materials[i] = Material.EnderChest; + materials[22950] = Material.ExposedChiseledCopper; + materials[22939] = Material.ExposedCopper; + for (int i = 24696; i <= 24699; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 23716; i <= 23779; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 24678; i <= 24679; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 24228; i <= 24291; i++) + materials[i] = Material.ExposedCopperTrapdoor; + materials[22946] = Material.ExposedCutCopper; + for (int i = 23288; i <= 23293; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 23116; i <= 23195; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4286; i <= 4293; i++) + materials[i] = Material.Farmland; + materials[2006] = Material.Fern; + for (int i = 2360; i <= 2871; i++) + materials[i] = Material.Fire; + for (int i = 12829; i <= 12830; i++) + materials[i] = Material.FireCoral; + materials[12811] = Material.FireCoralBlock; + for (int i = 12849; i <= 12850; i++) + materials[i] = Material.FireCoralFan; + for (int i = 12917; i <= 12924; i++) + materials[i] = Material.FireCoralWallFan; + materials[18437] = Material.FletchingTable; + materials[8567] = Material.FlowerPot; + materials[24825] = Material.FloweringAzalea; + for (int i = 489; i <= 516; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[26572] = Material.Frogspawn; + for (int i = 12539; i <= 12542; i++) + materials[i] = Material.FrostedIce; + for (int i = 4294; i <= 4301; i++) + materials[i] = Material.Furnace; + materials[20285] = Material.GildedBlackstone; + materials[519] = Material.Glass; + for (int i = 6779; i <= 6810; i++) + materials[i] = Material.GlassPane; + for (int i = 6869; i <= 6996; i++) + materials[i] = Material.GlowLichen; + materials[5863] = Material.Glowstone; + materials[2091] = Material.GoldBlock; + materials[123] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 14130; i <= 14135; i++) + materials[i] = Material.GraniteSlab; + for (int i = 13682; i <= 13761; i++) + materials[i] = Material.GraniteStairs; + for (int i = 15456; i <= 15779; i++) + materials[i] = Material.GraniteWall; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[118] = Material.Gravel; + for (int i = 10871; i <= 10886; i++) + materials[i] = Material.GrayBanner; + for (int i = 1800; i <= 1815; i++) + materials[i] = Material.GrayBed; + for (int i = 20853; i <= 20868; i++) + materials[i] = Material.GrayCandle; + for (int i = 21013; i <= 21014; i++) + materials[i] = Material.GrayCandleCake; + materials[10735] = Material.GrayCarpet; + materials[12735] = Material.GrayConcrete; + materials[12751] = Material.GrayConcretePowder; + for (int i = 12692; i <= 12695; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 12610; i <= 12615; i++) + materials[i] = Material.GrayShulkerBox; + materials[5952] = Material.GrayStainedGlass; + for (int i = 9596; i <= 9627; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[9363] = Material.GrayTerracotta; + for (int i = 11043; i <= 11046; i++) + materials[i] = Material.GrayWallBanner; + materials[2054] = Material.GrayWool; + for (int i = 10967; i <= 10982; i++) + materials[i] = Material.GreenBanner; + for (int i = 1896; i <= 1911; i++) + materials[i] = Material.GreenBed; + for (int i = 20949; i <= 20964; i++) + materials[i] = Material.GreenCandle; + for (int i = 21025; i <= 21026; i++) + materials[i] = Material.GreenCandleCake; + materials[10741] = Material.GreenCarpet; + materials[12741] = Material.GreenConcrete; + materials[12757] = Material.GreenConcretePowder; + for (int i = 12716; i <= 12719; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 12646; i <= 12651; i++) + materials[i] = Material.GreenShulkerBox; + materials[5958] = Material.GreenStainedGlass; + for (int i = 9788; i <= 9819; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[9369] = Material.GreenTerracotta; + for (int i = 11067; i <= 11070; i++) + materials[i] = Material.GreenWallBanner; + materials[2060] = Material.GreenWool; + for (int i = 18438; i <= 18449; i++) + materials[i] = Material.Grindstone; + for (int i = 24900; i <= 24901; i++) + materials[i] = Material.HangingRoots; + for (int i = 10725; i <= 10727; i++) + materials[i] = Material.HayBlock; + for (int i = 26682; i <= 26683; i++) + materials[i] = Material.HeavyCore; + for (int i = 9159; i <= 9174; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[19445] = Material.HoneyBlock; + materials[19446] = Material.HoneycombBlock; + for (int i = 9225; i <= 9234; i++) + materials[i] = Material.Hopper; + for (int i = 12831; i <= 12832; i++) + materials[i] = Material.HornCoral; + materials[12812] = Material.HornCoralBlock; + for (int i = 12851; i <= 12852; i++) + materials[i] = Material.HornCoralFan; + for (int i = 12925; i <= 12932; i++) + materials[i] = Material.HornCoralWallFan; + materials[5780] = Material.Ice; + materials[6548] = Material.InfestedChiseledStoneBricks; + materials[6544] = Material.InfestedCobblestone; + materials[6547] = Material.InfestedCrackedStoneBricks; + for (int i = 26554; i <= 26556; i++) + materials[i] = Material.InfestedDeepslate; + materials[6546] = Material.InfestedMossyStoneBricks; + materials[6543] = Material.InfestedStone; + materials[6545] = Material.InfestedStoneBricks; + for (int i = 6741; i <= 6772; i++) + materials[i] = Material.IronBars; + materials[2092] = Material.IronBlock; + for (int i = 5652; i <= 5715; i++) + materials[i] = Material.IronDoor; + materials[125] = Material.IronOre; + for (int i = 10399; i <= 10462; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 5870; i <= 5873; i++) + materials[i] = Material.JackOLantern; + for (int i = 19360; i <= 19371; i++) + materials[i] = Material.Jigsaw; + for (int i = 5815; i <= 5816; i++) + materials[i] = Material.Jukebox; + for (int i = 8683; i <= 8706; i++) + materials[i] = Material.JungleButton; + for (int i = 11950; i <= 12013; i++) + materials[i] = Material.JungleDoor; + for (int i = 11630; i <= 11661; i++) + materials[i] = Material.JungleFence; + for (int i = 11374; i <= 11405; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 5154; i <= 5217; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 321; i <= 348; i++) + materials[i] = Material.JungleLeaves; + for (int i = 139; i <= 141; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5722; i <= 5723; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 31; i <= 32; i++) + materials[i] = Material.JungleSapling; + for (int i = 4462; i <= 4493; i++) + materials[i] = Material.JungleSign; + for (int i = 11180; i <= 11185; i++) + materials[i] = Material.JungleSlab; + for (int i = 7826; i <= 7905; i++) + materials[i] = Material.JungleStairs; + for (int i = 6153; i <= 6216; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5578; i <= 5585; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4802; i <= 4809; i++) + materials[i] = Material.JungleWallSign; + for (int i = 198; i <= 200; i++) + materials[i] = Material.JungleWood; + for (int i = 12760; i <= 12785; i++) + materials[i] = Material.Kelp; + materials[12786] = Material.KelpPlant; + for (int i = 4654; i <= 4661; i++) + materials[i] = Material.Ladder; + for (int i = 18503; i <= 18506; i++) + materials[i] = Material.Lantern; + materials[522] = Material.LapisBlock; + materials[520] = Material.LapisOre; + for (int i = 21045; i <= 21056; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 10757; i <= 10758; i++) + materials[i] = Material.LargeFern; + for (int i = 96; i <= 111; i++) + materials[i] = Material.Lava; + materials[7402] = Material.LavaCauldron; + for (int i = 18450; i <= 18465; i++) + materials[i] = Material.Lectern; + for (int i = 5626; i <= 5649; i++) + materials[i] = Material.Lever; + for (int i = 10367; i <= 10398; i++) + materials[i] = Material.Light; + for (int i = 10807; i <= 10822; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1736; i <= 1751; i++) + materials[i] = Material.LightBlueBed; + for (int i = 20789; i <= 20804; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 21005; i <= 21006; i++) + materials[i] = Material.LightBlueCandleCake; + materials[10731] = Material.LightBlueCarpet; + materials[12731] = Material.LightBlueConcrete; + materials[12747] = Material.LightBlueConcretePowder; + for (int i = 12676; i <= 12679; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 12586; i <= 12591; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[5948] = Material.LightBlueStainedGlass; + for (int i = 9468; i <= 9499; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[9359] = Material.LightBlueTerracotta; + for (int i = 11027; i <= 11030; i++) + materials[i] = Material.LightBlueWallBanner; + materials[2050] = Material.LightBlueWool; + for (int i = 10887; i <= 10902; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1816; i <= 1831; i++) + materials[i] = Material.LightGrayBed; + for (int i = 20869; i <= 20884; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 21015; i <= 21016; i++) + materials[i] = Material.LightGrayCandleCake; + materials[10736] = Material.LightGrayCarpet; + materials[12736] = Material.LightGrayConcrete; + materials[12752] = Material.LightGrayConcretePowder; + for (int i = 12696; i <= 12699; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 12616; i <= 12621; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[5953] = Material.LightGrayStainedGlass; + for (int i = 9628; i <= 9659; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[9364] = Material.LightGrayTerracotta; + for (int i = 11047; i <= 11050; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2055] = Material.LightGrayWool; + for (int i = 9143; i <= 9158; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 24724; i <= 24747; i++) + materials[i] = Material.LightningRod; + for (int i = 10749; i <= 10750; i++) + materials[i] = Material.Lilac; + materials[2088] = Material.LilyOfTheValley; + materials[7271] = Material.LilyPad; + for (int i = 10839; i <= 10854; i++) + materials[i] = Material.LimeBanner; + for (int i = 1768; i <= 1783; i++) + materials[i] = Material.LimeBed; + for (int i = 20821; i <= 20836; i++) + materials[i] = Material.LimeCandle; + for (int i = 21009; i <= 21010; i++) + materials[i] = Material.LimeCandleCake; + materials[10733] = Material.LimeCarpet; + materials[12733] = Material.LimeConcrete; + materials[12749] = Material.LimeConcretePowder; + for (int i = 12684; i <= 12687; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 12598; i <= 12603; i++) + materials[i] = Material.LimeShulkerBox; + materials[5950] = Material.LimeStainedGlass; + for (int i = 9532; i <= 9563; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[9361] = Material.LimeTerracotta; + for (int i = 11035; i <= 11038; i++) + materials[i] = Material.LimeWallBanner; + materials[2052] = Material.LimeWool; + materials[19459] = Material.Lodestone; + for (int i = 18404; i <= 18407; i++) + materials[i] = Material.Loom; + for (int i = 10791; i <= 10806; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1720; i <= 1735; i++) + materials[i] = Material.MagentaBed; + for (int i = 20773; i <= 20788; i++) + materials[i] = Material.MagentaCandle; + for (int i = 21003; i <= 21004; i++) + materials[i] = Material.MagentaCandleCake; + materials[10730] = Material.MagentaCarpet; + materials[12730] = Material.MagentaConcrete; + materials[12746] = Material.MagentaConcretePowder; + for (int i = 12672; i <= 12675; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 12580; i <= 12585; i++) + materials[i] = Material.MagentaShulkerBox; + materials[5947] = Material.MagentaStainedGlass; + for (int i = 9436; i <= 9467; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[9358] = Material.MagentaTerracotta; + for (int i = 11023; i <= 11026; i++) + materials[i] = Material.MagentaWallBanner; + materials[2049] = Material.MagentaWool; + materials[12543] = Material.MagmaBlock; + for (int i = 8779; i <= 8802; i++) + materials[i] = Material.MangroveButton; + for (int i = 12206; i <= 12269; i++) + materials[i] = Material.MangroveDoor; + for (int i = 11758; i <= 11789; i++) + materials[i] = Material.MangroveFence; + for (int i = 11502; i <= 11533; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5410; i <= 5473; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 433; i <= 460; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 151; i <= 153; i++) + materials[i] = Material.MangroveLog; + materials[22] = Material.MangrovePlanks; + for (int i = 5730; i <= 5731; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 39; i <= 78; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 154; i <= 155; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4526; i <= 4557; i++) + materials[i] = Material.MangroveSign; + for (int i = 11204; i <= 11209; i++) + materials[i] = Material.MangroveSlab; + for (int i = 10124; i <= 10203; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6409; i <= 6472; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5594; i <= 5601; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4818; i <= 4825; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 210; i <= 212; i++) + materials[i] = Material.MangroveWood; + for (int i = 21057; i <= 21068; i++) + materials[i] = Material.MediumAmethystBud; + materials[6812] = Material.Melon; + for (int i = 6829; i <= 6836; i++) + materials[i] = Material.MelonStem; + materials[24843] = Material.MossBlock; + materials[24826] = Material.MossCarpet; + materials[2353] = Material.MossyCobblestone; + for (int i = 14106; i <= 14111; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 13282; i <= 13361; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 8243; i <= 8566; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 14094; i <= 14099; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 13122; i <= 13201; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 15132; i <= 15455; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6538] = Material.MossyStoneBricks; + for (int i = 2063; i <= 2074; i++) + materials[i] = Material.MovingPiston; + materials[24903] = Material.Mud; + for (int i = 11270; i <= 11275; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 7189; i <= 7268; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 16104; i <= 16427; i++) + materials[i] = Material.MudBrickWall; + materials[6542] = Material.MudBricks; + for (int i = 156; i <= 158; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6677; i <= 6740; i++) + materials[i] = Material.MushroomStem; + for (int i = 7269; i <= 7270; i++) + materials[i] = Material.Mycelium; + for (int i = 7273; i <= 7304; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 11276; i <= 11281; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 7305; i <= 7384; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 16428; i <= 16751; i++) + materials[i] = Material.NetherBrickWall; + materials[7272] = Material.NetherBricks; + materials[129] = Material.NetherGoldOre; + for (int i = 5864; i <= 5865; i++) + materials[i] = Material.NetherPortal; + materials[9224] = Material.NetherQuartzOre; + materials[18595] = Material.NetherSprouts; + for (int i = 7385; i <= 7388; i++) + materials[i] = Material.NetherWart; + materials[12544] = Material.NetherWartBlock; + materials[19447] = Material.NetheriteBlock; + materials[5849] = Material.Netherrack; + for (int i = 538; i <= 1687; i++) + materials[i] = Material.NoteBlock; + for (int i = 8611; i <= 8634; i++) + materials[i] = Material.OakButton; + for (int i = 4590; i <= 4653; i++) + materials[i] = Material.OakDoor; + for (int i = 5817; i <= 5848; i++) + materials[i] = Material.OakFence; + for (int i = 6997; i <= 7028; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4834; i <= 4897; i++) + materials[i] = Material.OakHangingSign; + for (int i = 237; i <= 264; i++) + materials[i] = Material.OakLeaves; + for (int i = 130; i <= 132; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5716; i <= 5717; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 25; i <= 26; i++) + materials[i] = Material.OakSapling; + for (int i = 4302; i <= 4333; i++) + materials[i] = Material.OakSign; + for (int i = 11162; i <= 11167; i++) + materials[i] = Material.OakSlab; + for (int i = 2874; i <= 2953; i++) + materials[i] = Material.OakStairs; + for (int i = 5961; i <= 6024; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5538; i <= 5545; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4762; i <= 4769; i++) + materials[i] = Material.OakWallSign; + for (int i = 189; i <= 191; i++) + materials[i] = Material.OakWood; + for (int i = 12550; i <= 12561; i++) + materials[i] = Material.Observer; + materials[2354] = Material.Obsidian; + for (int i = 26563; i <= 26565; i++) + materials[i] = Material.OchreFroglight; + for (int i = 10775; i <= 10790; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1704; i <= 1719; i++) + materials[i] = Material.OrangeBed; + for (int i = 20757; i <= 20772; i++) + materials[i] = Material.OrangeCandle; + for (int i = 21001; i <= 21002; i++) + materials[i] = Material.OrangeCandleCake; + materials[10729] = Material.OrangeCarpet; + materials[12729] = Material.OrangeConcrete; + materials[12745] = Material.OrangeConcretePowder; + for (int i = 12668; i <= 12671; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 12574; i <= 12579; i++) + materials[i] = Material.OrangeShulkerBox; + materials[5946] = Material.OrangeStainedGlass; + for (int i = 9404; i <= 9435; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[9357] = Material.OrangeTerracotta; + materials[2082] = Material.OrangeTulip; + for (int i = 11019; i <= 11022; i++) + materials[i] = Material.OrangeWallBanner; + materials[2048] = Material.OrangeWool; + materials[2085] = Material.OxeyeDaisy; + materials[22948] = Material.OxidizedChiseledCopper; + materials[22941] = Material.OxidizedCopper; + for (int i = 24704; i <= 24707; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 23780; i <= 23843; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 24682; i <= 24683; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 24292; i <= 24355; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + materials[22944] = Material.OxidizedCutCopper; + for (int i = 23276; i <= 23281; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 22956; i <= 23035; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[10746] = Material.PackedIce; + materials[6541] = Material.PackedMud; + for (int i = 26569; i <= 26571; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 10753; i <= 10754; i++) + materials[i] = Material.Peony; + for (int i = 11246; i <= 11251; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 9067; i <= 9098; i++) + materials[i] = Material.PiglinHead; + for (int i = 9099; i <= 9106; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 10855; i <= 10870; i++) + materials[i] = Material.PinkBanner; + for (int i = 1784; i <= 1799; i++) + materials[i] = Material.PinkBed; + for (int i = 20837; i <= 20852; i++) + materials[i] = Material.PinkCandle; + for (int i = 21011; i <= 21012; i++) + materials[i] = Material.PinkCandleCake; + materials[10734] = Material.PinkCarpet; + materials[12734] = Material.PinkConcrete; + materials[12750] = Material.PinkConcretePowder; + for (int i = 12688; i <= 12691; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 24827; i <= 24842; i++) + materials[i] = Material.PinkPetals; + for (int i = 12604; i <= 12609; i++) + materials[i] = Material.PinkShulkerBox; + materials[5951] = Material.PinkStainedGlass; + for (int i = 9564; i <= 9595; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[9362] = Material.PinkTerracotta; + materials[2084] = Material.PinkTulip; + for (int i = 11039; i <= 11042; i++) + materials[i] = Material.PinkWallBanner; + materials[2053] = Material.PinkWool; + for (int i = 2011; i <= 2022; i++) + materials[i] = Material.Piston; + for (int i = 2023; i <= 2046; i++) + materials[i] = Material.PistonHead; + for (int i = 12497; i <= 12506; i++) + materials[i] = Material.PitcherCrop; + for (int i = 12507; i <= 12508; i++) + materials[i] = Material.PitcherPlant; + for (int i = 8947; i <= 8978; i++) + materials[i] = Material.PlayerHead; + for (int i = 8979; i <= 8986; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 24748; i <= 24767; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 14148; i <= 14153; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 13922; i <= 14001; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 5855; i <= 5857; i++) + materials[i] = Material.PolishedBasalt; + materials[19871] = Material.PolishedBlackstone; + for (int i = 19875; i <= 19880; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 19881; i <= 19960; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 19961; i <= 20284; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[19872] = Material.PolishedBlackstoneBricks; + for (int i = 20374; i <= 20397; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 20372; i <= 20373; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 20366; i <= 20371; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 20286; i <= 20365; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 20398; i <= 20721; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[25318] = Material.PolishedDeepslate; + for (int i = 25399; i <= 25404; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 25319; i <= 25398; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 25405; i <= 25728; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 14100; i <= 14105; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 13202; i <= 13281; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 14082; i <= 14087; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 12962; i <= 13041; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[21492] = Material.PolishedTuff; + for (int i = 21493; i <= 21498; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 21499; i <= 21578; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 21579; i <= 21902; i++) + materials[i] = Material.PolishedTuffWall; + materials[2077] = Material.Poppy; + for (int i = 8603; i <= 8610; i++) + materials[i] = Material.Potatoes; + materials[8573] = Material.PottedAcaciaSapling; + materials[8581] = Material.PottedAllium; + materials[26561] = Material.PottedAzaleaBush; + materials[8582] = Material.PottedAzureBluet; + materials[12957] = Material.PottedBamboo; + materials[8571] = Material.PottedBirchSapling; + materials[8580] = Material.PottedBlueOrchid; + materials[8592] = Material.PottedBrownMushroom; + materials[8594] = Material.PottedCactus; + materials[8574] = Material.PottedCherrySapling; + materials[8588] = Material.PottedCornflower; + materials[19455] = Material.PottedCrimsonFungus; + materials[19457] = Material.PottedCrimsonRoots; + materials[8578] = Material.PottedDandelion; + materials[8575] = Material.PottedDarkOakSapling; + materials[8593] = Material.PottedDeadBush; + materials[8577] = Material.PottedFern; + materials[26562] = Material.PottedFloweringAzaleaBush; + materials[8572] = Material.PottedJungleSapling; + materials[8589] = Material.PottedLilyOfTheValley; + materials[8576] = Material.PottedMangrovePropagule; + materials[8569] = Material.PottedOakSapling; + materials[8584] = Material.PottedOrangeTulip; + materials[8587] = Material.PottedOxeyeDaisy; + materials[8586] = Material.PottedPinkTulip; + materials[8579] = Material.PottedPoppy; + materials[8591] = Material.PottedRedMushroom; + materials[8583] = Material.PottedRedTulip; + materials[8570] = Material.PottedSpruceSapling; + materials[8568] = Material.PottedTorchflower; + materials[19456] = Material.PottedWarpedFungus; + materials[19458] = Material.PottedWarpedRoots; + materials[8585] = Material.PottedWhiteTulip; + materials[8590] = Material.PottedWitherRose; + materials[22318] = Material.PowderSnow; + for (int i = 7403; i <= 7405; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1944; i <= 1967; i++) + materials[i] = Material.PoweredRail; + materials[10463] = Material.Prismarine; + for (int i = 10712; i <= 10717; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 10546; i <= 10625; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[10464] = Material.PrismarineBricks; + for (int i = 10706; i <= 10711; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 10466; i <= 10545; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 14484; i <= 14807; i++) + materials[i] = Material.PrismarineWall; + materials[6811] = Material.Pumpkin; + for (int i = 6821; i <= 6828; i++) + materials[i] = Material.PumpkinStem; + for (int i = 10919; i <= 10934; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1848; i <= 1863; i++) + materials[i] = Material.PurpleBed; + for (int i = 20901; i <= 20916; i++) + materials[i] = Material.PurpleCandle; + for (int i = 21019; i <= 21020; i++) + materials[i] = Material.PurpleCandleCake; + materials[10738] = Material.PurpleCarpet; + materials[12738] = Material.PurpleConcrete; + materials[12754] = Material.PurpleConcretePowder; + for (int i = 12704; i <= 12707; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 12628; i <= 12633; i++) + materials[i] = Material.PurpleShulkerBox; + materials[5955] = Material.PurpleStainedGlass; + for (int i = 9692; i <= 9723; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[9366] = Material.PurpleTerracotta; + for (int i = 11055; i <= 11058; i++) + materials[i] = Material.PurpleWallBanner; + materials[2057] = Material.PurpleWool; + materials[12410] = Material.PurpurBlock; + for (int i = 12411; i <= 12413; i++) + materials[i] = Material.PurpurPillar; + for (int i = 11300; i <= 11305; i++) + materials[i] = Material.PurpurSlab; + for (int i = 12414; i <= 12493; i++) + materials[i] = Material.PurpurStairs; + materials[9235] = Material.QuartzBlock; + materials[20724] = Material.QuartzBricks; + for (int i = 9237; i <= 9239; i++) + materials[i] = Material.QuartzPillar; + for (int i = 11282; i <= 11287; i++) + materials[i] = Material.QuartzSlab; + for (int i = 9240; i <= 9319; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4662; i <= 4681; i++) + materials[i] = Material.Rail; + materials[26559] = Material.RawCopperBlock; + materials[26560] = Material.RawGoldBlock; + materials[26558] = Material.RawIronBlock; + for (int i = 10983; i <= 10998; i++) + materials[i] = Material.RedBanner; + for (int i = 1912; i <= 1927; i++) + materials[i] = Material.RedBed; + for (int i = 20965; i <= 20980; i++) + materials[i] = Material.RedCandle; + for (int i = 21027; i <= 21028; i++) + materials[i] = Material.RedCandleCake; + materials[10742] = Material.RedCarpet; + materials[12742] = Material.RedConcrete; + materials[12758] = Material.RedConcretePowder; + for (int i = 12720; i <= 12723; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2090] = Material.RedMushroom; + for (int i = 6613; i <= 6676; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 14142; i <= 14147; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 13842; i <= 13921; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 17076; i <= 17399; i++) + materials[i] = Material.RedNetherBrickWall; + materials[12545] = Material.RedNetherBricks; + materials[117] = Material.RedSand; + materials[11079] = Material.RedSandstone; + for (int i = 11288; i <= 11293; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 11082; i <= 11161; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 14808; i <= 15131; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 12652; i <= 12657; i++) + materials[i] = Material.RedShulkerBox; + materials[5959] = Material.RedStainedGlass; + for (int i = 9820; i <= 9851; i++) + materials[i] = Material.RedStainedGlassPane; + materials[9370] = Material.RedTerracotta; + materials[2081] = Material.RedTulip; + for (int i = 11071; i <= 11074; i++) + materials[i] = Material.RedWallBanner; + materials[2061] = Material.RedWool; + materials[9223] = Material.RedstoneBlock; + for (int i = 7417; i <= 7418; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5734; i <= 5735; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5738; i <= 5739; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5740; i <= 5747; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 2978; i <= 4273; i++) + materials[i] = Material.RedstoneWire; + materials[26573] = Material.ReinforcedDeepslate; + for (int i = 5881; i <= 5944; i++) + materials[i] = Material.Repeater; + for (int i = 12515; i <= 12526; i++) + materials[i] = Material.RepeatingCommandBlock; + for (int i = 19450; i <= 19454; i++) + materials[i] = Material.RespawnAnchor; + materials[24902] = Material.RootedDirt; + for (int i = 10751; i <= 10752; i++) + materials[i] = Material.RoseBush; + materials[112] = Material.Sand; + materials[535] = Material.Sandstone; + for (int i = 11234; i <= 11239; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 7431; i <= 7510; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 17400; i <= 17723; i++) + materials[i] = Material.SandstoneWall; + for (int i = 18372; i <= 18403; i++) + materials[i] = Material.Scaffolding; + materials[22799] = Material.Sculk; + for (int i = 22928; i <= 22929; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 22319; i <= 22414; i++) + materials[i] = Material.SculkSensor; + for (int i = 22930; i <= 22937; i++) + materials[i] = Material.SculkShrieker; + for (int i = 22800; i <= 22927; i++) + materials[i] = Material.SculkVein; + materials[10724] = Material.SeaLantern; + for (int i = 12933; i <= 12940; i++) + materials[i] = Material.SeaPickle; + materials[2008] = Material.Seagrass; + materials[2005] = Material.ShortGrass; + materials[18610] = Material.Shroomlight; + for (int i = 12562; i <= 12567; i++) + materials[i] = Material.ShulkerBox; + for (int i = 8827; i <= 8858; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 8859; i <= 8866; i++) + materials[i] = Material.SkeletonWallSkull; + materials[10364] = Material.SlimeBlock; + for (int i = 21069; i <= 21080; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 24884; i <= 24899; i++) + materials[i] = Material.SmallDripleaf; + materials[18466] = Material.SmithingTable; + for (int i = 18420; i <= 18427; i++) + materials[i] = Material.Smoker; + materials[26557] = Material.SmoothBasalt; + materials[11308] = Material.SmoothQuartz; + for (int i = 14124; i <= 14129; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 13602; i <= 13681; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[11309] = Material.SmoothRedSandstone; + for (int i = 14088; i <= 14093; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 13042; i <= 13121; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[11307] = Material.SmoothSandstone; + for (int i = 14118; i <= 14123; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 13522; i <= 13601; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[11306] = Material.SmoothStone; + for (int i = 11228; i <= 11233; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 12800; i <= 12802; i++) + materials[i] = Material.SnifferEgg; + for (int i = 5772; i <= 5779; i++) + materials[i] = Material.Snow; + materials[5781] = Material.SnowBlock; + for (int i = 18543; i <= 18574; i++) + materials[i] = Material.SoulCampfire; + materials[2872] = Material.SoulFire; + for (int i = 18507; i <= 18510; i++) + materials[i] = Material.SoulLantern; + materials[5850] = Material.SoulSand; + materials[5851] = Material.SoulSoil; + materials[5858] = Material.SoulTorch; + for (int i = 5859; i <= 5862; i++) + materials[i] = Material.SoulWallTorch; + materials[2873] = Material.Spawner; + materials[517] = Material.Sponge; + materials[24823] = Material.SporeBlossom; + for (int i = 8635; i <= 8658; i++) + materials[i] = Material.SpruceButton; + for (int i = 11822; i <= 11885; i++) + materials[i] = Material.SpruceDoor; + for (int i = 11566; i <= 11597; i++) + materials[i] = Material.SpruceFence; + for (int i = 11310; i <= 11341; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 4898; i <= 4961; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 265; i <= 292; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 133; i <= 135; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5718; i <= 5719; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 27; i <= 28; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4334; i <= 4365; i++) + materials[i] = Material.SpruceSign; + for (int i = 11168; i <= 11173; i++) + materials[i] = Material.SpruceSlab; + for (int i = 7666; i <= 7745; i++) + materials[i] = Material.SpruceStairs; + for (int i = 6025; i <= 6088; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5546; i <= 5553; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4770; i <= 4777; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 192; i <= 194; i++) + materials[i] = Material.SpruceWood; + for (int i = 1992; i <= 2003; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 11264; i <= 11269; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 7109; i <= 7188; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 15780; i <= 16103; i++) + materials[i] = Material.StoneBrickWall; + materials[6537] = Material.StoneBricks; + for (int i = 5748; i <= 5771; i++) + materials[i] = Material.StoneButton; + for (int i = 5650; i <= 5651; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 11222; i <= 11227; i++) + materials[i] = Material.StoneSlab; + for (int i = 13442; i <= 13521; i++) + materials[i] = Material.StoneStairs; + for (int i = 18467; i <= 18470; i++) + materials[i] = Material.Stonecutter; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 165; i <= 167; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 219; i <= 221; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 18605; i <= 18607; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 18599; i <= 18601; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 168; i <= 170; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 222; i <= 224; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 213; i <= 215; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 162; i <= 164; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 216; i <= 218; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 18588; i <= 18590; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 18582; i <= 18584; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 19356; i <= 19359; i++) + materials[i] = Material.StructureBlock; + materials[12549] = Material.StructureVoid; + for (int i = 5799; i <= 5814; i++) + materials[i] = Material.SugarCane; + for (int i = 10747; i <= 10748; i++) + materials[i] = Material.Sunflower; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 113; i <= 116; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 18575; i <= 18578; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 10755; i <= 10756; i++) + materials[i] = Material.TallGrass; + for (int i = 2009; i <= 2010; i++) + materials[i] = Material.TallSeagrass; + for (int i = 19381; i <= 19396; i++) + materials[i] = Material.Target; + materials[10744] = Material.Terracotta; + materials[22317] = Material.TintedGlass; + for (int i = 2094; i <= 2095; i++) + materials[i] = Material.Tnt; + materials[2355] = Material.Torch; + materials[2076] = Material.Torchflower; + for (int i = 12495; i <= 12496; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 9119; i <= 9142; i++) + materials[i] = Material.TrappedChest; + for (int i = 26638; i <= 26649; i++) + materials[i] = Material.TrialSpawner; + for (int i = 7537; i <= 7664; i++) + materials[i] = Material.Tripwire; + for (int i = 7521; i <= 7536; i++) + materials[i] = Material.TripwireHook; + for (int i = 12823; i <= 12824; i++) + materials[i] = Material.TubeCoral; + materials[12808] = Material.TubeCoralBlock; + for (int i = 12843; i <= 12844; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 12893; i <= 12900; i++) + materials[i] = Material.TubeCoralWallFan; + materials[21081] = Material.Tuff; + for (int i = 21905; i <= 21910; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 21911; i <= 21990; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 21991; i <= 22314; i++) + materials[i] = Material.TuffBrickWall; + materials[21904] = Material.TuffBricks; + for (int i = 21082; i <= 21087; i++) + materials[i] = Material.TuffSlab; + for (int i = 21088; i <= 21167; i++) + materials[i] = Material.TuffStairs; + for (int i = 21168; i <= 21491; i++) + materials[i] = Material.TuffWall; + for (int i = 12788; i <= 12799; i++) + materials[i] = Material.TurtleEgg; + for (int i = 18638; i <= 18663; i++) + materials[i] = Material.TwistingVines; + materials[18664] = Material.TwistingVinesPlant; + for (int i = 26650; i <= 26681; i++) + materials[i] = Material.Vault; + for (int i = 26566; i <= 26568; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 6837; i <= 6868; i++) + materials[i] = Material.Vine; + materials[12958] = Material.VoidAir; + for (int i = 2356; i <= 2359; i++) + materials[i] = Material.WallTorch; + for (int i = 19124; i <= 19147; i++) + materials[i] = Material.WarpedButton; + for (int i = 19212; i <= 19275; i++) + materials[i] = Material.WarpedDoor; + for (int i = 18716; i <= 18747; i++) + materials[i] = Material.WarpedFence; + for (int i = 18908; i <= 18939; i++) + materials[i] = Material.WarpedFenceGate; + materials[18592] = Material.WarpedFungus; + for (int i = 5346; i <= 5409; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 18585; i <= 18587; i++) + materials[i] = Material.WarpedHyphae; + materials[18591] = Material.WarpedNylium; + materials[18667] = Material.WarpedPlanks; + for (int i = 18682; i <= 18683; i++) + materials[i] = Material.WarpedPressurePlate; + materials[18594] = Material.WarpedRoots; + for (int i = 19308; i <= 19339; i++) + materials[i] = Material.WarpedSign; + for (int i = 18674; i <= 18679; i++) + materials[i] = Material.WarpedSlab; + for (int i = 19020; i <= 19099; i++) + materials[i] = Material.WarpedStairs; + for (int i = 18579; i <= 18581; i++) + materials[i] = Material.WarpedStem; + for (int i = 18812; i <= 18875; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5610; i <= 5617; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 19348; i <= 19355; i++) + materials[i] = Material.WarpedWallSign; + materials[18593] = Material.WarpedWartBlock; + for (int i = 80; i <= 95; i++) + materials[i] = Material.Water; + for (int i = 7399; i <= 7401; i++) + materials[i] = Material.WaterCauldron; + materials[22955] = Material.WaxedChiseledCopper; + materials[23300] = Material.WaxedCopperBlock; + for (int i = 24708; i <= 24711; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 23908; i <= 23971; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 24684; i <= 24685; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 24420; i <= 24483; i++) + materials[i] = Material.WaxedCopperTrapdoor; + materials[23307] = Material.WaxedCutCopper; + for (int i = 23646; i <= 23651; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 23548; i <= 23627; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[22954] = Material.WaxedExposedChiseledCopper; + materials[23302] = Material.WaxedExposedCopper; + for (int i = 24712; i <= 24715; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 23972; i <= 24035; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 24686; i <= 24687; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 24484; i <= 24547; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + materials[23306] = Material.WaxedExposedCutCopper; + for (int i = 23640; i <= 23645; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 23468; i <= 23547; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[22952] = Material.WaxedOxidizedChiseledCopper; + materials[23303] = Material.WaxedOxidizedCopper; + for (int i = 24720; i <= 24723; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 24036; i <= 24099; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 24690; i <= 24691; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 24548; i <= 24611; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + materials[23304] = Material.WaxedOxidizedCutCopper; + for (int i = 23628; i <= 23633; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 23308; i <= 23387; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[22953] = Material.WaxedWeatheredChiseledCopper; + materials[23301] = Material.WaxedWeatheredCopper; + for (int i = 24716; i <= 24719; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 24100; i <= 24163; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 24688; i <= 24689; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 24612; i <= 24675; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + materials[23305] = Material.WaxedWeatheredCutCopper; + for (int i = 23634; i <= 23639; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 23388; i <= 23467; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[22949] = Material.WeatheredChiseledCopper; + materials[22940] = Material.WeatheredCopper; + for (int i = 24700; i <= 24703; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 23844; i <= 23907; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 24680; i <= 24681; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 24356; i <= 24419; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + materials[22945] = Material.WeatheredCutCopper; + for (int i = 23282; i <= 23287; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 23036; i <= 23115; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 18611; i <= 18636; i++) + materials[i] = Material.WeepingVines; + materials[18637] = Material.WeepingVinesPlant; + materials[518] = Material.WetSponge; + for (int i = 4278; i <= 4285; i++) + materials[i] = Material.Wheat; + for (int i = 10759; i <= 10774; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1688; i <= 1703; i++) + materials[i] = Material.WhiteBed; + for (int i = 20741; i <= 20756; i++) + materials[i] = Material.WhiteCandle; + for (int i = 20999; i <= 21000; i++) + materials[i] = Material.WhiteCandleCake; + materials[10728] = Material.WhiteCarpet; + materials[12728] = Material.WhiteConcrete; + materials[12744] = Material.WhiteConcretePowder; + for (int i = 12664; i <= 12667; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 12568; i <= 12573; i++) + materials[i] = Material.WhiteShulkerBox; + materials[5945] = Material.WhiteStainedGlass; + for (int i = 9372; i <= 9403; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[9356] = Material.WhiteTerracotta; + materials[2083] = Material.WhiteTulip; + for (int i = 11015; i <= 11018; i++) + materials[i] = Material.WhiteWallBanner; + materials[2047] = Material.WhiteWool; + materials[2087] = Material.WitherRose; + for (int i = 8867; i <= 8898; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 8899; i <= 8906; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 10823; i <= 10838; i++) + materials[i] = Material.YellowBanner; + for (int i = 1752; i <= 1767; i++) + materials[i] = Material.YellowBed; + for (int i = 20805; i <= 20820; i++) + materials[i] = Material.YellowCandle; + for (int i = 21007; i <= 21008; i++) + materials[i] = Material.YellowCandleCake; + materials[10732] = Material.YellowCarpet; + materials[12732] = Material.YellowConcrete; + materials[12748] = Material.YellowConcretePowder; + for (int i = 12680; i <= 12683; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 12592; i <= 12597; i++) + materials[i] = Material.YellowShulkerBox; + materials[5949] = Material.YellowStainedGlass; + for (int i = 9500; i <= 9531; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[9360] = Material.YellowTerracotta; + for (int i = 11031; i <= 11034; i++) + materials[i] = Material.YellowWallBanner; + materials[2051] = Material.YellowWool; + for (int i = 8907; i <= 8938; i++) + materials[i] = Material.ZombieHead; + for (int i = 8939; i <= 8946; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1212.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1212.cs new file mode 100644 index 00000000..e46c7650 --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1212.cs @@ -0,0 +1,1811 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1212 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1212() + { + for (int i = 8938; i <= 8961; i++) + materials[i] = Material.AcaciaButton; + for (int i = 12419; i <= 12482; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 12035; i <= 12066; i++) + materials[i] = Material.AcaciaFence; + for (int i = 11747; i <= 11778; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 5118; i <= 5181; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5888; i <= 5889; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4450; i <= 4481; i++) + materials[i] = Material.AcaciaSign; + for (int i = 11521; i <= 11526; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 10139; i <= 10218; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6383; i <= 6446; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5718; i <= 5725; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4870; i <= 4877; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 9575; i <= 9598; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2122] = Material.Allium; + materials[21500] = Material.AmethystBlock; + for (int i = 21502; i <= 21513; i++) + materials[i] = Material.AmethystCluster; + materials[19917] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 14605; i <= 14610; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 14231; i <= 14310; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 17221; i <= 17544; i++) + materials[i] = Material.AndesiteWall; + for (int i = 9362; i <= 9365; i++) + materials[i] = Material.Anvil; + for (int i = 7047; i <= 7050; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 7043; i <= 7046; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[25293] = Material.Azalea; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + materials[2123] = Material.AzureBluet; + for (int i = 13414; i <= 13425; i++) + materials[i] = Material.Bamboo; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 9058; i <= 9081; i++) + materials[i] = Material.BambooButton; + for (int i = 12739; i <= 12802; i++) + materials[i] = Material.BambooDoor; + for (int i = 12195; i <= 12226; i++) + materials[i] = Material.BambooFence; + for (int i = 11907; i <= 11938; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5630; i <= 5693; i++) + materials[i] = Material.BambooHangingSign; + materials[28] = Material.BambooMosaic; + for (int i = 11557; i <= 11562; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 10619; i <= 10698; i++) + materials[i] = Material.BambooMosaicStairs; + materials[27] = Material.BambooPlanks; + for (int i = 5898; i <= 5899; i++) + materials[i] = Material.BambooPressurePlate; + materials[13413] = Material.BambooSapling; + for (int i = 4642; i <= 4673; i++) + materials[i] = Material.BambooSign; + for (int i = 11551; i <= 11556; i++) + materials[i] = Material.BambooSlab; + for (int i = 10539; i <= 10618; i++) + materials[i] = Material.BambooStairs; + for (int i = 6703; i <= 6766; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5782; i <= 5789; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4918; i <= 4925; i++) + materials[i] = Material.BambooWallSign; + for (int i = 18877; i <= 18888; i++) + materials[i] = Material.Barrel; + for (int i = 10700; i <= 10701; i++) + materials[i] = Material.Barrier; + for (int i = 6018; i <= 6020; i++) + materials[i] = Material.Basalt; + materials[8148] = Material.Beacon; + materials[85] = Material.Bedrock; + for (int i = 19866; i <= 19889; i++) + materials[i] = Material.BeeNest; + for (int i = 19890; i <= 19913; i++) + materials[i] = Material.Beehive; + for (int i = 12978; i <= 12981; i++) + materials[i] = Material.Beetroots; + for (int i = 18940; i <= 18971; i++) + materials[i] = Material.Bell; + for (int i = 25313; i <= 25344; i++) + materials[i] = Material.BigDripleaf; + for (int i = 25345; i <= 25352; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 8890; i <= 8913; i++) + materials[i] = Material.BirchButton; + for (int i = 12291; i <= 12354; i++) + materials[i] = Material.BirchDoor; + for (int i = 11971; i <= 12002; i++) + materials[i] = Material.BirchFence; + for (int i = 11683; i <= 11714; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 5054; i <= 5117; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5884; i <= 5885; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 4418; i <= 4449; i++) + materials[i] = Material.BirchSign; + for (int i = 11509; i <= 11514; i++) + materials[i] = Material.BirchSlab; + for (int i = 7976; i <= 8055; i++) + materials[i] = Material.BirchStairs; + for (int i = 6255; i <= 6318; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5710; i <= 5717; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4862; i <= 4869; i++) + materials[i] = Material.BirchWallSign; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 11334; i <= 11349; i++) + materials[i] = Material.BlackBanner; + for (int i = 1971; i <= 1986; i++) + materials[i] = Material.BlackBed; + for (int i = 21450; i <= 21465; i++) + materials[i] = Material.BlackCandle; + for (int i = 21498; i <= 21499; i++) + materials[i] = Material.BlackCandleCake; + materials[11078] = Material.BlackCarpet; + materials[13212] = Material.BlackConcrete; + materials[13228] = Material.BlackConcretePowder; + for (int i = 13193; i <= 13196; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 13127; i <= 13132; i++) + materials[i] = Material.BlackShulkerBox; + materials[6126] = Material.BlackStainedGlass; + for (int i = 10107; i <= 10138; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[9626] = Material.BlackTerracotta; + for (int i = 11410; i <= 11413; i++) + materials[i] = Material.BlackWallBanner; + materials[2105] = Material.BlackWool; + materials[19929] = Material.Blackstone; + for (int i = 20334; i <= 20339; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 19930; i <= 20009; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 20010; i <= 20333; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 18897; i <= 18904; i++) + materials[i] = Material.BlastFurnace; + for (int i = 11270; i <= 11285; i++) + materials[i] = Material.BlueBanner; + for (int i = 1907; i <= 1922; i++) + materials[i] = Material.BlueBed; + for (int i = 21386; i <= 21401; i++) + materials[i] = Material.BlueCandle; + for (int i = 21490; i <= 21491; i++) + materials[i] = Material.BlueCandleCake; + materials[11074] = Material.BlueCarpet; + materials[13208] = Material.BlueConcrete; + materials[13224] = Material.BlueConcretePowder; + for (int i = 13177; i <= 13180; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[13410] = Material.BlueIce; + materials[2121] = Material.BlueOrchid; + for (int i = 13103; i <= 13108; i++) + materials[i] = Material.BlueShulkerBox; + materials[6122] = Material.BlueStainedGlass; + for (int i = 9979; i <= 10010; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[9622] = Material.BlueTerracotta; + for (int i = 11394; i <= 11397; i++) + materials[i] = Material.BlueWallBanner; + materials[2101] = Material.BlueWool; + for (int i = 13015; i <= 13017; i++) + materials[i] = Material.BoneBlock; + materials[2139] = Material.Bookshelf; + for (int i = 13294; i <= 13295; i++) + materials[i] = Material.BrainCoral; + materials[13278] = Material.BrainCoralBlock; + for (int i = 13314; i <= 13315; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 13370; i <= 13377; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 7620; i <= 7627; i++) + materials[i] = Material.BrewingStand; + for (int i = 11599; i <= 11604; i++) + materials[i] = Material.BrickSlab; + for (int i = 7259; i <= 7338; i++) + materials[i] = Material.BrickStairs; + for (int i = 14629; i <= 14952; i++) + materials[i] = Material.BrickWall; + materials[2136] = Material.Bricks; + for (int i = 11286; i <= 11301; i++) + materials[i] = Material.BrownBanner; + for (int i = 1923; i <= 1938; i++) + materials[i] = Material.BrownBed; + for (int i = 21402; i <= 21417; i++) + materials[i] = Material.BrownCandle; + for (int i = 21492; i <= 21493; i++) + materials[i] = Material.BrownCandleCake; + materials[11075] = Material.BrownCarpet; + materials[13209] = Material.BrownConcrete; + materials[13225] = Material.BrownConcretePowder; + for (int i = 13181; i <= 13184; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2132] = Material.BrownMushroom; + for (int i = 6779; i <= 6842; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 13109; i <= 13114; i++) + materials[i] = Material.BrownShulkerBox; + materials[6123] = Material.BrownStainedGlass; + for (int i = 10011; i <= 10042; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[9623] = Material.BrownTerracotta; + for (int i = 11398; i <= 11401; i++) + materials[i] = Material.BrownWallBanner; + materials[2102] = Material.BrownWool; + for (int i = 13429; i <= 13430; i++) + materials[i] = Material.BubbleColumn; + for (int i = 13296; i <= 13297; i++) + materials[i] = Material.BubbleCoral; + materials[13279] = Material.BubbleCoralBlock; + for (int i = 13316; i <= 13317; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 13378; i <= 13385; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[21501] = Material.BuddingAmethyst; + for (int i = 5948; i <= 5963; i++) + materials[i] = Material.Cactus; + for (int i = 6040; i <= 6046; i++) + materials[i] = Material.Cake; + materials[22785] = Material.Calcite; + for (int i = 22884; i <= 23267; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 18980; i <= 19011; i++) + materials[i] = Material.Campfire; + for (int i = 21194; i <= 21209; i++) + materials[i] = Material.Candle; + for (int i = 21466; i <= 21467; i++) + materials[i] = Material.CandleCake; + for (int i = 8826; i <= 8833; i++) + materials[i] = Material.Carrots; + materials[18905] = Material.CartographyTable; + for (int i = 6032; i <= 6035; i++) + materials[i] = Material.CarvedPumpkin; + materials[7628] = Material.Cauldron; + materials[13428] = Material.CaveAir; + for (int i = 25238; i <= 25289; i++) + materials[i] = Material.CaveVines; + for (int i = 25290; i <= 25291; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 7003; i <= 7008; i++) + materials[i] = Material.Chain; + for (int i = 12996; i <= 13007; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 8962; i <= 8985; i++) + materials[i] = Material.CherryButton; + for (int i = 12483; i <= 12546; i++) + materials[i] = Material.CherryDoor; + for (int i = 12067; i <= 12098; i++) + materials[i] = Material.CherryFence; + for (int i = 11779; i <= 11810; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 5182; i <= 5245; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + materials[20] = Material.CherryPlanks; + for (int i = 5890; i <= 5891; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 4482; i <= 4513; i++) + materials[i] = Material.CherrySign; + for (int i = 11527; i <= 11532; i++) + materials[i] = Material.CherrySlab; + for (int i = 10219; i <= 10298; i++) + materials[i] = Material.CherryStairs; + for (int i = 6447; i <= 6510; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 5726; i <= 5733; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 4878; i <= 4885; i++) + materials[i] = Material.CherryWallSign; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 3006; i <= 3029; i++) + materials[i] = Material.Chest; + for (int i = 9366; i <= 9369; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2140; i <= 2395; i++) + materials[i] = Material.ChiseledBookshelf; + materials[23420] = Material.ChiseledCopper; + materials[27020] = Material.ChiseledDeepslate; + materials[21191] = Material.ChiseledNetherBricks; + materials[20343] = Material.ChiseledPolishedBlackstone; + materials[9491] = Material.ChiseledQuartzBlock; + materials[11415] = Material.ChiseledRedSandstone; + materials[579] = Material.ChiseledSandstone; + materials[6770] = Material.ChiseledStoneBricks; + materials[22372] = Material.ChiseledTuff; + materials[22784] = Material.ChiseledTuffBricks; + for (int i = 12873; i <= 12878; i++) + materials[i] = Material.ChorusFlower; + for (int i = 12809; i <= 12872; i++) + materials[i] = Material.ChorusPlant; + materials[5964] = Material.Clay; + materials[11080] = Material.CoalBlock; + materials[133] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[25376] = Material.CobbledDeepslate; + for (int i = 25457; i <= 25462; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 25377; i <= 25456; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 25463; i <= 25786; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 11593; i <= 11598; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4766; i <= 4845; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 8149; i <= 8472; i++) + materials[i] = Material.CobblestoneWall; + materials[2047] = Material.Cobweb; + for (int i = 7649; i <= 7660; i++) + materials[i] = Material.Cocoa; + for (int i = 8136; i <= 8147; i++) + materials[i] = Material.CommandBlock; + for (int i = 9430; i <= 9445; i++) + materials[i] = Material.Comparator; + for (int i = 19841; i <= 19849; i++) + materials[i] = Material.Composter; + for (int i = 13411; i <= 13412; i++) + materials[i] = Material.Conduit; + materials[23407] = Material.CopperBlock; + for (int i = 25161; i <= 25164; i++) + materials[i] = Material.CopperBulb; + for (int i = 24121; i <= 24184; i++) + materials[i] = Material.CopperDoor; + for (int i = 25145; i <= 25146; i++) + materials[i] = Material.CopperGrate; + materials[23411] = Material.CopperOre; + for (int i = 24633; i <= 24696; i++) + materials[i] = Material.CopperTrapdoor; + materials[2129] = Material.Cornflower; + materials[27021] = Material.CrackedDeepslateBricks; + materials[27022] = Material.CrackedDeepslateTiles; + materials[21192] = Material.CrackedNetherBricks; + materials[20342] = Material.CrackedPolishedBlackstoneBricks; + materials[6769] = Material.CrackedStoneBricks; + for (int i = 27059; i <= 27106; i++) + materials[i] = Material.Crafter; + materials[4329] = Material.CraftingTable; + for (int i = 2917; i <= 2925; i++) + materials[i] = Material.CreakingHeart; + for (int i = 9242; i <= 9273; i++) + materials[i] = Material.CreeperHead; + for (int i = 9274; i <= 9281; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 19569; i <= 19592; i++) + materials[i] = Material.CrimsonButton; + for (int i = 19617; i <= 19680; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 19153; i <= 19184; i++) + materials[i] = Material.CrimsonFence; + for (int i = 19345; i <= 19376; i++) + materials[i] = Material.CrimsonFenceGate; + materials[19078] = Material.CrimsonFungus; + for (int i = 5438; i <= 5501; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 19071; i <= 19073; i++) + materials[i] = Material.CrimsonHyphae; + materials[19077] = Material.CrimsonNylium; + materials[19135] = Material.CrimsonPlanks; + for (int i = 19149; i <= 19150; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[19134] = Material.CrimsonRoots; + for (int i = 19745; i <= 19776; i++) + materials[i] = Material.CrimsonSign; + for (int i = 19137; i <= 19142; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 19409; i <= 19488; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 19065; i <= 19067; i++) + materials[i] = Material.CrimsonStem; + for (int i = 19217; i <= 19280; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5766; i <= 5773; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 19809; i <= 19816; i++) + materials[i] = Material.CrimsonWallSign; + materials[19918] = Material.CryingObsidian; + materials[23416] = Material.CutCopper; + for (int i = 23763; i <= 23768; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 23665; i <= 23744; i++) + materials[i] = Material.CutCopperStairs; + materials[11416] = Material.CutRedSandstone; + for (int i = 11635; i <= 11640; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[580] = Material.CutSandstone; + for (int i = 11581; i <= 11586; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 11238; i <= 11253; i++) + materials[i] = Material.CyanBanner; + for (int i = 1875; i <= 1890; i++) + materials[i] = Material.CyanBed; + for (int i = 21354; i <= 21369; i++) + materials[i] = Material.CyanCandle; + for (int i = 21486; i <= 21487; i++) + materials[i] = Material.CyanCandleCake; + materials[11072] = Material.CyanCarpet; + materials[13206] = Material.CyanConcrete; + materials[13222] = Material.CyanConcretePowder; + for (int i = 13169; i <= 13172; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 13091; i <= 13096; i++) + materials[i] = Material.CyanShulkerBox; + materials[6120] = Material.CyanStainedGlass; + for (int i = 9915; i <= 9946; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[9620] = Material.CyanTerracotta; + for (int i = 11386; i <= 11389; i++) + materials[i] = Material.CyanWallBanner; + materials[2099] = Material.CyanWool; + for (int i = 9370; i <= 9373; i++) + materials[i] = Material.DamagedAnvil; + materials[2118] = Material.Dandelion; + for (int i = 8986; i <= 9009; i++) + materials[i] = Material.DarkOakButton; + for (int i = 12547; i <= 12610; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 12099; i <= 12130; i++) + materials[i] = Material.DarkOakFence; + for (int i = 11811; i <= 11842; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5310; i <= 5373; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + materials[21] = Material.DarkOakPlanks; + for (int i = 5892; i <= 5893; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4546; i <= 4577; i++) + materials[i] = Material.DarkOakSign; + for (int i = 11533; i <= 11538; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 10299; i <= 10378; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6511; i <= 6574; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5742; i <= 5749; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4894; i <= 4901; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + materials[10800] = Material.DarkPrismarine; + for (int i = 11053; i <= 11058; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 10961; i <= 11040; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 9446; i <= 9477; i++) + materials[i] = Material.DaylightDetector; + for (int i = 13284; i <= 13285; i++) + materials[i] = Material.DeadBrainCoral; + materials[13273] = Material.DeadBrainCoralBlock; + for (int i = 13304; i <= 13305; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 13330; i <= 13337; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 13286; i <= 13287; i++) + materials[i] = Material.DeadBubbleCoral; + materials[13274] = Material.DeadBubbleCoralBlock; + for (int i = 13306; i <= 13307; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 13338; i <= 13345; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[2050] = Material.DeadBush; + for (int i = 13288; i <= 13289; i++) + materials[i] = Material.DeadFireCoral; + materials[13275] = Material.DeadFireCoralBlock; + for (int i = 13308; i <= 13309; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 13346; i <= 13353; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 13290; i <= 13291; i++) + materials[i] = Material.DeadHornCoral; + materials[13276] = Material.DeadHornCoralBlock; + for (int i = 13310; i <= 13311; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 13354; i <= 13361; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 13282; i <= 13283; i++) + materials[i] = Material.DeadTubeCoral; + materials[13272] = Material.DeadTubeCoralBlock; + for (int i = 13302; i <= 13303; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 13322; i <= 13329; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 27043; i <= 27058; i++) + materials[i] = Material.DecoratedPot; + for (int i = 25373; i <= 25375; i++) + materials[i] = Material.Deepslate; + for (int i = 26690; i <= 26695; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 26610; i <= 26689; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 26696; i <= 27019; i++) + materials[i] = Material.DeepslateBrickWall; + materials[26609] = Material.DeepslateBricks; + materials[134] = Material.DeepslateCoalOre; + materials[23412] = Material.DeepslateCopperOre; + materials[4327] = Material.DeepslateDiamondOre; + materials[7742] = Material.DeepslateEmeraldOre; + materials[130] = Material.DeepslateGoldOre; + materials[132] = Material.DeepslateIronOre; + materials[564] = Material.DeepslateLapisOre; + for (int i = 5902; i <= 5903; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 26279; i <= 26284; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 26199; i <= 26278; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 26285; i <= 26608; i++) + materials[i] = Material.DeepslateTileWall; + materials[26198] = Material.DeepslateTiles; + for (int i = 2011; i <= 2034; i++) + materials[i] = Material.DetectorRail; + materials[4328] = Material.DiamondBlock; + materials[4326] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 14623; i <= 14628; i++) + materials[i] = Material.DioriteSlab; + for (int i = 14471; i <= 14550; i++) + materials[i] = Material.DioriteStairs; + for (int i = 18517; i <= 18840; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[12982] = Material.DirtPath; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + materials[7646] = Material.DragonEgg; + for (int i = 9282; i <= 9313; i++) + materials[i] = Material.DragonHead; + for (int i = 9314; i <= 9321; i++) + materials[i] = Material.DragonWallHead; + materials[13256] = Material.DriedKelpBlock; + materials[25237] = Material.DripstoneBlock; + for (int i = 9599; i <= 9610; i++) + materials[i] = Material.Dropper; + materials[7895] = Material.EmeraldBlock; + materials[7741] = Material.EmeraldOre; + materials[7619] = Material.EnchantingTable; + materials[12983] = Material.EndGateway; + materials[7636] = Material.EndPortal; + for (int i = 7637; i <= 7644; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 12803; i <= 12808; i++) + materials[i] = Material.EndRod; + materials[7645] = Material.EndStone; + for (int i = 14581; i <= 14586; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 13831; i <= 13910; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 18193; i <= 18516; i++) + materials[i] = Material.EndStoneBrickWall; + materials[12963] = Material.EndStoneBricks; + for (int i = 7743; i <= 7750; i++) + materials[i] = Material.EnderChest; + materials[23419] = Material.ExposedChiseledCopper; + materials[23408] = Material.ExposedCopper; + for (int i = 25165; i <= 25168; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 24185; i <= 24248; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 25147; i <= 25148; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 24697; i <= 24760; i++) + materials[i] = Material.ExposedCopperTrapdoor; + materials[23415] = Material.ExposedCutCopper; + for (int i = 23757; i <= 23762; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 23585; i <= 23664; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4338; i <= 4345; i++) + materials[i] = Material.Farmland; + materials[2049] = Material.Fern; + for (int i = 2403; i <= 2914; i++) + materials[i] = Material.Fire; + for (int i = 13298; i <= 13299; i++) + materials[i] = Material.FireCoral; + materials[13280] = Material.FireCoralBlock; + for (int i = 13318; i <= 13319; i++) + materials[i] = Material.FireCoralFan; + for (int i = 13386; i <= 13393; i++) + materials[i] = Material.FireCoralWallFan; + materials[18906] = Material.FletchingTable; + materials[8797] = Material.FlowerPot; + materials[25294] = Material.FloweringAzalea; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[27041] = Material.Frogspawn; + for (int i = 13008; i <= 13011; i++) + materials[i] = Material.FrostedIce; + for (int i = 4346; i <= 4353; i++) + materials[i] = Material.Furnace; + materials[20754] = Material.GildedBlackstone; + materials[562] = Material.Glass; + for (int i = 7009; i <= 7040; i++) + materials[i] = Material.GlassPane; + for (int i = 7099; i <= 7226; i++) + materials[i] = Material.GlowLichen; + materials[6029] = Material.Glowstone; + materials[2134] = Material.GoldBlock; + materials[129] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 14599; i <= 14604; i++) + materials[i] = Material.GraniteSlab; + for (int i = 14151; i <= 14230; i++) + materials[i] = Material.GraniteStairs; + for (int i = 15925; i <= 16248; i++) + materials[i] = Material.GraniteWall; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[124] = Material.Gravel; + for (int i = 11206; i <= 11221; i++) + materials[i] = Material.GrayBanner; + for (int i = 1843; i <= 1858; i++) + materials[i] = Material.GrayBed; + for (int i = 21322; i <= 21337; i++) + materials[i] = Material.GrayCandle; + for (int i = 21482; i <= 21483; i++) + materials[i] = Material.GrayCandleCake; + materials[11070] = Material.GrayCarpet; + materials[13204] = Material.GrayConcrete; + materials[13220] = Material.GrayConcretePowder; + for (int i = 13161; i <= 13164; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 13079; i <= 13084; i++) + materials[i] = Material.GrayShulkerBox; + materials[6118] = Material.GrayStainedGlass; + for (int i = 9851; i <= 9882; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[9618] = Material.GrayTerracotta; + for (int i = 11378; i <= 11381; i++) + materials[i] = Material.GrayWallBanner; + materials[2097] = Material.GrayWool; + for (int i = 11302; i <= 11317; i++) + materials[i] = Material.GreenBanner; + for (int i = 1939; i <= 1954; i++) + materials[i] = Material.GreenBed; + for (int i = 21418; i <= 21433; i++) + materials[i] = Material.GreenCandle; + for (int i = 21494; i <= 21495; i++) + materials[i] = Material.GreenCandleCake; + materials[11076] = Material.GreenCarpet; + materials[13210] = Material.GreenConcrete; + materials[13226] = Material.GreenConcretePowder; + for (int i = 13185; i <= 13188; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 13115; i <= 13120; i++) + materials[i] = Material.GreenShulkerBox; + materials[6124] = Material.GreenStainedGlass; + for (int i = 10043; i <= 10074; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[9624] = Material.GreenTerracotta; + for (int i = 11402; i <= 11405; i++) + materials[i] = Material.GreenWallBanner; + materials[2103] = Material.GreenWool; + for (int i = 18907; i <= 18918; i++) + materials[i] = Material.Grindstone; + for (int i = 25369; i <= 25370; i++) + materials[i] = Material.HangingRoots; + for (int i = 11060; i <= 11062; i++) + materials[i] = Material.HayBlock; + for (int i = 27151; i <= 27152; i++) + materials[i] = Material.HeavyCore; + for (int i = 9414; i <= 9429; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[19914] = Material.HoneyBlock; + materials[19915] = Material.HoneycombBlock; + for (int i = 9480; i <= 9489; i++) + materials[i] = Material.Hopper; + for (int i = 13300; i <= 13301; i++) + materials[i] = Material.HornCoral; + materials[13281] = Material.HornCoralBlock; + for (int i = 13320; i <= 13321; i++) + materials[i] = Material.HornCoralFan; + for (int i = 13394; i <= 13401; i++) + materials[i] = Material.HornCoralWallFan; + materials[5946] = Material.Ice; + materials[6778] = Material.InfestedChiseledStoneBricks; + materials[6774] = Material.InfestedCobblestone; + materials[6777] = Material.InfestedCrackedStoneBricks; + for (int i = 27023; i <= 27025; i++) + materials[i] = Material.InfestedDeepslate; + materials[6776] = Material.InfestedMossyStoneBricks; + materials[6773] = Material.InfestedStone; + materials[6775] = Material.InfestedStoneBricks; + for (int i = 6971; i <= 7002; i++) + materials[i] = Material.IronBars; + materials[2135] = Material.IronBlock; + for (int i = 5816; i <= 5879; i++) + materials[i] = Material.IronDoor; + materials[131] = Material.IronOre; + for (int i = 10734; i <= 10797; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 6036; i <= 6039; i++) + materials[i] = Material.JackOLantern; + for (int i = 19829; i <= 19840; i++) + materials[i] = Material.Jigsaw; + for (int i = 5981; i <= 5982; i++) + materials[i] = Material.Jukebox; + for (int i = 8914; i <= 8937; i++) + materials[i] = Material.JungleButton; + for (int i = 12355; i <= 12418; i++) + materials[i] = Material.JungleDoor; + for (int i = 12003; i <= 12034; i++) + materials[i] = Material.JungleFence; + for (int i = 11715; i <= 11746; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 5246; i <= 5309; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5886; i <= 5887; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 4514; i <= 4545; i++) + materials[i] = Material.JungleSign; + for (int i = 11515; i <= 11520; i++) + materials[i] = Material.JungleSlab; + for (int i = 8056; i <= 8135; i++) + materials[i] = Material.JungleStairs; + for (int i = 6319; i <= 6382; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5734; i <= 5741; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4886; i <= 4893; i++) + materials[i] = Material.JungleWallSign; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 13229; i <= 13254; i++) + materials[i] = Material.Kelp; + materials[13255] = Material.KelpPlant; + for (int i = 4738; i <= 4745; i++) + materials[i] = Material.Ladder; + for (int i = 18972; i <= 18975; i++) + materials[i] = Material.Lantern; + materials[565] = Material.LapisBlock; + materials[563] = Material.LapisOre; + for (int i = 21514; i <= 21525; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 11092; i <= 11093; i++) + materials[i] = Material.LargeFern; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + materials[7632] = Material.LavaCauldron; + for (int i = 18919; i <= 18934; i++) + materials[i] = Material.Lectern; + for (int i = 5790; i <= 5813; i++) + materials[i] = Material.Lever; + for (int i = 10702; i <= 10733; i++) + materials[i] = Material.Light; + for (int i = 11142; i <= 11157; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1779; i <= 1794; i++) + materials[i] = Material.LightBlueBed; + for (int i = 21258; i <= 21273; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 21474; i <= 21475; i++) + materials[i] = Material.LightBlueCandleCake; + materials[11066] = Material.LightBlueCarpet; + materials[13200] = Material.LightBlueConcrete; + materials[13216] = Material.LightBlueConcretePowder; + for (int i = 13145; i <= 13148; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 13055; i <= 13060; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[6114] = Material.LightBlueStainedGlass; + for (int i = 9723; i <= 9754; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[9614] = Material.LightBlueTerracotta; + for (int i = 11362; i <= 11365; i++) + materials[i] = Material.LightBlueWallBanner; + materials[2093] = Material.LightBlueWool; + for (int i = 11222; i <= 11237; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1859; i <= 1874; i++) + materials[i] = Material.LightGrayBed; + for (int i = 21338; i <= 21353; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 21484; i <= 21485; i++) + materials[i] = Material.LightGrayCandleCake; + materials[11071] = Material.LightGrayCarpet; + materials[13205] = Material.LightGrayConcrete; + materials[13221] = Material.LightGrayConcretePowder; + for (int i = 13165; i <= 13168; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 13085; i <= 13090; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[6119] = Material.LightGrayStainedGlass; + for (int i = 9883; i <= 9914; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[9619] = Material.LightGrayTerracotta; + for (int i = 11382; i <= 11385; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2098] = Material.LightGrayWool; + for (int i = 9398; i <= 9413; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 25193; i <= 25216; i++) + materials[i] = Material.LightningRod; + for (int i = 11084; i <= 11085; i++) + materials[i] = Material.Lilac; + materials[2131] = Material.LilyOfTheValley; + materials[7501] = Material.LilyPad; + for (int i = 11174; i <= 11189; i++) + materials[i] = Material.LimeBanner; + for (int i = 1811; i <= 1826; i++) + materials[i] = Material.LimeBed; + for (int i = 21290; i <= 21305; i++) + materials[i] = Material.LimeCandle; + for (int i = 21478; i <= 21479; i++) + materials[i] = Material.LimeCandleCake; + materials[11068] = Material.LimeCarpet; + materials[13202] = Material.LimeConcrete; + materials[13218] = Material.LimeConcretePowder; + for (int i = 13153; i <= 13156; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 13067; i <= 13072; i++) + materials[i] = Material.LimeShulkerBox; + materials[6116] = Material.LimeStainedGlass; + for (int i = 9787; i <= 9818; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[9616] = Material.LimeTerracotta; + for (int i = 11370; i <= 11373; i++) + materials[i] = Material.LimeWallBanner; + materials[2095] = Material.LimeWool; + materials[19928] = Material.Lodestone; + for (int i = 18873; i <= 18876; i++) + materials[i] = Material.Loom; + for (int i = 11126; i <= 11141; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1763; i <= 1778; i++) + materials[i] = Material.MagentaBed; + for (int i = 21242; i <= 21257; i++) + materials[i] = Material.MagentaCandle; + for (int i = 21472; i <= 21473; i++) + materials[i] = Material.MagentaCandleCake; + materials[11065] = Material.MagentaCarpet; + materials[13199] = Material.MagentaConcrete; + materials[13215] = Material.MagentaConcretePowder; + for (int i = 13141; i <= 13144; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 13049; i <= 13054; i++) + materials[i] = Material.MagentaShulkerBox; + materials[6113] = Material.MagentaStainedGlass; + for (int i = 9691; i <= 9722; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[9613] = Material.MagentaTerracotta; + for (int i = 11358; i <= 11361; i++) + materials[i] = Material.MagentaWallBanner; + materials[2092] = Material.MagentaWool; + materials[13012] = Material.MagmaBlock; + for (int i = 9034; i <= 9057; i++) + materials[i] = Material.MangroveButton; + for (int i = 12675; i <= 12738; i++) + materials[i] = Material.MangroveDoor; + for (int i = 12163; i <= 12194; i++) + materials[i] = Material.MangroveFence; + for (int i = 11875; i <= 11906; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5566; i <= 5629; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + materials[26] = Material.MangrovePlanks; + for (int i = 5896; i <= 5897; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4610; i <= 4641; i++) + materials[i] = Material.MangroveSign; + for (int i = 11545; i <= 11550; i++) + materials[i] = Material.MangroveSlab; + for (int i = 10459; i <= 10538; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6639; i <= 6702; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5758; i <= 5765; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4910; i <= 4917; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 21526; i <= 21537; i++) + materials[i] = Material.MediumAmethystBud; + materials[7042] = Material.Melon; + for (int i = 7059; i <= 7066; i++) + materials[i] = Material.MelonStem; + materials[25312] = Material.MossBlock; + materials[25295] = Material.MossCarpet; + materials[2396] = Material.MossyCobblestone; + for (int i = 14575; i <= 14580; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 13751; i <= 13830; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 8473; i <= 8796; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 14563; i <= 14568; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 13591; i <= 13670; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 15601; i <= 15924; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6768] = Material.MossyStoneBricks; + for (int i = 2106; i <= 2117; i++) + materials[i] = Material.MovingPiston; + materials[25372] = Material.Mud; + for (int i = 11611; i <= 11616; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 7419; i <= 7498; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 16573; i <= 16896; i++) + materials[i] = Material.MudBrickWall; + materials[6772] = Material.MudBricks; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6907; i <= 6970; i++) + materials[i] = Material.MushroomStem; + for (int i = 7499; i <= 7500; i++) + materials[i] = Material.Mycelium; + for (int i = 7503; i <= 7534; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 11617; i <= 11622; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 7535; i <= 7614; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 16897; i <= 17220; i++) + materials[i] = Material.NetherBrickWall; + materials[7502] = Material.NetherBricks; + materials[135] = Material.NetherGoldOre; + for (int i = 6030; i <= 6031; i++) + materials[i] = Material.NetherPortal; + materials[9479] = Material.NetherQuartzOre; + materials[19064] = Material.NetherSprouts; + for (int i = 7615; i <= 7618; i++) + materials[i] = Material.NetherWart; + materials[13013] = Material.NetherWartBlock; + materials[19916] = Material.NetheriteBlock; + materials[6015] = Material.Netherrack; + for (int i = 581; i <= 1730; i++) + materials[i] = Material.NoteBlock; + for (int i = 8842; i <= 8865; i++) + materials[i] = Material.OakButton; + for (int i = 4674; i <= 4737; i++) + materials[i] = Material.OakDoor; + for (int i = 5983; i <= 6014; i++) + materials[i] = Material.OakFence; + for (int i = 7227; i <= 7258; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4926; i <= 4989; i++) + materials[i] = Material.OakHangingSign; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5880; i <= 5881; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 4354; i <= 4385; i++) + materials[i] = Material.OakSign; + for (int i = 11497; i <= 11502; i++) + materials[i] = Material.OakSlab; + for (int i = 2926; i <= 3005; i++) + materials[i] = Material.OakStairs; + for (int i = 6127; i <= 6190; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5694; i <= 5701; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4846; i <= 4853; i++) + materials[i] = Material.OakWallSign; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 13019; i <= 13030; i++) + materials[i] = Material.Observer; + materials[2397] = Material.Obsidian; + for (int i = 27032; i <= 27034; i++) + materials[i] = Material.OchreFroglight; + for (int i = 11110; i <= 11125; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1747; i <= 1762; i++) + materials[i] = Material.OrangeBed; + for (int i = 21226; i <= 21241; i++) + materials[i] = Material.OrangeCandle; + for (int i = 21470; i <= 21471; i++) + materials[i] = Material.OrangeCandleCake; + materials[11064] = Material.OrangeCarpet; + materials[13198] = Material.OrangeConcrete; + materials[13214] = Material.OrangeConcretePowder; + for (int i = 13137; i <= 13140; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 13043; i <= 13048; i++) + materials[i] = Material.OrangeShulkerBox; + materials[6112] = Material.OrangeStainedGlass; + for (int i = 9659; i <= 9690; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[9612] = Material.OrangeTerracotta; + materials[2125] = Material.OrangeTulip; + for (int i = 11354; i <= 11357; i++) + materials[i] = Material.OrangeWallBanner; + materials[2091] = Material.OrangeWool; + materials[2128] = Material.OxeyeDaisy; + materials[23417] = Material.OxidizedChiseledCopper; + materials[23410] = Material.OxidizedCopper; + for (int i = 25173; i <= 25176; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 24249; i <= 24312; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 25151; i <= 25152; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 24761; i <= 24824; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + materials[23413] = Material.OxidizedCutCopper; + for (int i = 23745; i <= 23750; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 23425; i <= 23504; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[11081] = Material.PackedIce; + materials[6771] = Material.PackedMud; + for (int i = 27316; i <= 27317; i++) + materials[i] = Material.PaleHangingMoss; + materials[27153] = Material.PaleMossBlock; + for (int i = 27154; i <= 27315; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 9010; i <= 9033; i++) + materials[i] = Material.PaleOakButton; + for (int i = 12611; i <= 12674; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 12131; i <= 12162; i++) + materials[i] = Material.PaleOakFence; + for (int i = 11843; i <= 11874; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 5374; i <= 5437; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + materials[25] = Material.PaleOakPlanks; + for (int i = 5894; i <= 5895; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 4578; i <= 4609; i++) + materials[i] = Material.PaleOakSign; + for (int i = 11539; i <= 11544; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 10379; i <= 10458; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 6575; i <= 6638; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 5750; i <= 5757; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 4902; i <= 4909; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 27038; i <= 27040; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 11088; i <= 11089; i++) + materials[i] = Material.Peony; + for (int i = 11587; i <= 11592; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 9322; i <= 9353; i++) + materials[i] = Material.PiglinHead; + for (int i = 9354; i <= 9361; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 11190; i <= 11205; i++) + materials[i] = Material.PinkBanner; + for (int i = 1827; i <= 1842; i++) + materials[i] = Material.PinkBed; + for (int i = 21306; i <= 21321; i++) + materials[i] = Material.PinkCandle; + for (int i = 21480; i <= 21481; i++) + materials[i] = Material.PinkCandleCake; + materials[11069] = Material.PinkCarpet; + materials[13203] = Material.PinkConcrete; + materials[13219] = Material.PinkConcretePowder; + for (int i = 13157; i <= 13160; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 25296; i <= 25311; i++) + materials[i] = Material.PinkPetals; + for (int i = 13073; i <= 13078; i++) + materials[i] = Material.PinkShulkerBox; + materials[6117] = Material.PinkStainedGlass; + for (int i = 9819; i <= 9850; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[9617] = Material.PinkTerracotta; + materials[2127] = Material.PinkTulip; + for (int i = 11374; i <= 11377; i++) + materials[i] = Material.PinkWallBanner; + materials[2096] = Material.PinkWool; + for (int i = 2054; i <= 2065; i++) + materials[i] = Material.Piston; + for (int i = 2066; i <= 2089; i++) + materials[i] = Material.PistonHead; + for (int i = 12966; i <= 12975; i++) + materials[i] = Material.PitcherCrop; + for (int i = 12976; i <= 12977; i++) + materials[i] = Material.PitcherPlant; + for (int i = 9202; i <= 9233; i++) + materials[i] = Material.PlayerHead; + for (int i = 9234; i <= 9241; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 25217; i <= 25236; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 14617; i <= 14622; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 14391; i <= 14470; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 6021; i <= 6023; i++) + materials[i] = Material.PolishedBasalt; + materials[20340] = Material.PolishedBlackstone; + for (int i = 20344; i <= 20349; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 20350; i <= 20429; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 20430; i <= 20753; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[20341] = Material.PolishedBlackstoneBricks; + for (int i = 20843; i <= 20866; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 20841; i <= 20842; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 20835; i <= 20840; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 20755; i <= 20834; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 20867; i <= 21190; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[25787] = Material.PolishedDeepslate; + for (int i = 25868; i <= 25873; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 25788; i <= 25867; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 25874; i <= 26197; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 14569; i <= 14574; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 13671; i <= 13750; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 14551; i <= 14556; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 13431; i <= 13510; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[21961] = Material.PolishedTuff; + for (int i = 21962; i <= 21967; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 21968; i <= 22047; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 22048; i <= 22371; i++) + materials[i] = Material.PolishedTuffWall; + materials[2120] = Material.Poppy; + for (int i = 8834; i <= 8841; i++) + materials[i] = Material.Potatoes; + materials[8803] = Material.PottedAcaciaSapling; + materials[8812] = Material.PottedAllium; + materials[27030] = Material.PottedAzaleaBush; + materials[8813] = Material.PottedAzureBluet; + materials[13426] = Material.PottedBamboo; + materials[8801] = Material.PottedBirchSapling; + materials[8811] = Material.PottedBlueOrchid; + materials[8823] = Material.PottedBrownMushroom; + materials[8825] = Material.PottedCactus; + materials[8804] = Material.PottedCherrySapling; + materials[8819] = Material.PottedCornflower; + materials[19924] = Material.PottedCrimsonFungus; + materials[19926] = Material.PottedCrimsonRoots; + materials[8809] = Material.PottedDandelion; + materials[8805] = Material.PottedDarkOakSapling; + materials[8824] = Material.PottedDeadBush; + materials[8808] = Material.PottedFern; + materials[27031] = Material.PottedFloweringAzaleaBush; + materials[8802] = Material.PottedJungleSapling; + materials[8820] = Material.PottedLilyOfTheValley; + materials[8807] = Material.PottedMangrovePropagule; + materials[8799] = Material.PottedOakSapling; + materials[8815] = Material.PottedOrangeTulip; + materials[8818] = Material.PottedOxeyeDaisy; + materials[8806] = Material.PottedPaleOakSapling; + materials[8817] = Material.PottedPinkTulip; + materials[8810] = Material.PottedPoppy; + materials[8822] = Material.PottedRedMushroom; + materials[8814] = Material.PottedRedTulip; + materials[8800] = Material.PottedSpruceSapling; + materials[8798] = Material.PottedTorchflower; + materials[19925] = Material.PottedWarpedFungus; + materials[19927] = Material.PottedWarpedRoots; + materials[8816] = Material.PottedWhiteTulip; + materials[8821] = Material.PottedWitherRose; + materials[22787] = Material.PowderSnow; + for (int i = 7633; i <= 7635; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1987; i <= 2010; i++) + materials[i] = Material.PoweredRail; + materials[10798] = Material.Prismarine; + for (int i = 11047; i <= 11052; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 10881; i <= 10960; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[10799] = Material.PrismarineBricks; + for (int i = 11041; i <= 11046; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 10801; i <= 10880; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 14953; i <= 15276; i++) + materials[i] = Material.PrismarineWall; + materials[7041] = Material.Pumpkin; + for (int i = 7051; i <= 7058; i++) + materials[i] = Material.PumpkinStem; + for (int i = 11254; i <= 11269; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1891; i <= 1906; i++) + materials[i] = Material.PurpleBed; + for (int i = 21370; i <= 21385; i++) + materials[i] = Material.PurpleCandle; + for (int i = 21488; i <= 21489; i++) + materials[i] = Material.PurpleCandleCake; + materials[11073] = Material.PurpleCarpet; + materials[13207] = Material.PurpleConcrete; + materials[13223] = Material.PurpleConcretePowder; + for (int i = 13173; i <= 13176; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 13097; i <= 13102; i++) + materials[i] = Material.PurpleShulkerBox; + materials[6121] = Material.PurpleStainedGlass; + for (int i = 9947; i <= 9978; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[9621] = Material.PurpleTerracotta; + for (int i = 11390; i <= 11393; i++) + materials[i] = Material.PurpleWallBanner; + materials[2100] = Material.PurpleWool; + materials[12879] = Material.PurpurBlock; + for (int i = 12880; i <= 12882; i++) + materials[i] = Material.PurpurPillar; + for (int i = 11641; i <= 11646; i++) + materials[i] = Material.PurpurSlab; + for (int i = 12883; i <= 12962; i++) + materials[i] = Material.PurpurStairs; + materials[9490] = Material.QuartzBlock; + materials[21193] = Material.QuartzBricks; + for (int i = 9492; i <= 9494; i++) + materials[i] = Material.QuartzPillar; + for (int i = 11623; i <= 11628; i++) + materials[i] = Material.QuartzSlab; + for (int i = 9495; i <= 9574; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4746; i <= 4765; i++) + materials[i] = Material.Rail; + materials[27028] = Material.RawCopperBlock; + materials[27029] = Material.RawGoldBlock; + materials[27027] = Material.RawIronBlock; + for (int i = 11318; i <= 11333; i++) + materials[i] = Material.RedBanner; + for (int i = 1955; i <= 1970; i++) + materials[i] = Material.RedBed; + for (int i = 21434; i <= 21449; i++) + materials[i] = Material.RedCandle; + for (int i = 21496; i <= 21497; i++) + materials[i] = Material.RedCandleCake; + materials[11077] = Material.RedCarpet; + materials[13211] = Material.RedConcrete; + materials[13227] = Material.RedConcretePowder; + for (int i = 13189; i <= 13192; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2133] = Material.RedMushroom; + for (int i = 6843; i <= 6906; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 14611; i <= 14616; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 14311; i <= 14390; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 17545; i <= 17868; i++) + materials[i] = Material.RedNetherBrickWall; + materials[13014] = Material.RedNetherBricks; + materials[123] = Material.RedSand; + materials[11414] = Material.RedSandstone; + for (int i = 11629; i <= 11634; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 11417; i <= 11496; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 15277; i <= 15600; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 13121; i <= 13126; i++) + materials[i] = Material.RedShulkerBox; + materials[6125] = Material.RedStainedGlass; + for (int i = 10075; i <= 10106; i++) + materials[i] = Material.RedStainedGlassPane; + materials[9625] = Material.RedTerracotta; + materials[2124] = Material.RedTulip; + for (int i = 11406; i <= 11409; i++) + materials[i] = Material.RedWallBanner; + materials[2104] = Material.RedWool; + materials[9478] = Material.RedstoneBlock; + for (int i = 7647; i <= 7648; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5900; i <= 5901; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5904; i <= 5905; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5906; i <= 5913; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 3030; i <= 4325; i++) + materials[i] = Material.RedstoneWire; + materials[27042] = Material.ReinforcedDeepslate; + for (int i = 6047; i <= 6110; i++) + materials[i] = Material.Repeater; + for (int i = 12984; i <= 12995; i++) + materials[i] = Material.RepeatingCommandBlock; + for (int i = 19919; i <= 19923; i++) + materials[i] = Material.RespawnAnchor; + materials[25371] = Material.RootedDirt; + for (int i = 11086; i <= 11087; i++) + materials[i] = Material.RoseBush; + materials[118] = Material.Sand; + materials[578] = Material.Sandstone; + for (int i = 11575; i <= 11580; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 7661; i <= 7740; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 17869; i <= 18192; i++) + materials[i] = Material.SandstoneWall; + for (int i = 18841; i <= 18872; i++) + materials[i] = Material.Scaffolding; + materials[23268] = Material.Sculk; + for (int i = 23397; i <= 23398; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 22788; i <= 22883; i++) + materials[i] = Material.SculkSensor; + for (int i = 23399; i <= 23406; i++) + materials[i] = Material.SculkShrieker; + for (int i = 23269; i <= 23396; i++) + materials[i] = Material.SculkVein; + materials[11059] = Material.SeaLantern; + for (int i = 13402; i <= 13409; i++) + materials[i] = Material.SeaPickle; + materials[2051] = Material.Seagrass; + materials[2048] = Material.ShortGrass; + materials[19079] = Material.Shroomlight; + for (int i = 13031; i <= 13036; i++) + materials[i] = Material.ShulkerBox; + for (int i = 9082; i <= 9113; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 9114; i <= 9121; i++) + materials[i] = Material.SkeletonWallSkull; + materials[10699] = Material.SlimeBlock; + for (int i = 21538; i <= 21549; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 25353; i <= 25368; i++) + materials[i] = Material.SmallDripleaf; + materials[18935] = Material.SmithingTable; + for (int i = 18889; i <= 18896; i++) + materials[i] = Material.Smoker; + materials[27026] = Material.SmoothBasalt; + materials[11649] = Material.SmoothQuartz; + for (int i = 14593; i <= 14598; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 14071; i <= 14150; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[11650] = Material.SmoothRedSandstone; + for (int i = 14557; i <= 14562; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 13511; i <= 13590; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[11648] = Material.SmoothSandstone; + for (int i = 14587; i <= 14592; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 13991; i <= 14070; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[11647] = Material.SmoothStone; + for (int i = 11569; i <= 11574; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13269; i <= 13271; i++) + materials[i] = Material.SnifferEgg; + for (int i = 5938; i <= 5945; i++) + materials[i] = Material.Snow; + materials[5947] = Material.SnowBlock; + for (int i = 19012; i <= 19043; i++) + materials[i] = Material.SoulCampfire; + materials[2915] = Material.SoulFire; + for (int i = 18976; i <= 18979; i++) + materials[i] = Material.SoulLantern; + materials[6016] = Material.SoulSand; + materials[6017] = Material.SoulSoil; + materials[6024] = Material.SoulTorch; + for (int i = 6025; i <= 6028; i++) + materials[i] = Material.SoulWallTorch; + materials[2916] = Material.Spawner; + materials[560] = Material.Sponge; + materials[25292] = Material.SporeBlossom; + for (int i = 8866; i <= 8889; i++) + materials[i] = Material.SpruceButton; + for (int i = 12227; i <= 12290; i++) + materials[i] = Material.SpruceDoor; + for (int i = 11939; i <= 11970; i++) + materials[i] = Material.SpruceFence; + for (int i = 11651; i <= 11682; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 4990; i <= 5053; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5882; i <= 5883; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4386; i <= 4417; i++) + materials[i] = Material.SpruceSign; + for (int i = 11503; i <= 11508; i++) + materials[i] = Material.SpruceSlab; + for (int i = 7896; i <= 7975; i++) + materials[i] = Material.SpruceStairs; + for (int i = 6191; i <= 6254; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5702; i <= 5709; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4854; i <= 4861; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 2035; i <= 2046; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 11605; i <= 11610; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 7339; i <= 7418; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 16249; i <= 16572; i++) + materials[i] = Material.StoneBrickWall; + materials[6767] = Material.StoneBricks; + for (int i = 5914; i <= 5937; i++) + materials[i] = Material.StoneButton; + for (int i = 5814; i <= 5815; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 11563; i <= 11568; i++) + materials[i] = Material.StoneSlab; + for (int i = 13911; i <= 13990; i++) + materials[i] = Material.StoneStairs; + for (int i = 18936; i <= 18939; i++) + materials[i] = Material.Stonecutter; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 19074; i <= 19076; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 19068; i <= 19070; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 19057; i <= 19059; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 19051; i <= 19053; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 19825; i <= 19828; i++) + materials[i] = Material.StructureBlock; + materials[13018] = Material.StructureVoid; + for (int i = 5965; i <= 5980; i++) + materials[i] = Material.SugarCane; + for (int i = 11082; i <= 11083; i++) + materials[i] = Material.Sunflower; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 19044; i <= 19047; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 11090; i <= 11091; i++) + materials[i] = Material.TallGrass; + for (int i = 2052; i <= 2053; i++) + materials[i] = Material.TallSeagrass; + for (int i = 19850; i <= 19865; i++) + materials[i] = Material.Target; + materials[11079] = Material.Terracotta; + materials[22786] = Material.TintedGlass; + for (int i = 2137; i <= 2138; i++) + materials[i] = Material.Tnt; + materials[2398] = Material.Torch; + materials[2119] = Material.Torchflower; + for (int i = 12964; i <= 12965; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 9374; i <= 9397; i++) + materials[i] = Material.TrappedChest; + for (int i = 27107; i <= 27118; i++) + materials[i] = Material.TrialSpawner; + for (int i = 7767; i <= 7894; i++) + materials[i] = Material.Tripwire; + for (int i = 7751; i <= 7766; i++) + materials[i] = Material.TripwireHook; + for (int i = 13292; i <= 13293; i++) + materials[i] = Material.TubeCoral; + materials[13277] = Material.TubeCoralBlock; + for (int i = 13312; i <= 13313; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 13362; i <= 13369; i++) + materials[i] = Material.TubeCoralWallFan; + materials[21550] = Material.Tuff; + for (int i = 22374; i <= 22379; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 22380; i <= 22459; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 22460; i <= 22783; i++) + materials[i] = Material.TuffBrickWall; + materials[22373] = Material.TuffBricks; + for (int i = 21551; i <= 21556; i++) + materials[i] = Material.TuffSlab; + for (int i = 21557; i <= 21636; i++) + materials[i] = Material.TuffStairs; + for (int i = 21637; i <= 21960; i++) + materials[i] = Material.TuffWall; + for (int i = 13257; i <= 13268; i++) + materials[i] = Material.TurtleEgg; + for (int i = 19107; i <= 19132; i++) + materials[i] = Material.TwistingVines; + materials[19133] = Material.TwistingVinesPlant; + for (int i = 27119; i <= 27150; i++) + materials[i] = Material.Vault; + for (int i = 27035; i <= 27037; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 7067; i <= 7098; i++) + materials[i] = Material.Vine; + materials[13427] = Material.VoidAir; + for (int i = 2399; i <= 2402; i++) + materials[i] = Material.WallTorch; + for (int i = 19593; i <= 19616; i++) + materials[i] = Material.WarpedButton; + for (int i = 19681; i <= 19744; i++) + materials[i] = Material.WarpedDoor; + for (int i = 19185; i <= 19216; i++) + materials[i] = Material.WarpedFence; + for (int i = 19377; i <= 19408; i++) + materials[i] = Material.WarpedFenceGate; + materials[19061] = Material.WarpedFungus; + for (int i = 5502; i <= 5565; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 19054; i <= 19056; i++) + materials[i] = Material.WarpedHyphae; + materials[19060] = Material.WarpedNylium; + materials[19136] = Material.WarpedPlanks; + for (int i = 19151; i <= 19152; i++) + materials[i] = Material.WarpedPressurePlate; + materials[19063] = Material.WarpedRoots; + for (int i = 19777; i <= 19808; i++) + materials[i] = Material.WarpedSign; + for (int i = 19143; i <= 19148; i++) + materials[i] = Material.WarpedSlab; + for (int i = 19489; i <= 19568; i++) + materials[i] = Material.WarpedStairs; + for (int i = 19048; i <= 19050; i++) + materials[i] = Material.WarpedStem; + for (int i = 19281; i <= 19344; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5774; i <= 5781; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 19817; i <= 19824; i++) + materials[i] = Material.WarpedWallSign; + materials[19062] = Material.WarpedWartBlock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 7629; i <= 7631; i++) + materials[i] = Material.WaterCauldron; + materials[23424] = Material.WaxedChiseledCopper; + materials[23769] = Material.WaxedCopperBlock; + for (int i = 25177; i <= 25180; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 24377; i <= 24440; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 25153; i <= 25154; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 24889; i <= 24952; i++) + materials[i] = Material.WaxedCopperTrapdoor; + materials[23776] = Material.WaxedCutCopper; + for (int i = 24115; i <= 24120; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 24017; i <= 24096; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[23423] = Material.WaxedExposedChiseledCopper; + materials[23771] = Material.WaxedExposedCopper; + for (int i = 25181; i <= 25184; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 24441; i <= 24504; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 25155; i <= 25156; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 24953; i <= 25016; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + materials[23775] = Material.WaxedExposedCutCopper; + for (int i = 24109; i <= 24114; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 23937; i <= 24016; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[23421] = Material.WaxedOxidizedChiseledCopper; + materials[23772] = Material.WaxedOxidizedCopper; + for (int i = 25189; i <= 25192; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 24505; i <= 24568; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 25159; i <= 25160; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 25017; i <= 25080; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + materials[23773] = Material.WaxedOxidizedCutCopper; + for (int i = 24097; i <= 24102; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 23777; i <= 23856; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[23422] = Material.WaxedWeatheredChiseledCopper; + materials[23770] = Material.WaxedWeatheredCopper; + for (int i = 25185; i <= 25188; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 24569; i <= 24632; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 25157; i <= 25158; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 25081; i <= 25144; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + materials[23774] = Material.WaxedWeatheredCutCopper; + for (int i = 24103; i <= 24108; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 23857; i <= 23936; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[23418] = Material.WeatheredChiseledCopper; + materials[23409] = Material.WeatheredCopper; + for (int i = 25169; i <= 25172; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 24313; i <= 24376; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 25149; i <= 25150; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 24825; i <= 24888; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + materials[23414] = Material.WeatheredCutCopper; + for (int i = 23751; i <= 23756; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 23505; i <= 23584; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 19080; i <= 19105; i++) + materials[i] = Material.WeepingVines; + materials[19106] = Material.WeepingVinesPlant; + materials[561] = Material.WetSponge; + for (int i = 4330; i <= 4337; i++) + materials[i] = Material.Wheat; + for (int i = 11094; i <= 11109; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1731; i <= 1746; i++) + materials[i] = Material.WhiteBed; + for (int i = 21210; i <= 21225; i++) + materials[i] = Material.WhiteCandle; + for (int i = 21468; i <= 21469; i++) + materials[i] = Material.WhiteCandleCake; + materials[11063] = Material.WhiteCarpet; + materials[13197] = Material.WhiteConcrete; + materials[13213] = Material.WhiteConcretePowder; + for (int i = 13133; i <= 13136; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 13037; i <= 13042; i++) + materials[i] = Material.WhiteShulkerBox; + materials[6111] = Material.WhiteStainedGlass; + for (int i = 9627; i <= 9658; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[9611] = Material.WhiteTerracotta; + materials[2126] = Material.WhiteTulip; + for (int i = 11350; i <= 11353; i++) + materials[i] = Material.WhiteWallBanner; + materials[2090] = Material.WhiteWool; + materials[2130] = Material.WitherRose; + for (int i = 9122; i <= 9153; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 9154; i <= 9161; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 11158; i <= 11173; i++) + materials[i] = Material.YellowBanner; + for (int i = 1795; i <= 1810; i++) + materials[i] = Material.YellowBed; + for (int i = 21274; i <= 21289; i++) + materials[i] = Material.YellowCandle; + for (int i = 21476; i <= 21477; i++) + materials[i] = Material.YellowCandleCake; + materials[11067] = Material.YellowCarpet; + materials[13201] = Material.YellowConcrete; + materials[13217] = Material.YellowConcretePowder; + for (int i = 13149; i <= 13152; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 13061; i <= 13066; i++) + materials[i] = Material.YellowShulkerBox; + materials[6115] = Material.YellowStainedGlass; + for (int i = 9755; i <= 9786; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[9615] = Material.YellowTerracotta; + for (int i = 11366; i <= 11369; i++) + materials[i] = Material.YellowWallBanner; + materials[2094] = Material.YellowWool; + for (int i = 9162; i <= 9193; i++) + materials[i] = Material.ZombieHead; + for (int i = 9194; i <= 9201; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1214.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1214.cs new file mode 100644 index 00000000..fbffcc7f --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1214.cs @@ -0,0 +1,1826 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1214 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1214() + { + for (int i = 9482; i <= 9505; i++) + materials[i] = Material.AcaciaButton; + for (int i = 12963; i <= 13026; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 12579; i <= 12610; i++) + materials[i] = Material.AcaciaFence; + for (int i = 12291; i <= 12322; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 5121; i <= 5184; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5891; i <= 5892; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4453; i <= 4484; i++) + materials[i] = Material.AcaciaSign; + for (int i = 12065; i <= 12070; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 10683; i <= 10762; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6386; i <= 6449; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5721; i <= 5728; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4873; i <= 4880; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 10119; i <= 10142; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2122] = Material.Allium; + materials[22044] = Material.AmethystBlock; + for (int i = 22046; i <= 22057; i++) + materials[i] = Material.AmethystCluster; + materials[20461] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 15149; i <= 15154; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 14775; i <= 14854; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 17765; i <= 18088; i++) + materials[i] = Material.AndesiteWall; + for (int i = 9906; i <= 9909; i++) + materials[i] = Material.Anvil; + for (int i = 7050; i <= 7053; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 7046; i <= 7049; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[25837] = Material.Azalea; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + materials[2123] = Material.AzureBluet; + for (int i = 13958; i <= 13969; i++) + materials[i] = Material.Bamboo; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 9602; i <= 9625; i++) + materials[i] = Material.BambooButton; + for (int i = 13283; i <= 13346; i++) + materials[i] = Material.BambooDoor; + for (int i = 12739; i <= 12770; i++) + materials[i] = Material.BambooFence; + for (int i = 12451; i <= 12482; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5633; i <= 5696; i++) + materials[i] = Material.BambooHangingSign; + materials[28] = Material.BambooMosaic; + for (int i = 12101; i <= 12106; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 11163; i <= 11242; i++) + materials[i] = Material.BambooMosaicStairs; + materials[27] = Material.BambooPlanks; + for (int i = 5901; i <= 5902; i++) + materials[i] = Material.BambooPressurePlate; + materials[13957] = Material.BambooSapling; + for (int i = 4645; i <= 4676; i++) + materials[i] = Material.BambooSign; + for (int i = 12095; i <= 12100; i++) + materials[i] = Material.BambooSlab; + for (int i = 11083; i <= 11162; i++) + materials[i] = Material.BambooStairs; + for (int i = 6706; i <= 6769; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5785; i <= 5792; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4921; i <= 4928; i++) + materials[i] = Material.BambooWallSign; + for (int i = 19421; i <= 19432; i++) + materials[i] = Material.Barrel; + for (int i = 11244; i <= 11245; i++) + materials[i] = Material.Barrier; + for (int i = 6021; i <= 6023; i++) + materials[i] = Material.Basalt; + materials[8692] = Material.Beacon; + materials[85] = Material.Bedrock; + for (int i = 20410; i <= 20433; i++) + materials[i] = Material.BeeNest; + for (int i = 20434; i <= 20457; i++) + materials[i] = Material.Beehive; + for (int i = 13522; i <= 13525; i++) + materials[i] = Material.Beetroots; + for (int i = 19484; i <= 19515; i++) + materials[i] = Material.Bell; + for (int i = 25857; i <= 25888; i++) + materials[i] = Material.BigDripleaf; + for (int i = 25889; i <= 25896; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 9434; i <= 9457; i++) + materials[i] = Material.BirchButton; + for (int i = 12835; i <= 12898; i++) + materials[i] = Material.BirchDoor; + for (int i = 12515; i <= 12546; i++) + materials[i] = Material.BirchFence; + for (int i = 12227; i <= 12258; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 5057; i <= 5120; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5887; i <= 5888; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 4421; i <= 4452; i++) + materials[i] = Material.BirchSign; + for (int i = 12053; i <= 12058; i++) + materials[i] = Material.BirchSlab; + for (int i = 8520; i <= 8599; i++) + materials[i] = Material.BirchStairs; + for (int i = 6258; i <= 6321; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5713; i <= 5720; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4865; i <= 4872; i++) + materials[i] = Material.BirchWallSign; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 11878; i <= 11893; i++) + materials[i] = Material.BlackBanner; + for (int i = 1971; i <= 1986; i++) + materials[i] = Material.BlackBed; + for (int i = 21994; i <= 22009; i++) + materials[i] = Material.BlackCandle; + for (int i = 22042; i <= 22043; i++) + materials[i] = Material.BlackCandleCake; + materials[11622] = Material.BlackCarpet; + materials[13756] = Material.BlackConcrete; + materials[13772] = Material.BlackConcretePowder; + for (int i = 13737; i <= 13740; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 13671; i <= 13676; i++) + materials[i] = Material.BlackShulkerBox; + materials[6129] = Material.BlackStainedGlass; + for (int i = 10651; i <= 10682; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[10170] = Material.BlackTerracotta; + for (int i = 11954; i <= 11957; i++) + materials[i] = Material.BlackWallBanner; + materials[2105] = Material.BlackWool; + materials[20473] = Material.Blackstone; + for (int i = 20878; i <= 20883; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 20474; i <= 20553; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 20554; i <= 20877; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 19441; i <= 19448; i++) + materials[i] = Material.BlastFurnace; + for (int i = 11814; i <= 11829; i++) + materials[i] = Material.BlueBanner; + for (int i = 1907; i <= 1922; i++) + materials[i] = Material.BlueBed; + for (int i = 21930; i <= 21945; i++) + materials[i] = Material.BlueCandle; + for (int i = 22034; i <= 22035; i++) + materials[i] = Material.BlueCandleCake; + materials[11618] = Material.BlueCarpet; + materials[13752] = Material.BlueConcrete; + materials[13768] = Material.BlueConcretePowder; + for (int i = 13721; i <= 13724; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[13954] = Material.BlueIce; + materials[2121] = Material.BlueOrchid; + for (int i = 13647; i <= 13652; i++) + materials[i] = Material.BlueShulkerBox; + materials[6125] = Material.BlueStainedGlass; + for (int i = 10523; i <= 10554; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[10166] = Material.BlueTerracotta; + for (int i = 11938; i <= 11941; i++) + materials[i] = Material.BlueWallBanner; + materials[2101] = Material.BlueWool; + for (int i = 13559; i <= 13561; i++) + materials[i] = Material.BoneBlock; + materials[2139] = Material.Bookshelf; + for (int i = 13838; i <= 13839; i++) + materials[i] = Material.BrainCoral; + materials[13822] = Material.BrainCoralBlock; + for (int i = 13858; i <= 13859; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 13914; i <= 13921; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 8164; i <= 8171; i++) + materials[i] = Material.BrewingStand; + for (int i = 12143; i <= 12148; i++) + materials[i] = Material.BrickSlab; + for (int i = 7390; i <= 7469; i++) + materials[i] = Material.BrickStairs; + for (int i = 15173; i <= 15496; i++) + materials[i] = Material.BrickWall; + materials[2136] = Material.Bricks; + for (int i = 11830; i <= 11845; i++) + materials[i] = Material.BrownBanner; + for (int i = 1923; i <= 1938; i++) + materials[i] = Material.BrownBed; + for (int i = 21946; i <= 21961; i++) + materials[i] = Material.BrownCandle; + for (int i = 22036; i <= 22037; i++) + materials[i] = Material.BrownCandleCake; + materials[11619] = Material.BrownCarpet; + materials[13753] = Material.BrownConcrete; + materials[13769] = Material.BrownConcretePowder; + for (int i = 13725; i <= 13728; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2132] = Material.BrownMushroom; + for (int i = 6782; i <= 6845; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 13653; i <= 13658; i++) + materials[i] = Material.BrownShulkerBox; + materials[6126] = Material.BrownStainedGlass; + for (int i = 10555; i <= 10586; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[10167] = Material.BrownTerracotta; + for (int i = 11942; i <= 11945; i++) + materials[i] = Material.BrownWallBanner; + materials[2102] = Material.BrownWool; + for (int i = 13973; i <= 13974; i++) + materials[i] = Material.BubbleColumn; + for (int i = 13840; i <= 13841; i++) + materials[i] = Material.BubbleCoral; + materials[13823] = Material.BubbleCoralBlock; + for (int i = 13860; i <= 13861; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 13922; i <= 13929; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[22045] = Material.BuddingAmethyst; + for (int i = 5951; i <= 5966; i++) + materials[i] = Material.Cactus; + for (int i = 6043; i <= 6049; i++) + materials[i] = Material.Cake; + materials[23329] = Material.Calcite; + for (int i = 23428; i <= 23811; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 19524; i <= 19555; i++) + materials[i] = Material.Campfire; + for (int i = 21738; i <= 21753; i++) + materials[i] = Material.Candle; + for (int i = 22010; i <= 22011; i++) + materials[i] = Material.CandleCake; + for (int i = 9370; i <= 9377; i++) + materials[i] = Material.Carrots; + materials[19449] = Material.CartographyTable; + for (int i = 6035; i <= 6038; i++) + materials[i] = Material.CarvedPumpkin; + materials[8172] = Material.Cauldron; + materials[13972] = Material.CaveAir; + for (int i = 25782; i <= 25833; i++) + materials[i] = Material.CaveVines; + for (int i = 25834; i <= 25835; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 7006; i <= 7011; i++) + materials[i] = Material.Chain; + for (int i = 13540; i <= 13551; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 9506; i <= 9529; i++) + materials[i] = Material.CherryButton; + for (int i = 13027; i <= 13090; i++) + materials[i] = Material.CherryDoor; + for (int i = 12611; i <= 12642; i++) + materials[i] = Material.CherryFence; + for (int i = 12323; i <= 12354; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 5185; i <= 5248; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + materials[20] = Material.CherryPlanks; + for (int i = 5893; i <= 5894; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 4485; i <= 4516; i++) + materials[i] = Material.CherrySign; + for (int i = 12071; i <= 12076; i++) + materials[i] = Material.CherrySlab; + for (int i = 10763; i <= 10842; i++) + materials[i] = Material.CherryStairs; + for (int i = 6450; i <= 6513; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 5729; i <= 5736; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 4881; i <= 4888; i++) + materials[i] = Material.CherryWallSign; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 3009; i <= 3032; i++) + materials[i] = Material.Chest; + for (int i = 9910; i <= 9913; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2140; i <= 2395; i++) + materials[i] = Material.ChiseledBookshelf; + materials[23964] = Material.ChiseledCopper; + materials[27564] = Material.ChiseledDeepslate; + materials[21735] = Material.ChiseledNetherBricks; + materials[20887] = Material.ChiseledPolishedBlackstone; + materials[10035] = Material.ChiseledQuartzBlock; + materials[11959] = Material.ChiseledRedSandstone; + materials[8045] = Material.ChiseledResinBricks; + materials[579] = Material.ChiseledSandstone; + materials[6773] = Material.ChiseledStoneBricks; + materials[22916] = Material.ChiseledTuff; + materials[23328] = Material.ChiseledTuffBricks; + for (int i = 13417; i <= 13422; i++) + materials[i] = Material.ChorusFlower; + for (int i = 13353; i <= 13416; i++) + materials[i] = Material.ChorusPlant; + materials[5967] = Material.Clay; + materials[27863] = Material.ClosedEyeblossom; + materials[11624] = Material.CoalBlock; + materials[133] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[25920] = Material.CobbledDeepslate; + for (int i = 26001; i <= 26006; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 25921; i <= 26000; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 26007; i <= 26330; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 12137; i <= 12142; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4769; i <= 4848; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 8693; i <= 9016; i++) + materials[i] = Material.CobblestoneWall; + materials[2047] = Material.Cobweb; + for (int i = 8193; i <= 8204; i++) + materials[i] = Material.Cocoa; + for (int i = 8680; i <= 8691; i++) + materials[i] = Material.CommandBlock; + for (int i = 9974; i <= 9989; i++) + materials[i] = Material.Comparator; + for (int i = 20385; i <= 20393; i++) + materials[i] = Material.Composter; + for (int i = 13955; i <= 13956; i++) + materials[i] = Material.Conduit; + materials[23951] = Material.CopperBlock; + for (int i = 25705; i <= 25708; i++) + materials[i] = Material.CopperBulb; + for (int i = 24665; i <= 24728; i++) + materials[i] = Material.CopperDoor; + for (int i = 25689; i <= 25690; i++) + materials[i] = Material.CopperGrate; + materials[23955] = Material.CopperOre; + for (int i = 25177; i <= 25240; i++) + materials[i] = Material.CopperTrapdoor; + materials[2129] = Material.Cornflower; + materials[27565] = Material.CrackedDeepslateBricks; + materials[27566] = Material.CrackedDeepslateTiles; + materials[21736] = Material.CrackedNetherBricks; + materials[20886] = Material.CrackedPolishedBlackstoneBricks; + materials[6772] = Material.CrackedStoneBricks; + for (int i = 27603; i <= 27650; i++) + materials[i] = Material.Crafter; + materials[4332] = Material.CraftingTable; + for (int i = 2917; i <= 2928; i++) + materials[i] = Material.CreakingHeart; + for (int i = 9786; i <= 9817; i++) + materials[i] = Material.CreeperHead; + for (int i = 9818; i <= 9825; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 20113; i <= 20136; i++) + materials[i] = Material.CrimsonButton; + for (int i = 20161; i <= 20224; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 19697; i <= 19728; i++) + materials[i] = Material.CrimsonFence; + for (int i = 19889; i <= 19920; i++) + materials[i] = Material.CrimsonFenceGate; + materials[19622] = Material.CrimsonFungus; + for (int i = 5441; i <= 5504; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 19615; i <= 19617; i++) + materials[i] = Material.CrimsonHyphae; + materials[19621] = Material.CrimsonNylium; + materials[19679] = Material.CrimsonPlanks; + for (int i = 19693; i <= 19694; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[19678] = Material.CrimsonRoots; + for (int i = 20289; i <= 20320; i++) + materials[i] = Material.CrimsonSign; + for (int i = 19681; i <= 19686; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 19953; i <= 20032; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 19609; i <= 19611; i++) + materials[i] = Material.CrimsonStem; + for (int i = 19761; i <= 19824; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5769; i <= 5776; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 20353; i <= 20360; i++) + materials[i] = Material.CrimsonWallSign; + materials[20462] = Material.CryingObsidian; + materials[23960] = Material.CutCopper; + for (int i = 24307; i <= 24312; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 24209; i <= 24288; i++) + materials[i] = Material.CutCopperStairs; + materials[11960] = Material.CutRedSandstone; + for (int i = 12179; i <= 12184; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[580] = Material.CutSandstone; + for (int i = 12125; i <= 12130; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 11782; i <= 11797; i++) + materials[i] = Material.CyanBanner; + for (int i = 1875; i <= 1890; i++) + materials[i] = Material.CyanBed; + for (int i = 21898; i <= 21913; i++) + materials[i] = Material.CyanCandle; + for (int i = 22030; i <= 22031; i++) + materials[i] = Material.CyanCandleCake; + materials[11616] = Material.CyanCarpet; + materials[13750] = Material.CyanConcrete; + materials[13766] = Material.CyanConcretePowder; + for (int i = 13713; i <= 13716; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 13635; i <= 13640; i++) + materials[i] = Material.CyanShulkerBox; + materials[6123] = Material.CyanStainedGlass; + for (int i = 10459; i <= 10490; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[10164] = Material.CyanTerracotta; + for (int i = 11930; i <= 11933; i++) + materials[i] = Material.CyanWallBanner; + materials[2099] = Material.CyanWool; + for (int i = 9914; i <= 9917; i++) + materials[i] = Material.DamagedAnvil; + materials[2118] = Material.Dandelion; + for (int i = 9530; i <= 9553; i++) + materials[i] = Material.DarkOakButton; + for (int i = 13091; i <= 13154; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 12643; i <= 12674; i++) + materials[i] = Material.DarkOakFence; + for (int i = 12355; i <= 12386; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5313; i <= 5376; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + materials[21] = Material.DarkOakPlanks; + for (int i = 5895; i <= 5896; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4549; i <= 4580; i++) + materials[i] = Material.DarkOakSign; + for (int i = 12077; i <= 12082; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 10843; i <= 10922; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6514; i <= 6577; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5745; i <= 5752; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4897; i <= 4904; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + materials[11344] = Material.DarkPrismarine; + for (int i = 11597; i <= 11602; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 11505; i <= 11584; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 9990; i <= 10021; i++) + materials[i] = Material.DaylightDetector; + for (int i = 13828; i <= 13829; i++) + materials[i] = Material.DeadBrainCoral; + materials[13817] = Material.DeadBrainCoralBlock; + for (int i = 13848; i <= 13849; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 13874; i <= 13881; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 13830; i <= 13831; i++) + materials[i] = Material.DeadBubbleCoral; + materials[13818] = Material.DeadBubbleCoralBlock; + for (int i = 13850; i <= 13851; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 13882; i <= 13889; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[2050] = Material.DeadBush; + for (int i = 13832; i <= 13833; i++) + materials[i] = Material.DeadFireCoral; + materials[13819] = Material.DeadFireCoralBlock; + for (int i = 13852; i <= 13853; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 13890; i <= 13897; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 13834; i <= 13835; i++) + materials[i] = Material.DeadHornCoral; + materials[13820] = Material.DeadHornCoralBlock; + for (int i = 13854; i <= 13855; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 13898; i <= 13905; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 13826; i <= 13827; i++) + materials[i] = Material.DeadTubeCoral; + materials[13816] = Material.DeadTubeCoralBlock; + for (int i = 13846; i <= 13847; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 13866; i <= 13873; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 27587; i <= 27602; i++) + materials[i] = Material.DecoratedPot; + for (int i = 25917; i <= 25919; i++) + materials[i] = Material.Deepslate; + for (int i = 27234; i <= 27239; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 27154; i <= 27233; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 27240; i <= 27563; i++) + materials[i] = Material.DeepslateBrickWall; + materials[27153] = Material.DeepslateBricks; + materials[134] = Material.DeepslateCoalOre; + materials[23956] = Material.DeepslateCopperOre; + materials[4330] = Material.DeepslateDiamondOre; + materials[8286] = Material.DeepslateEmeraldOre; + materials[130] = Material.DeepslateGoldOre; + materials[132] = Material.DeepslateIronOre; + materials[564] = Material.DeepslateLapisOre; + for (int i = 5905; i <= 5906; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 26823; i <= 26828; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 26743; i <= 26822; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 26829; i <= 27152; i++) + materials[i] = Material.DeepslateTileWall; + materials[26742] = Material.DeepslateTiles; + for (int i = 2011; i <= 2034; i++) + materials[i] = Material.DetectorRail; + materials[4331] = Material.DiamondBlock; + materials[4329] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 15167; i <= 15172; i++) + materials[i] = Material.DioriteSlab; + for (int i = 15015; i <= 15094; i++) + materials[i] = Material.DioriteStairs; + for (int i = 19061; i <= 19384; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[13526] = Material.DirtPath; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + materials[8190] = Material.DragonEgg; + for (int i = 9826; i <= 9857; i++) + materials[i] = Material.DragonHead; + for (int i = 9858; i <= 9865; i++) + materials[i] = Material.DragonWallHead; + materials[13800] = Material.DriedKelpBlock; + materials[25781] = Material.DripstoneBlock; + for (int i = 10143; i <= 10154; i++) + materials[i] = Material.Dropper; + materials[8439] = Material.EmeraldBlock; + materials[8285] = Material.EmeraldOre; + materials[8163] = Material.EnchantingTable; + materials[13527] = Material.EndGateway; + materials[8180] = Material.EndPortal; + for (int i = 8181; i <= 8188; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 13347; i <= 13352; i++) + materials[i] = Material.EndRod; + materials[8189] = Material.EndStone; + for (int i = 15125; i <= 15130; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 14375; i <= 14454; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 18737; i <= 19060; i++) + materials[i] = Material.EndStoneBrickWall; + materials[13507] = Material.EndStoneBricks; + for (int i = 8287; i <= 8294; i++) + materials[i] = Material.EnderChest; + materials[23963] = Material.ExposedChiseledCopper; + materials[23952] = Material.ExposedCopper; + for (int i = 25709; i <= 25712; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 24729; i <= 24792; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 25691; i <= 25692; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 25241; i <= 25304; i++) + materials[i] = Material.ExposedCopperTrapdoor; + materials[23959] = Material.ExposedCutCopper; + for (int i = 24301; i <= 24306; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 24129; i <= 24208; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4341; i <= 4348; i++) + materials[i] = Material.Farmland; + materials[2049] = Material.Fern; + for (int i = 2403; i <= 2914; i++) + materials[i] = Material.Fire; + for (int i = 13842; i <= 13843; i++) + materials[i] = Material.FireCoral; + materials[13824] = Material.FireCoralBlock; + for (int i = 13862; i <= 13863; i++) + materials[i] = Material.FireCoralFan; + for (int i = 13930; i <= 13937; i++) + materials[i] = Material.FireCoralWallFan; + materials[19450] = Material.FletchingTable; + materials[9341] = Material.FlowerPot; + materials[25838] = Material.FloweringAzalea; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[27585] = Material.Frogspawn; + for (int i = 13552; i <= 13555; i++) + materials[i] = Material.FrostedIce; + for (int i = 4349; i <= 4356; i++) + materials[i] = Material.Furnace; + materials[21298] = Material.GildedBlackstone; + materials[562] = Material.Glass; + for (int i = 7012; i <= 7043; i++) + materials[i] = Material.GlassPane; + for (int i = 7102; i <= 7229; i++) + materials[i] = Material.GlowLichen; + materials[6032] = Material.Glowstone; + materials[2134] = Material.GoldBlock; + materials[129] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 15143; i <= 15148; i++) + materials[i] = Material.GraniteSlab; + for (int i = 14695; i <= 14774; i++) + materials[i] = Material.GraniteStairs; + for (int i = 16469; i <= 16792; i++) + materials[i] = Material.GraniteWall; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[124] = Material.Gravel; + for (int i = 11750; i <= 11765; i++) + materials[i] = Material.GrayBanner; + for (int i = 1843; i <= 1858; i++) + materials[i] = Material.GrayBed; + for (int i = 21866; i <= 21881; i++) + materials[i] = Material.GrayCandle; + for (int i = 22026; i <= 22027; i++) + materials[i] = Material.GrayCandleCake; + materials[11614] = Material.GrayCarpet; + materials[13748] = Material.GrayConcrete; + materials[13764] = Material.GrayConcretePowder; + for (int i = 13705; i <= 13708; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 13623; i <= 13628; i++) + materials[i] = Material.GrayShulkerBox; + materials[6121] = Material.GrayStainedGlass; + for (int i = 10395; i <= 10426; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[10162] = Material.GrayTerracotta; + for (int i = 11922; i <= 11925; i++) + materials[i] = Material.GrayWallBanner; + materials[2097] = Material.GrayWool; + for (int i = 11846; i <= 11861; i++) + materials[i] = Material.GreenBanner; + for (int i = 1939; i <= 1954; i++) + materials[i] = Material.GreenBed; + for (int i = 21962; i <= 21977; i++) + materials[i] = Material.GreenCandle; + for (int i = 22038; i <= 22039; i++) + materials[i] = Material.GreenCandleCake; + materials[11620] = Material.GreenCarpet; + materials[13754] = Material.GreenConcrete; + materials[13770] = Material.GreenConcretePowder; + for (int i = 13729; i <= 13732; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 13659; i <= 13664; i++) + materials[i] = Material.GreenShulkerBox; + materials[6127] = Material.GreenStainedGlass; + for (int i = 10587; i <= 10618; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[10168] = Material.GreenTerracotta; + for (int i = 11946; i <= 11949; i++) + materials[i] = Material.GreenWallBanner; + materials[2103] = Material.GreenWool; + for (int i = 19451; i <= 19462; i++) + materials[i] = Material.Grindstone; + for (int i = 25913; i <= 25914; i++) + materials[i] = Material.HangingRoots; + for (int i = 11604; i <= 11606; i++) + materials[i] = Material.HayBlock; + for (int i = 27695; i <= 27696; i++) + materials[i] = Material.HeavyCore; + for (int i = 9958; i <= 9973; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[20458] = Material.HoneyBlock; + materials[20459] = Material.HoneycombBlock; + for (int i = 10024; i <= 10033; i++) + materials[i] = Material.Hopper; + for (int i = 13844; i <= 13845; i++) + materials[i] = Material.HornCoral; + materials[13825] = Material.HornCoralBlock; + for (int i = 13864; i <= 13865; i++) + materials[i] = Material.HornCoralFan; + for (int i = 13938; i <= 13945; i++) + materials[i] = Material.HornCoralWallFan; + materials[5949] = Material.Ice; + materials[6781] = Material.InfestedChiseledStoneBricks; + materials[6777] = Material.InfestedCobblestone; + materials[6780] = Material.InfestedCrackedStoneBricks; + for (int i = 27567; i <= 27569; i++) + materials[i] = Material.InfestedDeepslate; + materials[6779] = Material.InfestedMossyStoneBricks; + materials[6776] = Material.InfestedStone; + materials[6778] = Material.InfestedStoneBricks; + for (int i = 6974; i <= 7005; i++) + materials[i] = Material.IronBars; + materials[2135] = Material.IronBlock; + for (int i = 5819; i <= 5882; i++) + materials[i] = Material.IronDoor; + materials[131] = Material.IronOre; + for (int i = 11278; i <= 11341; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 6039; i <= 6042; i++) + materials[i] = Material.JackOLantern; + for (int i = 20373; i <= 20384; i++) + materials[i] = Material.Jigsaw; + for (int i = 5984; i <= 5985; i++) + materials[i] = Material.Jukebox; + for (int i = 9458; i <= 9481; i++) + materials[i] = Material.JungleButton; + for (int i = 12899; i <= 12962; i++) + materials[i] = Material.JungleDoor; + for (int i = 12547; i <= 12578; i++) + materials[i] = Material.JungleFence; + for (int i = 12259; i <= 12290; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 5249; i <= 5312; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5889; i <= 5890; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 4517; i <= 4548; i++) + materials[i] = Material.JungleSign; + for (int i = 12059; i <= 12064; i++) + materials[i] = Material.JungleSlab; + for (int i = 8600; i <= 8679; i++) + materials[i] = Material.JungleStairs; + for (int i = 6322; i <= 6385; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5737; i <= 5744; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4889; i <= 4896; i++) + materials[i] = Material.JungleWallSign; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 13773; i <= 13798; i++) + materials[i] = Material.Kelp; + materials[13799] = Material.KelpPlant; + for (int i = 4741; i <= 4748; i++) + materials[i] = Material.Ladder; + for (int i = 19516; i <= 19519; i++) + materials[i] = Material.Lantern; + materials[565] = Material.LapisBlock; + materials[563] = Material.LapisOre; + for (int i = 22058; i <= 22069; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 11636; i <= 11637; i++) + materials[i] = Material.LargeFern; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + materials[8176] = Material.LavaCauldron; + for (int i = 19463; i <= 19478; i++) + materials[i] = Material.Lectern; + for (int i = 5793; i <= 5816; i++) + materials[i] = Material.Lever; + for (int i = 11246; i <= 11277; i++) + materials[i] = Material.Light; + for (int i = 11686; i <= 11701; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1779; i <= 1794; i++) + materials[i] = Material.LightBlueBed; + for (int i = 21802; i <= 21817; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 22018; i <= 22019; i++) + materials[i] = Material.LightBlueCandleCake; + materials[11610] = Material.LightBlueCarpet; + materials[13744] = Material.LightBlueConcrete; + materials[13760] = Material.LightBlueConcretePowder; + for (int i = 13689; i <= 13692; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 13599; i <= 13604; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[6117] = Material.LightBlueStainedGlass; + for (int i = 10267; i <= 10298; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[10158] = Material.LightBlueTerracotta; + for (int i = 11906; i <= 11909; i++) + materials[i] = Material.LightBlueWallBanner; + materials[2093] = Material.LightBlueWool; + for (int i = 11766; i <= 11781; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1859; i <= 1874; i++) + materials[i] = Material.LightGrayBed; + for (int i = 21882; i <= 21897; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 22028; i <= 22029; i++) + materials[i] = Material.LightGrayCandleCake; + materials[11615] = Material.LightGrayCarpet; + materials[13749] = Material.LightGrayConcrete; + materials[13765] = Material.LightGrayConcretePowder; + for (int i = 13709; i <= 13712; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 13629; i <= 13634; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[6122] = Material.LightGrayStainedGlass; + for (int i = 10427; i <= 10458; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[10163] = Material.LightGrayTerracotta; + for (int i = 11926; i <= 11929; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2098] = Material.LightGrayWool; + for (int i = 9942; i <= 9957; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 25737; i <= 25760; i++) + materials[i] = Material.LightningRod; + for (int i = 11628; i <= 11629; i++) + materials[i] = Material.Lilac; + materials[2131] = Material.LilyOfTheValley; + materials[7632] = Material.LilyPad; + for (int i = 11718; i <= 11733; i++) + materials[i] = Material.LimeBanner; + for (int i = 1811; i <= 1826; i++) + materials[i] = Material.LimeBed; + for (int i = 21834; i <= 21849; i++) + materials[i] = Material.LimeCandle; + for (int i = 22022; i <= 22023; i++) + materials[i] = Material.LimeCandleCake; + materials[11612] = Material.LimeCarpet; + materials[13746] = Material.LimeConcrete; + materials[13762] = Material.LimeConcretePowder; + for (int i = 13697; i <= 13700; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 13611; i <= 13616; i++) + materials[i] = Material.LimeShulkerBox; + materials[6119] = Material.LimeStainedGlass; + for (int i = 10331; i <= 10362; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[10160] = Material.LimeTerracotta; + for (int i = 11914; i <= 11917; i++) + materials[i] = Material.LimeWallBanner; + materials[2095] = Material.LimeWool; + materials[20472] = Material.Lodestone; + for (int i = 19417; i <= 19420; i++) + materials[i] = Material.Loom; + for (int i = 11670; i <= 11685; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1763; i <= 1778; i++) + materials[i] = Material.MagentaBed; + for (int i = 21786; i <= 21801; i++) + materials[i] = Material.MagentaCandle; + for (int i = 22016; i <= 22017; i++) + materials[i] = Material.MagentaCandleCake; + materials[11609] = Material.MagentaCarpet; + materials[13743] = Material.MagentaConcrete; + materials[13759] = Material.MagentaConcretePowder; + for (int i = 13685; i <= 13688; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 13593; i <= 13598; i++) + materials[i] = Material.MagentaShulkerBox; + materials[6116] = Material.MagentaStainedGlass; + for (int i = 10235; i <= 10266; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[10157] = Material.MagentaTerracotta; + for (int i = 11902; i <= 11905; i++) + materials[i] = Material.MagentaWallBanner; + materials[2092] = Material.MagentaWool; + materials[13556] = Material.MagmaBlock; + for (int i = 9578; i <= 9601; i++) + materials[i] = Material.MangroveButton; + for (int i = 13219; i <= 13282; i++) + materials[i] = Material.MangroveDoor; + for (int i = 12707; i <= 12738; i++) + materials[i] = Material.MangroveFence; + for (int i = 12419; i <= 12450; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5569; i <= 5632; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + materials[26] = Material.MangrovePlanks; + for (int i = 5899; i <= 5900; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4613; i <= 4644; i++) + materials[i] = Material.MangroveSign; + for (int i = 12089; i <= 12094; i++) + materials[i] = Material.MangroveSlab; + for (int i = 11003; i <= 11082; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6642; i <= 6705; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5761; i <= 5768; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4913; i <= 4920; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 22070; i <= 22081; i++) + materials[i] = Material.MediumAmethystBud; + materials[7045] = Material.Melon; + for (int i = 7062; i <= 7069; i++) + materials[i] = Material.MelonStem; + materials[25856] = Material.MossBlock; + materials[25839] = Material.MossCarpet; + materials[2396] = Material.MossyCobblestone; + for (int i = 15119; i <= 15124; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 14295; i <= 14374; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 9017; i <= 9340; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 15107; i <= 15112; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 14135; i <= 14214; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 16145; i <= 16468; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6771] = Material.MossyStoneBricks; + for (int i = 2106; i <= 2117; i++) + materials[i] = Material.MovingPiston; + materials[25916] = Material.Mud; + for (int i = 12155; i <= 12160; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 7550; i <= 7629; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 17117; i <= 17440; i++) + materials[i] = Material.MudBrickWall; + materials[6775] = Material.MudBricks; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6910; i <= 6973; i++) + materials[i] = Material.MushroomStem; + for (int i = 7630; i <= 7631; i++) + materials[i] = Material.Mycelium; + for (int i = 8047; i <= 8078; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 12161; i <= 12166; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 8079; i <= 8158; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 17441; i <= 17764; i++) + materials[i] = Material.NetherBrickWall; + materials[8046] = Material.NetherBricks; + materials[135] = Material.NetherGoldOre; + for (int i = 6033; i <= 6034; i++) + materials[i] = Material.NetherPortal; + materials[10023] = Material.NetherQuartzOre; + materials[19608] = Material.NetherSprouts; + for (int i = 8159; i <= 8162; i++) + materials[i] = Material.NetherWart; + materials[13557] = Material.NetherWartBlock; + materials[20460] = Material.NetheriteBlock; + materials[6018] = Material.Netherrack; + for (int i = 581; i <= 1730; i++) + materials[i] = Material.NoteBlock; + for (int i = 9386; i <= 9409; i++) + materials[i] = Material.OakButton; + for (int i = 4677; i <= 4740; i++) + materials[i] = Material.OakDoor; + for (int i = 5986; i <= 6017; i++) + materials[i] = Material.OakFence; + for (int i = 7358; i <= 7389; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4929; i <= 4992; i++) + materials[i] = Material.OakHangingSign; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5883; i <= 5884; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 4357; i <= 4388; i++) + materials[i] = Material.OakSign; + for (int i = 12041; i <= 12046; i++) + materials[i] = Material.OakSlab; + for (int i = 2929; i <= 3008; i++) + materials[i] = Material.OakStairs; + for (int i = 6130; i <= 6193; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5697; i <= 5704; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4849; i <= 4856; i++) + materials[i] = Material.OakWallSign; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 13563; i <= 13574; i++) + materials[i] = Material.Observer; + materials[2397] = Material.Obsidian; + for (int i = 27576; i <= 27578; i++) + materials[i] = Material.OchreFroglight; + materials[27862] = Material.OpenEyeblossom; + for (int i = 11654; i <= 11669; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1747; i <= 1762; i++) + materials[i] = Material.OrangeBed; + for (int i = 21770; i <= 21785; i++) + materials[i] = Material.OrangeCandle; + for (int i = 22014; i <= 22015; i++) + materials[i] = Material.OrangeCandleCake; + materials[11608] = Material.OrangeCarpet; + materials[13742] = Material.OrangeConcrete; + materials[13758] = Material.OrangeConcretePowder; + for (int i = 13681; i <= 13684; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 13587; i <= 13592; i++) + materials[i] = Material.OrangeShulkerBox; + materials[6115] = Material.OrangeStainedGlass; + for (int i = 10203; i <= 10234; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[10156] = Material.OrangeTerracotta; + materials[2125] = Material.OrangeTulip; + for (int i = 11898; i <= 11901; i++) + materials[i] = Material.OrangeWallBanner; + materials[2091] = Material.OrangeWool; + materials[2128] = Material.OxeyeDaisy; + materials[23961] = Material.OxidizedChiseledCopper; + materials[23954] = Material.OxidizedCopper; + for (int i = 25717; i <= 25720; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 24793; i <= 24856; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 25695; i <= 25696; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 25305; i <= 25368; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + materials[23957] = Material.OxidizedCutCopper; + for (int i = 24289; i <= 24294; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 23969; i <= 24048; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[11625] = Material.PackedIce; + materials[6774] = Material.PackedMud; + for (int i = 27860; i <= 27861; i++) + materials[i] = Material.PaleHangingMoss; + materials[27697] = Material.PaleMossBlock; + for (int i = 27698; i <= 27859; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 9554; i <= 9577; i++) + materials[i] = Material.PaleOakButton; + for (int i = 13155; i <= 13218; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 12675; i <= 12706; i++) + materials[i] = Material.PaleOakFence; + for (int i = 12387; i <= 12418; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 5377; i <= 5440; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + materials[25] = Material.PaleOakPlanks; + for (int i = 5897; i <= 5898; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 4581; i <= 4612; i++) + materials[i] = Material.PaleOakSign; + for (int i = 12083; i <= 12088; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 10923; i <= 11002; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 6578; i <= 6641; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 5753; i <= 5760; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 4905; i <= 4912; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 27582; i <= 27584; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 11632; i <= 11633; i++) + materials[i] = Material.Peony; + for (int i = 12131; i <= 12136; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 9866; i <= 9897; i++) + materials[i] = Material.PiglinHead; + for (int i = 9898; i <= 9905; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 11734; i <= 11749; i++) + materials[i] = Material.PinkBanner; + for (int i = 1827; i <= 1842; i++) + materials[i] = Material.PinkBed; + for (int i = 21850; i <= 21865; i++) + materials[i] = Material.PinkCandle; + for (int i = 22024; i <= 22025; i++) + materials[i] = Material.PinkCandleCake; + materials[11613] = Material.PinkCarpet; + materials[13747] = Material.PinkConcrete; + materials[13763] = Material.PinkConcretePowder; + for (int i = 13701; i <= 13704; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 25840; i <= 25855; i++) + materials[i] = Material.PinkPetals; + for (int i = 13617; i <= 13622; i++) + materials[i] = Material.PinkShulkerBox; + materials[6120] = Material.PinkStainedGlass; + for (int i = 10363; i <= 10394; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[10161] = Material.PinkTerracotta; + materials[2127] = Material.PinkTulip; + for (int i = 11918; i <= 11921; i++) + materials[i] = Material.PinkWallBanner; + materials[2096] = Material.PinkWool; + for (int i = 2054; i <= 2065; i++) + materials[i] = Material.Piston; + for (int i = 2066; i <= 2089; i++) + materials[i] = Material.PistonHead; + for (int i = 13510; i <= 13519; i++) + materials[i] = Material.PitcherCrop; + for (int i = 13520; i <= 13521; i++) + materials[i] = Material.PitcherPlant; + for (int i = 9746; i <= 9777; i++) + materials[i] = Material.PlayerHead; + for (int i = 9778; i <= 9785; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 25761; i <= 25780; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 15161; i <= 15166; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 14935; i <= 15014; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 6024; i <= 6026; i++) + materials[i] = Material.PolishedBasalt; + materials[20884] = Material.PolishedBlackstone; + for (int i = 20888; i <= 20893; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 20894; i <= 20973; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 20974; i <= 21297; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[20885] = Material.PolishedBlackstoneBricks; + for (int i = 21387; i <= 21410; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 21385; i <= 21386; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 21379; i <= 21384; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 21299; i <= 21378; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 21411; i <= 21734; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[26331] = Material.PolishedDeepslate; + for (int i = 26412; i <= 26417; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 26332; i <= 26411; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 26418; i <= 26741; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 15113; i <= 15118; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 14215; i <= 14294; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 15095; i <= 15100; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 13975; i <= 14054; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[22505] = Material.PolishedTuff; + for (int i = 22506; i <= 22511; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 22512; i <= 22591; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 22592; i <= 22915; i++) + materials[i] = Material.PolishedTuffWall; + materials[2120] = Material.Poppy; + for (int i = 9378; i <= 9385; i++) + materials[i] = Material.Potatoes; + materials[9347] = Material.PottedAcaciaSapling; + materials[9356] = Material.PottedAllium; + materials[27574] = Material.PottedAzaleaBush; + materials[9357] = Material.PottedAzureBluet; + materials[13970] = Material.PottedBamboo; + materials[9345] = Material.PottedBirchSapling; + materials[9355] = Material.PottedBlueOrchid; + materials[9367] = Material.PottedBrownMushroom; + materials[9369] = Material.PottedCactus; + materials[9348] = Material.PottedCherrySapling; + materials[27865] = Material.PottedClosedEyeblossom; + materials[9363] = Material.PottedCornflower; + materials[20468] = Material.PottedCrimsonFungus; + materials[20470] = Material.PottedCrimsonRoots; + materials[9353] = Material.PottedDandelion; + materials[9349] = Material.PottedDarkOakSapling; + materials[9368] = Material.PottedDeadBush; + materials[9352] = Material.PottedFern; + materials[27575] = Material.PottedFloweringAzaleaBush; + materials[9346] = Material.PottedJungleSapling; + materials[9364] = Material.PottedLilyOfTheValley; + materials[9351] = Material.PottedMangrovePropagule; + materials[9343] = Material.PottedOakSapling; + materials[27864] = Material.PottedOpenEyeblossom; + materials[9359] = Material.PottedOrangeTulip; + materials[9362] = Material.PottedOxeyeDaisy; + materials[9350] = Material.PottedPaleOakSapling; + materials[9361] = Material.PottedPinkTulip; + materials[9354] = Material.PottedPoppy; + materials[9366] = Material.PottedRedMushroom; + materials[9358] = Material.PottedRedTulip; + materials[9344] = Material.PottedSpruceSapling; + materials[9342] = Material.PottedTorchflower; + materials[20469] = Material.PottedWarpedFungus; + materials[20471] = Material.PottedWarpedRoots; + materials[9360] = Material.PottedWhiteTulip; + materials[9365] = Material.PottedWitherRose; + materials[23331] = Material.PowderSnow; + for (int i = 8177; i <= 8179; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1987; i <= 2010; i++) + materials[i] = Material.PoweredRail; + materials[11342] = Material.Prismarine; + for (int i = 11591; i <= 11596; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 11425; i <= 11504; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[11343] = Material.PrismarineBricks; + for (int i = 11585; i <= 11590; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 11345; i <= 11424; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 15497; i <= 15820; i++) + materials[i] = Material.PrismarineWall; + materials[7044] = Material.Pumpkin; + for (int i = 7054; i <= 7061; i++) + materials[i] = Material.PumpkinStem; + for (int i = 11798; i <= 11813; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1891; i <= 1906; i++) + materials[i] = Material.PurpleBed; + for (int i = 21914; i <= 21929; i++) + materials[i] = Material.PurpleCandle; + for (int i = 22032; i <= 22033; i++) + materials[i] = Material.PurpleCandleCake; + materials[11617] = Material.PurpleCarpet; + materials[13751] = Material.PurpleConcrete; + materials[13767] = Material.PurpleConcretePowder; + for (int i = 13717; i <= 13720; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 13641; i <= 13646; i++) + materials[i] = Material.PurpleShulkerBox; + materials[6124] = Material.PurpleStainedGlass; + for (int i = 10491; i <= 10522; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[10165] = Material.PurpleTerracotta; + for (int i = 11934; i <= 11937; i++) + materials[i] = Material.PurpleWallBanner; + materials[2100] = Material.PurpleWool; + materials[13423] = Material.PurpurBlock; + for (int i = 13424; i <= 13426; i++) + materials[i] = Material.PurpurPillar; + for (int i = 12185; i <= 12190; i++) + materials[i] = Material.PurpurSlab; + for (int i = 13427; i <= 13506; i++) + materials[i] = Material.PurpurStairs; + materials[10034] = Material.QuartzBlock; + materials[21737] = Material.QuartzBricks; + for (int i = 10036; i <= 10038; i++) + materials[i] = Material.QuartzPillar; + for (int i = 12167; i <= 12172; i++) + materials[i] = Material.QuartzSlab; + for (int i = 10039; i <= 10118; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4749; i <= 4768; i++) + materials[i] = Material.Rail; + materials[27572] = Material.RawCopperBlock; + materials[27573] = Material.RawGoldBlock; + materials[27571] = Material.RawIronBlock; + for (int i = 11862; i <= 11877; i++) + materials[i] = Material.RedBanner; + for (int i = 1955; i <= 1970; i++) + materials[i] = Material.RedBed; + for (int i = 21978; i <= 21993; i++) + materials[i] = Material.RedCandle; + for (int i = 22040; i <= 22041; i++) + materials[i] = Material.RedCandleCake; + materials[11621] = Material.RedCarpet; + materials[13755] = Material.RedConcrete; + materials[13771] = Material.RedConcretePowder; + for (int i = 13733; i <= 13736; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2133] = Material.RedMushroom; + for (int i = 6846; i <= 6909; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 15155; i <= 15160; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 14855; i <= 14934; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 18089; i <= 18412; i++) + materials[i] = Material.RedNetherBrickWall; + materials[13558] = Material.RedNetherBricks; + materials[123] = Material.RedSand; + materials[11958] = Material.RedSandstone; + for (int i = 12173; i <= 12178; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 11961; i <= 12040; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 15821; i <= 16144; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 13665; i <= 13670; i++) + materials[i] = Material.RedShulkerBox; + materials[6128] = Material.RedStainedGlass; + for (int i = 10619; i <= 10650; i++) + materials[i] = Material.RedStainedGlassPane; + materials[10169] = Material.RedTerracotta; + materials[2124] = Material.RedTulip; + for (int i = 11950; i <= 11953; i++) + materials[i] = Material.RedWallBanner; + materials[2104] = Material.RedWool; + materials[10022] = Material.RedstoneBlock; + for (int i = 8191; i <= 8192; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5903; i <= 5904; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5907; i <= 5908; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5909; i <= 5916; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 3033; i <= 4328; i++) + materials[i] = Material.RedstoneWire; + materials[27586] = Material.ReinforcedDeepslate; + for (int i = 6050; i <= 6113; i++) + materials[i] = Material.Repeater; + for (int i = 13528; i <= 13539; i++) + materials[i] = Material.RepeatingCommandBlock; + materials[7633] = Material.ResinBlock; + for (int i = 7715; i <= 7720; i++) + materials[i] = Material.ResinBrickSlab; + for (int i = 7635; i <= 7714; i++) + materials[i] = Material.ResinBrickStairs; + for (int i = 7721; i <= 8044; i++) + materials[i] = Material.ResinBrickWall; + materials[7634] = Material.ResinBricks; + for (int i = 7230; i <= 7357; i++) + materials[i] = Material.ResinClump; + for (int i = 20463; i <= 20467; i++) + materials[i] = Material.RespawnAnchor; + materials[25915] = Material.RootedDirt; + for (int i = 11630; i <= 11631; i++) + materials[i] = Material.RoseBush; + materials[118] = Material.Sand; + materials[578] = Material.Sandstone; + for (int i = 12119; i <= 12124; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 8205; i <= 8284; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 18413; i <= 18736; i++) + materials[i] = Material.SandstoneWall; + for (int i = 19385; i <= 19416; i++) + materials[i] = Material.Scaffolding; + materials[23812] = Material.Sculk; + for (int i = 23941; i <= 23942; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 23332; i <= 23427; i++) + materials[i] = Material.SculkSensor; + for (int i = 23943; i <= 23950; i++) + materials[i] = Material.SculkShrieker; + for (int i = 23813; i <= 23940; i++) + materials[i] = Material.SculkVein; + materials[11603] = Material.SeaLantern; + for (int i = 13946; i <= 13953; i++) + materials[i] = Material.SeaPickle; + materials[2051] = Material.Seagrass; + materials[2048] = Material.ShortGrass; + materials[19623] = Material.Shroomlight; + for (int i = 13575; i <= 13580; i++) + materials[i] = Material.ShulkerBox; + for (int i = 9626; i <= 9657; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 9658; i <= 9665; i++) + materials[i] = Material.SkeletonWallSkull; + materials[11243] = Material.SlimeBlock; + for (int i = 22082; i <= 22093; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 25897; i <= 25912; i++) + materials[i] = Material.SmallDripleaf; + materials[19479] = Material.SmithingTable; + for (int i = 19433; i <= 19440; i++) + materials[i] = Material.Smoker; + materials[27570] = Material.SmoothBasalt; + materials[12193] = Material.SmoothQuartz; + for (int i = 15137; i <= 15142; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 14615; i <= 14694; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[12194] = Material.SmoothRedSandstone; + for (int i = 15101; i <= 15106; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 14055; i <= 14134; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[12192] = Material.SmoothSandstone; + for (int i = 15131; i <= 15136; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 14535; i <= 14614; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[12191] = Material.SmoothStone; + for (int i = 12113; i <= 12118; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13813; i <= 13815; i++) + materials[i] = Material.SnifferEgg; + for (int i = 5941; i <= 5948; i++) + materials[i] = Material.Snow; + materials[5950] = Material.SnowBlock; + for (int i = 19556; i <= 19587; i++) + materials[i] = Material.SoulCampfire; + materials[2915] = Material.SoulFire; + for (int i = 19520; i <= 19523; i++) + materials[i] = Material.SoulLantern; + materials[6019] = Material.SoulSand; + materials[6020] = Material.SoulSoil; + materials[6027] = Material.SoulTorch; + for (int i = 6028; i <= 6031; i++) + materials[i] = Material.SoulWallTorch; + materials[2916] = Material.Spawner; + materials[560] = Material.Sponge; + materials[25836] = Material.SporeBlossom; + for (int i = 9410; i <= 9433; i++) + materials[i] = Material.SpruceButton; + for (int i = 12771; i <= 12834; i++) + materials[i] = Material.SpruceDoor; + for (int i = 12483; i <= 12514; i++) + materials[i] = Material.SpruceFence; + for (int i = 12195; i <= 12226; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 4993; i <= 5056; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5885; i <= 5886; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4389; i <= 4420; i++) + materials[i] = Material.SpruceSign; + for (int i = 12047; i <= 12052; i++) + materials[i] = Material.SpruceSlab; + for (int i = 8440; i <= 8519; i++) + materials[i] = Material.SpruceStairs; + for (int i = 6194; i <= 6257; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5705; i <= 5712; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4857; i <= 4864; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 2035; i <= 2046; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 12149; i <= 12154; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 7470; i <= 7549; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 16793; i <= 17116; i++) + materials[i] = Material.StoneBrickWall; + materials[6770] = Material.StoneBricks; + for (int i = 5917; i <= 5940; i++) + materials[i] = Material.StoneButton; + for (int i = 5817; i <= 5818; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 12107; i <= 12112; i++) + materials[i] = Material.StoneSlab; + for (int i = 14455; i <= 14534; i++) + materials[i] = Material.StoneStairs; + for (int i = 19480; i <= 19483; i++) + materials[i] = Material.Stonecutter; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 19618; i <= 19620; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 19612; i <= 19614; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 19601; i <= 19603; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 19595; i <= 19597; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 20369; i <= 20372; i++) + materials[i] = Material.StructureBlock; + materials[13562] = Material.StructureVoid; + for (int i = 5968; i <= 5983; i++) + materials[i] = Material.SugarCane; + for (int i = 11626; i <= 11627; i++) + materials[i] = Material.Sunflower; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 19588; i <= 19591; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 11634; i <= 11635; i++) + materials[i] = Material.TallGrass; + for (int i = 2052; i <= 2053; i++) + materials[i] = Material.TallSeagrass; + for (int i = 20394; i <= 20409; i++) + materials[i] = Material.Target; + materials[11623] = Material.Terracotta; + materials[23330] = Material.TintedGlass; + for (int i = 2137; i <= 2138; i++) + materials[i] = Material.Tnt; + materials[2398] = Material.Torch; + materials[2119] = Material.Torchflower; + for (int i = 13508; i <= 13509; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 9918; i <= 9941; i++) + materials[i] = Material.TrappedChest; + for (int i = 27651; i <= 27662; i++) + materials[i] = Material.TrialSpawner; + for (int i = 8311; i <= 8438; i++) + materials[i] = Material.Tripwire; + for (int i = 8295; i <= 8310; i++) + materials[i] = Material.TripwireHook; + for (int i = 13836; i <= 13837; i++) + materials[i] = Material.TubeCoral; + materials[13821] = Material.TubeCoralBlock; + for (int i = 13856; i <= 13857; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 13906; i <= 13913; i++) + materials[i] = Material.TubeCoralWallFan; + materials[22094] = Material.Tuff; + for (int i = 22918; i <= 22923; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 22924; i <= 23003; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 23004; i <= 23327; i++) + materials[i] = Material.TuffBrickWall; + materials[22917] = Material.TuffBricks; + for (int i = 22095; i <= 22100; i++) + materials[i] = Material.TuffSlab; + for (int i = 22101; i <= 22180; i++) + materials[i] = Material.TuffStairs; + for (int i = 22181; i <= 22504; i++) + materials[i] = Material.TuffWall; + for (int i = 13801; i <= 13812; i++) + materials[i] = Material.TurtleEgg; + for (int i = 19651; i <= 19676; i++) + materials[i] = Material.TwistingVines; + materials[19677] = Material.TwistingVinesPlant; + for (int i = 27663; i <= 27694; i++) + materials[i] = Material.Vault; + for (int i = 27579; i <= 27581; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 7070; i <= 7101; i++) + materials[i] = Material.Vine; + materials[13971] = Material.VoidAir; + for (int i = 2399; i <= 2402; i++) + materials[i] = Material.WallTorch; + for (int i = 20137; i <= 20160; i++) + materials[i] = Material.WarpedButton; + for (int i = 20225; i <= 20288; i++) + materials[i] = Material.WarpedDoor; + for (int i = 19729; i <= 19760; i++) + materials[i] = Material.WarpedFence; + for (int i = 19921; i <= 19952; i++) + materials[i] = Material.WarpedFenceGate; + materials[19605] = Material.WarpedFungus; + for (int i = 5505; i <= 5568; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 19598; i <= 19600; i++) + materials[i] = Material.WarpedHyphae; + materials[19604] = Material.WarpedNylium; + materials[19680] = Material.WarpedPlanks; + for (int i = 19695; i <= 19696; i++) + materials[i] = Material.WarpedPressurePlate; + materials[19607] = Material.WarpedRoots; + for (int i = 20321; i <= 20352; i++) + materials[i] = Material.WarpedSign; + for (int i = 19687; i <= 19692; i++) + materials[i] = Material.WarpedSlab; + for (int i = 20033; i <= 20112; i++) + materials[i] = Material.WarpedStairs; + for (int i = 19592; i <= 19594; i++) + materials[i] = Material.WarpedStem; + for (int i = 19825; i <= 19888; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5777; i <= 5784; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 20361; i <= 20368; i++) + materials[i] = Material.WarpedWallSign; + materials[19606] = Material.WarpedWartBlock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 8173; i <= 8175; i++) + materials[i] = Material.WaterCauldron; + materials[23968] = Material.WaxedChiseledCopper; + materials[24313] = Material.WaxedCopperBlock; + for (int i = 25721; i <= 25724; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 24921; i <= 24984; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 25697; i <= 25698; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 25433; i <= 25496; i++) + materials[i] = Material.WaxedCopperTrapdoor; + materials[24320] = Material.WaxedCutCopper; + for (int i = 24659; i <= 24664; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 24561; i <= 24640; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[23967] = Material.WaxedExposedChiseledCopper; + materials[24315] = Material.WaxedExposedCopper; + for (int i = 25725; i <= 25728; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 24985; i <= 25048; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 25699; i <= 25700; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 25497; i <= 25560; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + materials[24319] = Material.WaxedExposedCutCopper; + for (int i = 24653; i <= 24658; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 24481; i <= 24560; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[23965] = Material.WaxedOxidizedChiseledCopper; + materials[24316] = Material.WaxedOxidizedCopper; + for (int i = 25733; i <= 25736; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 25049; i <= 25112; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 25703; i <= 25704; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 25561; i <= 25624; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + materials[24317] = Material.WaxedOxidizedCutCopper; + for (int i = 24641; i <= 24646; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 24321; i <= 24400; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[23966] = Material.WaxedWeatheredChiseledCopper; + materials[24314] = Material.WaxedWeatheredCopper; + for (int i = 25729; i <= 25732; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 25113; i <= 25176; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 25701; i <= 25702; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 25625; i <= 25688; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + materials[24318] = Material.WaxedWeatheredCutCopper; + for (int i = 24647; i <= 24652; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 24401; i <= 24480; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[23962] = Material.WeatheredChiseledCopper; + materials[23953] = Material.WeatheredCopper; + for (int i = 25713; i <= 25716; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 24857; i <= 24920; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 25693; i <= 25694; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 25369; i <= 25432; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + materials[23958] = Material.WeatheredCutCopper; + for (int i = 24295; i <= 24300; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 24049; i <= 24128; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 19624; i <= 19649; i++) + materials[i] = Material.WeepingVines; + materials[19650] = Material.WeepingVinesPlant; + materials[561] = Material.WetSponge; + for (int i = 4333; i <= 4340; i++) + materials[i] = Material.Wheat; + for (int i = 11638; i <= 11653; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1731; i <= 1746; i++) + materials[i] = Material.WhiteBed; + for (int i = 21754; i <= 21769; i++) + materials[i] = Material.WhiteCandle; + for (int i = 22012; i <= 22013; i++) + materials[i] = Material.WhiteCandleCake; + materials[11607] = Material.WhiteCarpet; + materials[13741] = Material.WhiteConcrete; + materials[13757] = Material.WhiteConcretePowder; + for (int i = 13677; i <= 13680; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 13581; i <= 13586; i++) + materials[i] = Material.WhiteShulkerBox; + materials[6114] = Material.WhiteStainedGlass; + for (int i = 10171; i <= 10202; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[10155] = Material.WhiteTerracotta; + materials[2126] = Material.WhiteTulip; + for (int i = 11894; i <= 11897; i++) + materials[i] = Material.WhiteWallBanner; + materials[2090] = Material.WhiteWool; + materials[2130] = Material.WitherRose; + for (int i = 9666; i <= 9697; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 9698; i <= 9705; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 11702; i <= 11717; i++) + materials[i] = Material.YellowBanner; + for (int i = 1795; i <= 1810; i++) + materials[i] = Material.YellowBed; + for (int i = 21818; i <= 21833; i++) + materials[i] = Material.YellowCandle; + for (int i = 22020; i <= 22021; i++) + materials[i] = Material.YellowCandleCake; + materials[11611] = Material.YellowCarpet; + materials[13745] = Material.YellowConcrete; + materials[13761] = Material.YellowConcretePowder; + for (int i = 13693; i <= 13696; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 13605; i <= 13610; i++) + materials[i] = Material.YellowShulkerBox; + materials[6118] = Material.YellowStainedGlass; + for (int i = 10299; i <= 10330; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[10159] = Material.YellowTerracotta; + for (int i = 11910; i <= 11913; i++) + materials[i] = Material.YellowWallBanner; + materials[2094] = Material.YellowWool; + for (int i = 9706; i <= 9737; i++) + materials[i] = Material.ZombieHead; + for (int i = 9738; i <= 9745; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1215.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1215.cs new file mode 100644 index 00000000..c01f5d00 --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1215.cs @@ -0,0 +1,1838 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1215 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1215() + { + for (int i = 9492; i <= 9515; i++) + materials[i] = Material.AcaciaButton; + for (int i = 12973; i <= 13036; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 12589; i <= 12620; i++) + materials[i] = Material.AcaciaFence; + for (int i = 12301; i <= 12332; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 5130; i <= 5193; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5900; i <= 5901; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4462; i <= 4493; i++) + materials[i] = Material.AcaciaSign; + for (int i = 12075; i <= 12080; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 10693; i <= 10772; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6396; i <= 6459; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5730; i <= 5737; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4882; i <= 4889; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 10129; i <= 10152; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2125] = Material.Allium; + materials[22059] = Material.AmethystBlock; + for (int i = 22061; i <= 22072; i++) + materials[i] = Material.AmethystCluster; + materials[20476] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 15159; i <= 15164; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 14785; i <= 14864; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 17775; i <= 18098; i++) + materials[i] = Material.AndesiteWall; + for (int i = 9916; i <= 9919; i++) + materials[i] = Material.Anvil; + for (int i = 7060; i <= 7063; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 7056; i <= 7059; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[25852] = Material.Azalea; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + materials[2126] = Material.AzureBluet; + for (int i = 13968; i <= 13979; i++) + materials[i] = Material.Bamboo; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 9612; i <= 9635; i++) + materials[i] = Material.BambooButton; + for (int i = 13293; i <= 13356; i++) + materials[i] = Material.BambooDoor; + for (int i = 12749; i <= 12780; i++) + materials[i] = Material.BambooFence; + for (int i = 12461; i <= 12492; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5642; i <= 5705; i++) + materials[i] = Material.BambooHangingSign; + materials[28] = Material.BambooMosaic; + for (int i = 12111; i <= 12116; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 11173; i <= 11252; i++) + materials[i] = Material.BambooMosaicStairs; + materials[27] = Material.BambooPlanks; + for (int i = 5910; i <= 5911; i++) + materials[i] = Material.BambooPressurePlate; + materials[13967] = Material.BambooSapling; + for (int i = 4654; i <= 4685; i++) + materials[i] = Material.BambooSign; + for (int i = 12105; i <= 12110; i++) + materials[i] = Material.BambooSlab; + for (int i = 11093; i <= 11172; i++) + materials[i] = Material.BambooStairs; + for (int i = 6716; i <= 6779; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5794; i <= 5801; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4930; i <= 4937; i++) + materials[i] = Material.BambooWallSign; + for (int i = 19431; i <= 19442; i++) + materials[i] = Material.Barrel; + for (int i = 11254; i <= 11255; i++) + materials[i] = Material.Barrier; + for (int i = 6031; i <= 6033; i++) + materials[i] = Material.Basalt; + materials[8702] = Material.Beacon; + materials[85] = Material.Bedrock; + for (int i = 20425; i <= 20448; i++) + materials[i] = Material.BeeNest; + for (int i = 20449; i <= 20472; i++) + materials[i] = Material.Beehive; + for (int i = 13532; i <= 13535; i++) + materials[i] = Material.Beetroots; + for (int i = 19494; i <= 19525; i++) + materials[i] = Material.Bell; + for (int i = 25904; i <= 25935; i++) + materials[i] = Material.BigDripleaf; + for (int i = 25936; i <= 25943; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 9444; i <= 9467; i++) + materials[i] = Material.BirchButton; + for (int i = 12845; i <= 12908; i++) + materials[i] = Material.BirchDoor; + for (int i = 12525; i <= 12556; i++) + materials[i] = Material.BirchFence; + for (int i = 12237; i <= 12268; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 5066; i <= 5129; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5896; i <= 5897; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 4430; i <= 4461; i++) + materials[i] = Material.BirchSign; + for (int i = 12063; i <= 12068; i++) + materials[i] = Material.BirchSlab; + for (int i = 8530; i <= 8609; i++) + materials[i] = Material.BirchStairs; + for (int i = 6268; i <= 6331; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5722; i <= 5729; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4874; i <= 4881; i++) + materials[i] = Material.BirchWallSign; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 11888; i <= 11903; i++) + materials[i] = Material.BlackBanner; + for (int i = 1971; i <= 1986; i++) + materials[i] = Material.BlackBed; + for (int i = 22009; i <= 22024; i++) + materials[i] = Material.BlackCandle; + for (int i = 22057; i <= 22058; i++) + materials[i] = Material.BlackCandleCake; + materials[11632] = Material.BlackCarpet; + materials[13766] = Material.BlackConcrete; + materials[13782] = Material.BlackConcretePowder; + for (int i = 13747; i <= 13750; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 13681; i <= 13686; i++) + materials[i] = Material.BlackShulkerBox; + materials[6139] = Material.BlackStainedGlass; + for (int i = 10661; i <= 10692; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[10180] = Material.BlackTerracotta; + for (int i = 11964; i <= 11967; i++) + materials[i] = Material.BlackWallBanner; + materials[2108] = Material.BlackWool; + materials[20488] = Material.Blackstone; + for (int i = 20893; i <= 20898; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 20489; i <= 20568; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 20569; i <= 20892; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 19451; i <= 19458; i++) + materials[i] = Material.BlastFurnace; + for (int i = 11824; i <= 11839; i++) + materials[i] = Material.BlueBanner; + for (int i = 1907; i <= 1922; i++) + materials[i] = Material.BlueBed; + for (int i = 21945; i <= 21960; i++) + materials[i] = Material.BlueCandle; + for (int i = 22049; i <= 22050; i++) + materials[i] = Material.BlueCandleCake; + materials[11628] = Material.BlueCarpet; + materials[13762] = Material.BlueConcrete; + materials[13778] = Material.BlueConcretePowder; + for (int i = 13731; i <= 13734; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[13964] = Material.BlueIce; + materials[2124] = Material.BlueOrchid; + for (int i = 13657; i <= 13662; i++) + materials[i] = Material.BlueShulkerBox; + materials[6135] = Material.BlueStainedGlass; + for (int i = 10533; i <= 10564; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[10176] = Material.BlueTerracotta; + for (int i = 11948; i <= 11951; i++) + materials[i] = Material.BlueWallBanner; + materials[2104] = Material.BlueWool; + for (int i = 13569; i <= 13571; i++) + materials[i] = Material.BoneBlock; + materials[2142] = Material.Bookshelf; + for (int i = 13848; i <= 13849; i++) + materials[i] = Material.BrainCoral; + materials[13832] = Material.BrainCoralBlock; + for (int i = 13868; i <= 13869; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 13924; i <= 13931; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 8174; i <= 8181; i++) + materials[i] = Material.BrewingStand; + for (int i = 12153; i <= 12158; i++) + materials[i] = Material.BrickSlab; + for (int i = 7400; i <= 7479; i++) + materials[i] = Material.BrickStairs; + for (int i = 15183; i <= 15506; i++) + materials[i] = Material.BrickWall; + materials[2139] = Material.Bricks; + for (int i = 11840; i <= 11855; i++) + materials[i] = Material.BrownBanner; + for (int i = 1923; i <= 1938; i++) + materials[i] = Material.BrownBed; + for (int i = 21961; i <= 21976; i++) + materials[i] = Material.BrownCandle; + for (int i = 22051; i <= 22052; i++) + materials[i] = Material.BrownCandleCake; + materials[11629] = Material.BrownCarpet; + materials[13763] = Material.BrownConcrete; + materials[13779] = Material.BrownConcretePowder; + for (int i = 13735; i <= 13738; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2135] = Material.BrownMushroom; + for (int i = 6792; i <= 6855; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 13663; i <= 13668; i++) + materials[i] = Material.BrownShulkerBox; + materials[6136] = Material.BrownStainedGlass; + for (int i = 10565; i <= 10596; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[10177] = Material.BrownTerracotta; + for (int i = 11952; i <= 11955; i++) + materials[i] = Material.BrownWallBanner; + materials[2105] = Material.BrownWool; + for (int i = 13983; i <= 13984; i++) + materials[i] = Material.BubbleColumn; + for (int i = 13850; i <= 13851; i++) + materials[i] = Material.BubbleCoral; + materials[13833] = Material.BubbleCoralBlock; + for (int i = 13870; i <= 13871; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 13932; i <= 13939; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[22060] = Material.BuddingAmethyst; + materials[2051] = Material.Bush; + for (int i = 5960; i <= 5975; i++) + materials[i] = Material.Cactus; + materials[5976] = Material.CactusFlower; + for (int i = 6053; i <= 6059; i++) + materials[i] = Material.Cake; + materials[23344] = Material.Calcite; + for (int i = 23443; i <= 23826; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 19534; i <= 19565; i++) + materials[i] = Material.Campfire; + for (int i = 21753; i <= 21768; i++) + materials[i] = Material.Candle; + for (int i = 22025; i <= 22026; i++) + materials[i] = Material.CandleCake; + for (int i = 9380; i <= 9387; i++) + materials[i] = Material.Carrots; + materials[19459] = Material.CartographyTable; + for (int i = 6045; i <= 6048; i++) + materials[i] = Material.CarvedPumpkin; + materials[8182] = Material.Cauldron; + materials[13982] = Material.CaveAir; + for (int i = 25797; i <= 25848; i++) + materials[i] = Material.CaveVines; + for (int i = 25849; i <= 25850; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 7016; i <= 7021; i++) + materials[i] = Material.Chain; + for (int i = 13550; i <= 13561; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 9516; i <= 9539; i++) + materials[i] = Material.CherryButton; + for (int i = 13037; i <= 13100; i++) + materials[i] = Material.CherryDoor; + for (int i = 12621; i <= 12652; i++) + materials[i] = Material.CherryFence; + for (int i = 12333; i <= 12364; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 5194; i <= 5257; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + materials[20] = Material.CherryPlanks; + for (int i = 5902; i <= 5903; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 4494; i <= 4525; i++) + materials[i] = Material.CherrySign; + for (int i = 12081; i <= 12086; i++) + materials[i] = Material.CherrySlab; + for (int i = 10773; i <= 10852; i++) + materials[i] = Material.CherryStairs; + for (int i = 6460; i <= 6523; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 5738; i <= 5745; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 4890; i <= 4897; i++) + materials[i] = Material.CherryWallSign; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 3018; i <= 3041; i++) + materials[i] = Material.Chest; + for (int i = 9920; i <= 9923; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2143; i <= 2398; i++) + materials[i] = Material.ChiseledBookshelf; + materials[23979] = Material.ChiseledCopper; + materials[27611] = Material.ChiseledDeepslate; + materials[21750] = Material.ChiseledNetherBricks; + materials[20902] = Material.ChiseledPolishedBlackstone; + materials[10045] = Material.ChiseledQuartzBlock; + materials[11969] = Material.ChiseledRedSandstone; + materials[8055] = Material.ChiseledResinBricks; + materials[579] = Material.ChiseledSandstone; + materials[6783] = Material.ChiseledStoneBricks; + materials[22931] = Material.ChiseledTuff; + materials[23343] = Material.ChiseledTuffBricks; + for (int i = 13427; i <= 13432; i++) + materials[i] = Material.ChorusFlower; + for (int i = 13363; i <= 13426; i++) + materials[i] = Material.ChorusPlant; + materials[5977] = Material.Clay; + materials[27910] = Material.ClosedEyeblossom; + materials[11634] = Material.CoalBlock; + materials[133] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[25967] = Material.CobbledDeepslate; + for (int i = 26048; i <= 26053; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 25968; i <= 26047; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 26054; i <= 26377; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 12147; i <= 12152; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4778; i <= 4857; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 8703; i <= 9026; i++) + materials[i] = Material.CobblestoneWall; + materials[2047] = Material.Cobweb; + for (int i = 8203; i <= 8214; i++) + materials[i] = Material.Cocoa; + for (int i = 8690; i <= 8701; i++) + materials[i] = Material.CommandBlock; + for (int i = 9984; i <= 9999; i++) + materials[i] = Material.Comparator; + for (int i = 20400; i <= 20408; i++) + materials[i] = Material.Composter; + for (int i = 13965; i <= 13966; i++) + materials[i] = Material.Conduit; + materials[23966] = Material.CopperBlock; + for (int i = 25720; i <= 25723; i++) + materials[i] = Material.CopperBulb; + for (int i = 24680; i <= 24743; i++) + materials[i] = Material.CopperDoor; + for (int i = 25704; i <= 25705; i++) + materials[i] = Material.CopperGrate; + materials[23970] = Material.CopperOre; + for (int i = 25192; i <= 25255; i++) + materials[i] = Material.CopperTrapdoor; + materials[2132] = Material.Cornflower; + materials[27612] = Material.CrackedDeepslateBricks; + materials[27613] = Material.CrackedDeepslateTiles; + materials[21751] = Material.CrackedNetherBricks; + materials[20901] = Material.CrackedPolishedBlackstoneBricks; + materials[6782] = Material.CrackedStoneBricks; + for (int i = 27650; i <= 27697; i++) + materials[i] = Material.Crafter; + materials[4341] = Material.CraftingTable; + for (int i = 2920; i <= 2937; i++) + materials[i] = Material.CreakingHeart; + for (int i = 9796; i <= 9827; i++) + materials[i] = Material.CreeperHead; + for (int i = 9828; i <= 9835; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 20123; i <= 20146; i++) + materials[i] = Material.CrimsonButton; + for (int i = 20171; i <= 20234; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 19707; i <= 19738; i++) + materials[i] = Material.CrimsonFence; + for (int i = 19899; i <= 19930; i++) + materials[i] = Material.CrimsonFenceGate; + materials[19632] = Material.CrimsonFungus; + for (int i = 5450; i <= 5513; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 19625; i <= 19627; i++) + materials[i] = Material.CrimsonHyphae; + materials[19631] = Material.CrimsonNylium; + materials[19689] = Material.CrimsonPlanks; + for (int i = 19703; i <= 19704; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[19688] = Material.CrimsonRoots; + for (int i = 20299; i <= 20330; i++) + materials[i] = Material.CrimsonSign; + for (int i = 19691; i <= 19696; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 19963; i <= 20042; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 19619; i <= 19621; i++) + materials[i] = Material.CrimsonStem; + for (int i = 19771; i <= 19834; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5778; i <= 5785; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 20363; i <= 20370; i++) + materials[i] = Material.CrimsonWallSign; + materials[20477] = Material.CryingObsidian; + materials[23975] = Material.CutCopper; + for (int i = 24322; i <= 24327; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 24224; i <= 24303; i++) + materials[i] = Material.CutCopperStairs; + materials[11970] = Material.CutRedSandstone; + for (int i = 12189; i <= 12194; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[580] = Material.CutSandstone; + for (int i = 12135; i <= 12140; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 11792; i <= 11807; i++) + materials[i] = Material.CyanBanner; + for (int i = 1875; i <= 1890; i++) + materials[i] = Material.CyanBed; + for (int i = 21913; i <= 21928; i++) + materials[i] = Material.CyanCandle; + for (int i = 22045; i <= 22046; i++) + materials[i] = Material.CyanCandleCake; + materials[11626] = Material.CyanCarpet; + materials[13760] = Material.CyanConcrete; + materials[13776] = Material.CyanConcretePowder; + for (int i = 13723; i <= 13726; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 13645; i <= 13650; i++) + materials[i] = Material.CyanShulkerBox; + materials[6133] = Material.CyanStainedGlass; + for (int i = 10469; i <= 10500; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[10174] = Material.CyanTerracotta; + for (int i = 11940; i <= 11943; i++) + materials[i] = Material.CyanWallBanner; + materials[2102] = Material.CyanWool; + for (int i = 9924; i <= 9927; i++) + materials[i] = Material.DamagedAnvil; + materials[2121] = Material.Dandelion; + for (int i = 9540; i <= 9563; i++) + materials[i] = Material.DarkOakButton; + for (int i = 13101; i <= 13164; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 12653; i <= 12684; i++) + materials[i] = Material.DarkOakFence; + for (int i = 12365; i <= 12396; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5322; i <= 5385; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + materials[21] = Material.DarkOakPlanks; + for (int i = 5904; i <= 5905; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4558; i <= 4589; i++) + materials[i] = Material.DarkOakSign; + for (int i = 12087; i <= 12092; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 10853; i <= 10932; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6524; i <= 6587; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5754; i <= 5761; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4906; i <= 4913; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + materials[11354] = Material.DarkPrismarine; + for (int i = 11607; i <= 11612; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 11515; i <= 11594; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 10000; i <= 10031; i++) + materials[i] = Material.DaylightDetector; + for (int i = 13838; i <= 13839; i++) + materials[i] = Material.DeadBrainCoral; + materials[13827] = Material.DeadBrainCoralBlock; + for (int i = 13858; i <= 13859; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 13884; i <= 13891; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 13840; i <= 13841; i++) + materials[i] = Material.DeadBubbleCoral; + materials[13828] = Material.DeadBubbleCoralBlock; + for (int i = 13860; i <= 13861; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 13892; i <= 13899; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[2050] = Material.DeadBush; + for (int i = 13842; i <= 13843; i++) + materials[i] = Material.DeadFireCoral; + materials[13829] = Material.DeadFireCoralBlock; + for (int i = 13862; i <= 13863; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 13900; i <= 13907; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 13844; i <= 13845; i++) + materials[i] = Material.DeadHornCoral; + materials[13830] = Material.DeadHornCoralBlock; + for (int i = 13864; i <= 13865; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 13908; i <= 13915; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 13836; i <= 13837; i++) + materials[i] = Material.DeadTubeCoral; + materials[13826] = Material.DeadTubeCoralBlock; + for (int i = 13856; i <= 13857; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 13876; i <= 13883; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 27634; i <= 27649; i++) + materials[i] = Material.DecoratedPot; + for (int i = 25964; i <= 25966; i++) + materials[i] = Material.Deepslate; + for (int i = 27281; i <= 27286; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 27201; i <= 27280; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 27287; i <= 27610; i++) + materials[i] = Material.DeepslateBrickWall; + materials[27200] = Material.DeepslateBricks; + materials[134] = Material.DeepslateCoalOre; + materials[23971] = Material.DeepslateCopperOre; + materials[4339] = Material.DeepslateDiamondOre; + materials[8296] = Material.DeepslateEmeraldOre; + materials[130] = Material.DeepslateGoldOre; + materials[132] = Material.DeepslateIronOre; + materials[564] = Material.DeepslateLapisOre; + for (int i = 5914; i <= 5915; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 26870; i <= 26875; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 26790; i <= 26869; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 26876; i <= 27199; i++) + materials[i] = Material.DeepslateTileWall; + materials[26789] = Material.DeepslateTiles; + for (int i = 2011; i <= 2034; i++) + materials[i] = Material.DetectorRail; + materials[4340] = Material.DiamondBlock; + materials[4338] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 15177; i <= 15182; i++) + materials[i] = Material.DioriteSlab; + for (int i = 15025; i <= 15104; i++) + materials[i] = Material.DioriteStairs; + for (int i = 19071; i <= 19394; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[13536] = Material.DirtPath; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + materials[8200] = Material.DragonEgg; + for (int i = 9836; i <= 9867; i++) + materials[i] = Material.DragonHead; + for (int i = 9868; i <= 9875; i++) + materials[i] = Material.DragonWallHead; + materials[13810] = Material.DriedKelpBlock; + materials[25796] = Material.DripstoneBlock; + for (int i = 10153; i <= 10164; i++) + materials[i] = Material.Dropper; + materials[8449] = Material.EmeraldBlock; + materials[8295] = Material.EmeraldOre; + materials[8173] = Material.EnchantingTable; + materials[13537] = Material.EndGateway; + materials[8190] = Material.EndPortal; + for (int i = 8191; i <= 8198; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 13357; i <= 13362; i++) + materials[i] = Material.EndRod; + materials[8199] = Material.EndStone; + for (int i = 15135; i <= 15140; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 14385; i <= 14464; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 18747; i <= 19070; i++) + materials[i] = Material.EndStoneBrickWall; + materials[13517] = Material.EndStoneBricks; + for (int i = 8297; i <= 8304; i++) + materials[i] = Material.EnderChest; + materials[23978] = Material.ExposedChiseledCopper; + materials[23967] = Material.ExposedCopper; + for (int i = 25724; i <= 25727; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 24744; i <= 24807; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 25706; i <= 25707; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 25256; i <= 25319; i++) + materials[i] = Material.ExposedCopperTrapdoor; + materials[23974] = Material.ExposedCutCopper; + for (int i = 24316; i <= 24321; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 24144; i <= 24223; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4350; i <= 4357; i++) + materials[i] = Material.Farmland; + materials[2049] = Material.Fern; + for (int i = 2406; i <= 2917; i++) + materials[i] = Material.Fire; + for (int i = 13852; i <= 13853; i++) + materials[i] = Material.FireCoral; + materials[13834] = Material.FireCoralBlock; + for (int i = 13872; i <= 13873; i++) + materials[i] = Material.FireCoralFan; + for (int i = 13940; i <= 13947; i++) + materials[i] = Material.FireCoralWallFan; + materials[27913] = Material.FireflyBush; + materials[19460] = Material.FletchingTable; + materials[9351] = Material.FlowerPot; + materials[25853] = Material.FloweringAzalea; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[27632] = Material.Frogspawn; + for (int i = 13562; i <= 13565; i++) + materials[i] = Material.FrostedIce; + for (int i = 4358; i <= 4365; i++) + materials[i] = Material.Furnace; + materials[21313] = Material.GildedBlackstone; + materials[562] = Material.Glass; + for (int i = 7022; i <= 7053; i++) + materials[i] = Material.GlassPane; + for (int i = 7112; i <= 7239; i++) + materials[i] = Material.GlowLichen; + materials[6042] = Material.Glowstone; + materials[2137] = Material.GoldBlock; + materials[129] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 15153; i <= 15158; i++) + materials[i] = Material.GraniteSlab; + for (int i = 14705; i <= 14784; i++) + materials[i] = Material.GraniteStairs; + for (int i = 16479; i <= 16802; i++) + materials[i] = Material.GraniteWall; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[124] = Material.Gravel; + for (int i = 11760; i <= 11775; i++) + materials[i] = Material.GrayBanner; + for (int i = 1843; i <= 1858; i++) + materials[i] = Material.GrayBed; + for (int i = 21881; i <= 21896; i++) + materials[i] = Material.GrayCandle; + for (int i = 22041; i <= 22042; i++) + materials[i] = Material.GrayCandleCake; + materials[11624] = Material.GrayCarpet; + materials[13758] = Material.GrayConcrete; + materials[13774] = Material.GrayConcretePowder; + for (int i = 13715; i <= 13718; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 13633; i <= 13638; i++) + materials[i] = Material.GrayShulkerBox; + materials[6131] = Material.GrayStainedGlass; + for (int i = 10405; i <= 10436; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[10172] = Material.GrayTerracotta; + for (int i = 11932; i <= 11935; i++) + materials[i] = Material.GrayWallBanner; + materials[2100] = Material.GrayWool; + for (int i = 11856; i <= 11871; i++) + materials[i] = Material.GreenBanner; + for (int i = 1939; i <= 1954; i++) + materials[i] = Material.GreenBed; + for (int i = 21977; i <= 21992; i++) + materials[i] = Material.GreenCandle; + for (int i = 22053; i <= 22054; i++) + materials[i] = Material.GreenCandleCake; + materials[11630] = Material.GreenCarpet; + materials[13764] = Material.GreenConcrete; + materials[13780] = Material.GreenConcretePowder; + for (int i = 13739; i <= 13742; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 13669; i <= 13674; i++) + materials[i] = Material.GreenShulkerBox; + materials[6137] = Material.GreenStainedGlass; + for (int i = 10597; i <= 10628; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[10178] = Material.GreenTerracotta; + for (int i = 11956; i <= 11959; i++) + materials[i] = Material.GreenWallBanner; + materials[2106] = Material.GreenWool; + for (int i = 19461; i <= 19472; i++) + materials[i] = Material.Grindstone; + for (int i = 25960; i <= 25961; i++) + materials[i] = Material.HangingRoots; + for (int i = 11614; i <= 11616; i++) + materials[i] = Material.HayBlock; + for (int i = 27742; i <= 27743; i++) + materials[i] = Material.HeavyCore; + for (int i = 9968; i <= 9983; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[20473] = Material.HoneyBlock; + materials[20474] = Material.HoneycombBlock; + for (int i = 10034; i <= 10043; i++) + materials[i] = Material.Hopper; + for (int i = 13854; i <= 13855; i++) + materials[i] = Material.HornCoral; + materials[13835] = Material.HornCoralBlock; + for (int i = 13874; i <= 13875; i++) + materials[i] = Material.HornCoralFan; + for (int i = 13948; i <= 13955; i++) + materials[i] = Material.HornCoralWallFan; + materials[5958] = Material.Ice; + materials[6791] = Material.InfestedChiseledStoneBricks; + materials[6787] = Material.InfestedCobblestone; + materials[6790] = Material.InfestedCrackedStoneBricks; + for (int i = 27614; i <= 27616; i++) + materials[i] = Material.InfestedDeepslate; + materials[6789] = Material.InfestedMossyStoneBricks; + materials[6786] = Material.InfestedStone; + materials[6788] = Material.InfestedStoneBricks; + for (int i = 6984; i <= 7015; i++) + materials[i] = Material.IronBars; + materials[2138] = Material.IronBlock; + for (int i = 5828; i <= 5891; i++) + materials[i] = Material.IronDoor; + materials[131] = Material.IronOre; + for (int i = 11288; i <= 11351; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 6049; i <= 6052; i++) + materials[i] = Material.JackOLantern; + for (int i = 20383; i <= 20394; i++) + materials[i] = Material.Jigsaw; + for (int i = 5994; i <= 5995; i++) + materials[i] = Material.Jukebox; + for (int i = 9468; i <= 9491; i++) + materials[i] = Material.JungleButton; + for (int i = 12909; i <= 12972; i++) + materials[i] = Material.JungleDoor; + for (int i = 12557; i <= 12588; i++) + materials[i] = Material.JungleFence; + for (int i = 12269; i <= 12300; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 5258; i <= 5321; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5898; i <= 5899; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 4526; i <= 4557; i++) + materials[i] = Material.JungleSign; + for (int i = 12069; i <= 12074; i++) + materials[i] = Material.JungleSlab; + for (int i = 8610; i <= 8689; i++) + materials[i] = Material.JungleStairs; + for (int i = 6332; i <= 6395; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5746; i <= 5753; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4898; i <= 4905; i++) + materials[i] = Material.JungleWallSign; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 13783; i <= 13808; i++) + materials[i] = Material.Kelp; + materials[13809] = Material.KelpPlant; + for (int i = 4750; i <= 4757; i++) + materials[i] = Material.Ladder; + for (int i = 19526; i <= 19529; i++) + materials[i] = Material.Lantern; + materials[565] = Material.LapisBlock; + materials[563] = Material.LapisOre; + for (int i = 22073; i <= 22084; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 11646; i <= 11647; i++) + materials[i] = Material.LargeFern; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + materials[8186] = Material.LavaCauldron; + for (int i = 25887; i <= 25902; i++) + materials[i] = Material.LeafLitter; + for (int i = 19473; i <= 19488; i++) + materials[i] = Material.Lectern; + for (int i = 5802; i <= 5825; i++) + materials[i] = Material.Lever; + for (int i = 11256; i <= 11287; i++) + materials[i] = Material.Light; + for (int i = 11696; i <= 11711; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1779; i <= 1794; i++) + materials[i] = Material.LightBlueBed; + for (int i = 21817; i <= 21832; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 22033; i <= 22034; i++) + materials[i] = Material.LightBlueCandleCake; + materials[11620] = Material.LightBlueCarpet; + materials[13754] = Material.LightBlueConcrete; + materials[13770] = Material.LightBlueConcretePowder; + for (int i = 13699; i <= 13702; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 13609; i <= 13614; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[6127] = Material.LightBlueStainedGlass; + for (int i = 10277; i <= 10308; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[10168] = Material.LightBlueTerracotta; + for (int i = 11916; i <= 11919; i++) + materials[i] = Material.LightBlueWallBanner; + materials[2096] = Material.LightBlueWool; + for (int i = 11776; i <= 11791; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1859; i <= 1874; i++) + materials[i] = Material.LightGrayBed; + for (int i = 21897; i <= 21912; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 22043; i <= 22044; i++) + materials[i] = Material.LightGrayCandleCake; + materials[11625] = Material.LightGrayCarpet; + materials[13759] = Material.LightGrayConcrete; + materials[13775] = Material.LightGrayConcretePowder; + for (int i = 13719; i <= 13722; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 13639; i <= 13644; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[6132] = Material.LightGrayStainedGlass; + for (int i = 10437; i <= 10468; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[10173] = Material.LightGrayTerracotta; + for (int i = 11936; i <= 11939; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2101] = Material.LightGrayWool; + for (int i = 9952; i <= 9967; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 25752; i <= 25775; i++) + materials[i] = Material.LightningRod; + for (int i = 11638; i <= 11639; i++) + materials[i] = Material.Lilac; + materials[2134] = Material.LilyOfTheValley; + materials[7642] = Material.LilyPad; + for (int i = 11728; i <= 11743; i++) + materials[i] = Material.LimeBanner; + for (int i = 1811; i <= 1826; i++) + materials[i] = Material.LimeBed; + for (int i = 21849; i <= 21864; i++) + materials[i] = Material.LimeCandle; + for (int i = 22037; i <= 22038; i++) + materials[i] = Material.LimeCandleCake; + materials[11622] = Material.LimeCarpet; + materials[13756] = Material.LimeConcrete; + materials[13772] = Material.LimeConcretePowder; + for (int i = 13707; i <= 13710; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 13621; i <= 13626; i++) + materials[i] = Material.LimeShulkerBox; + materials[6129] = Material.LimeStainedGlass; + for (int i = 10341; i <= 10372; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[10170] = Material.LimeTerracotta; + for (int i = 11924; i <= 11927; i++) + materials[i] = Material.LimeWallBanner; + materials[2098] = Material.LimeWool; + materials[20487] = Material.Lodestone; + for (int i = 19427; i <= 19430; i++) + materials[i] = Material.Loom; + for (int i = 11680; i <= 11695; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1763; i <= 1778; i++) + materials[i] = Material.MagentaBed; + for (int i = 21801; i <= 21816; i++) + materials[i] = Material.MagentaCandle; + for (int i = 22031; i <= 22032; i++) + materials[i] = Material.MagentaCandleCake; + materials[11619] = Material.MagentaCarpet; + materials[13753] = Material.MagentaConcrete; + materials[13769] = Material.MagentaConcretePowder; + for (int i = 13695; i <= 13698; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 13603; i <= 13608; i++) + materials[i] = Material.MagentaShulkerBox; + materials[6126] = Material.MagentaStainedGlass; + for (int i = 10245; i <= 10276; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[10167] = Material.MagentaTerracotta; + for (int i = 11912; i <= 11915; i++) + materials[i] = Material.MagentaWallBanner; + materials[2095] = Material.MagentaWool; + materials[13566] = Material.MagmaBlock; + for (int i = 9588; i <= 9611; i++) + materials[i] = Material.MangroveButton; + for (int i = 13229; i <= 13292; i++) + materials[i] = Material.MangroveDoor; + for (int i = 12717; i <= 12748; i++) + materials[i] = Material.MangroveFence; + for (int i = 12429; i <= 12460; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5578; i <= 5641; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + materials[26] = Material.MangrovePlanks; + for (int i = 5908; i <= 5909; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4622; i <= 4653; i++) + materials[i] = Material.MangroveSign; + for (int i = 12099; i <= 12104; i++) + materials[i] = Material.MangroveSlab; + for (int i = 11013; i <= 11092; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6652; i <= 6715; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5770; i <= 5777; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4922; i <= 4929; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 22085; i <= 22096; i++) + materials[i] = Material.MediumAmethystBud; + materials[7055] = Material.Melon; + for (int i = 7072; i <= 7079; i++) + materials[i] = Material.MelonStem; + materials[25903] = Material.MossBlock; + materials[25854] = Material.MossCarpet; + materials[2399] = Material.MossyCobblestone; + for (int i = 15129; i <= 15134; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 14305; i <= 14384; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 9027; i <= 9350; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 15117; i <= 15122; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 14145; i <= 14224; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 16155; i <= 16478; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6781] = Material.MossyStoneBricks; + for (int i = 2109; i <= 2120; i++) + materials[i] = Material.MovingPiston; + materials[25963] = Material.Mud; + for (int i = 12165; i <= 12170; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 7560; i <= 7639; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 17127; i <= 17450; i++) + materials[i] = Material.MudBrickWall; + materials[6785] = Material.MudBricks; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6920; i <= 6983; i++) + materials[i] = Material.MushroomStem; + for (int i = 7640; i <= 7641; i++) + materials[i] = Material.Mycelium; + for (int i = 8057; i <= 8088; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 12171; i <= 12176; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 8089; i <= 8168; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 17451; i <= 17774; i++) + materials[i] = Material.NetherBrickWall; + materials[8056] = Material.NetherBricks; + materials[135] = Material.NetherGoldOre; + for (int i = 6043; i <= 6044; i++) + materials[i] = Material.NetherPortal; + materials[10033] = Material.NetherQuartzOre; + materials[19618] = Material.NetherSprouts; + for (int i = 8169; i <= 8172; i++) + materials[i] = Material.NetherWart; + materials[13567] = Material.NetherWartBlock; + materials[20475] = Material.NetheriteBlock; + materials[6028] = Material.Netherrack; + for (int i = 581; i <= 1730; i++) + materials[i] = Material.NoteBlock; + for (int i = 9396; i <= 9419; i++) + materials[i] = Material.OakButton; + for (int i = 4686; i <= 4749; i++) + materials[i] = Material.OakDoor; + for (int i = 5996; i <= 6027; i++) + materials[i] = Material.OakFence; + for (int i = 7368; i <= 7399; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4938; i <= 5001; i++) + materials[i] = Material.OakHangingSign; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5892; i <= 5893; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 4366; i <= 4397; i++) + materials[i] = Material.OakSign; + for (int i = 12051; i <= 12056; i++) + materials[i] = Material.OakSlab; + for (int i = 2938; i <= 3017; i++) + materials[i] = Material.OakStairs; + for (int i = 6140; i <= 6203; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5706; i <= 5713; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4858; i <= 4865; i++) + materials[i] = Material.OakWallSign; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 13573; i <= 13584; i++) + materials[i] = Material.Observer; + materials[2400] = Material.Obsidian; + for (int i = 27623; i <= 27625; i++) + materials[i] = Material.OchreFroglight; + materials[27909] = Material.OpenEyeblossom; + for (int i = 11664; i <= 11679; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1747; i <= 1762; i++) + materials[i] = Material.OrangeBed; + for (int i = 21785; i <= 21800; i++) + materials[i] = Material.OrangeCandle; + for (int i = 22029; i <= 22030; i++) + materials[i] = Material.OrangeCandleCake; + materials[11618] = Material.OrangeCarpet; + materials[13752] = Material.OrangeConcrete; + materials[13768] = Material.OrangeConcretePowder; + for (int i = 13691; i <= 13694; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 13597; i <= 13602; i++) + materials[i] = Material.OrangeShulkerBox; + materials[6125] = Material.OrangeStainedGlass; + for (int i = 10213; i <= 10244; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[10166] = Material.OrangeTerracotta; + materials[2128] = Material.OrangeTulip; + for (int i = 11908; i <= 11911; i++) + materials[i] = Material.OrangeWallBanner; + materials[2094] = Material.OrangeWool; + materials[2131] = Material.OxeyeDaisy; + materials[23976] = Material.OxidizedChiseledCopper; + materials[23969] = Material.OxidizedCopper; + for (int i = 25732; i <= 25735; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 24808; i <= 24871; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 25710; i <= 25711; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 25320; i <= 25383; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + materials[23972] = Material.OxidizedCutCopper; + for (int i = 24304; i <= 24309; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 23984; i <= 24063; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[11635] = Material.PackedIce; + materials[6784] = Material.PackedMud; + for (int i = 27907; i <= 27908; i++) + materials[i] = Material.PaleHangingMoss; + materials[27744] = Material.PaleMossBlock; + for (int i = 27745; i <= 27906; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 9564; i <= 9587; i++) + materials[i] = Material.PaleOakButton; + for (int i = 13165; i <= 13228; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 12685; i <= 12716; i++) + materials[i] = Material.PaleOakFence; + for (int i = 12397; i <= 12428; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 5386; i <= 5449; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + materials[25] = Material.PaleOakPlanks; + for (int i = 5906; i <= 5907; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 4590; i <= 4621; i++) + materials[i] = Material.PaleOakSign; + for (int i = 12093; i <= 12098; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 10933; i <= 11012; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 6588; i <= 6651; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 5762; i <= 5769; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 4914; i <= 4921; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 27629; i <= 27631; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 11642; i <= 11643; i++) + materials[i] = Material.Peony; + for (int i = 12141; i <= 12146; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 9876; i <= 9907; i++) + materials[i] = Material.PiglinHead; + for (int i = 9908; i <= 9915; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 11744; i <= 11759; i++) + materials[i] = Material.PinkBanner; + for (int i = 1827; i <= 1842; i++) + materials[i] = Material.PinkBed; + for (int i = 21865; i <= 21880; i++) + materials[i] = Material.PinkCandle; + for (int i = 22039; i <= 22040; i++) + materials[i] = Material.PinkCandleCake; + materials[11623] = Material.PinkCarpet; + materials[13757] = Material.PinkConcrete; + materials[13773] = Material.PinkConcretePowder; + for (int i = 13711; i <= 13714; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 25855; i <= 25870; i++) + materials[i] = Material.PinkPetals; + for (int i = 13627; i <= 13632; i++) + materials[i] = Material.PinkShulkerBox; + materials[6130] = Material.PinkStainedGlass; + for (int i = 10373; i <= 10404; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[10171] = Material.PinkTerracotta; + materials[2130] = Material.PinkTulip; + for (int i = 11928; i <= 11931; i++) + materials[i] = Material.PinkWallBanner; + materials[2099] = Material.PinkWool; + for (int i = 2057; i <= 2068; i++) + materials[i] = Material.Piston; + for (int i = 2069; i <= 2092; i++) + materials[i] = Material.PistonHead; + for (int i = 13520; i <= 13529; i++) + materials[i] = Material.PitcherCrop; + for (int i = 13530; i <= 13531; i++) + materials[i] = Material.PitcherPlant; + for (int i = 9756; i <= 9787; i++) + materials[i] = Material.PlayerHead; + for (int i = 9788; i <= 9795; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 25776; i <= 25795; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 15171; i <= 15176; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 14945; i <= 15024; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 6034; i <= 6036; i++) + materials[i] = Material.PolishedBasalt; + materials[20899] = Material.PolishedBlackstone; + for (int i = 20903; i <= 20908; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 20909; i <= 20988; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 20989; i <= 21312; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[20900] = Material.PolishedBlackstoneBricks; + for (int i = 21402; i <= 21425; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 21400; i <= 21401; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 21394; i <= 21399; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 21314; i <= 21393; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 21426; i <= 21749; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[26378] = Material.PolishedDeepslate; + for (int i = 26459; i <= 26464; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 26379; i <= 26458; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 26465; i <= 26788; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 15123; i <= 15128; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 14225; i <= 14304; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 15105; i <= 15110; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 13985; i <= 14064; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[22520] = Material.PolishedTuff; + for (int i = 22521; i <= 22526; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 22527; i <= 22606; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 22607; i <= 22930; i++) + materials[i] = Material.PolishedTuffWall; + materials[2123] = Material.Poppy; + for (int i = 9388; i <= 9395; i++) + materials[i] = Material.Potatoes; + materials[9357] = Material.PottedAcaciaSapling; + materials[9366] = Material.PottedAllium; + materials[27621] = Material.PottedAzaleaBush; + materials[9367] = Material.PottedAzureBluet; + materials[13980] = Material.PottedBamboo; + materials[9355] = Material.PottedBirchSapling; + materials[9365] = Material.PottedBlueOrchid; + materials[9377] = Material.PottedBrownMushroom; + materials[9379] = Material.PottedCactus; + materials[9358] = Material.PottedCherrySapling; + materials[27912] = Material.PottedClosedEyeblossom; + materials[9373] = Material.PottedCornflower; + materials[20483] = Material.PottedCrimsonFungus; + materials[20485] = Material.PottedCrimsonRoots; + materials[9363] = Material.PottedDandelion; + materials[9359] = Material.PottedDarkOakSapling; + materials[9378] = Material.PottedDeadBush; + materials[9362] = Material.PottedFern; + materials[27622] = Material.PottedFloweringAzaleaBush; + materials[9356] = Material.PottedJungleSapling; + materials[9374] = Material.PottedLilyOfTheValley; + materials[9361] = Material.PottedMangrovePropagule; + materials[9353] = Material.PottedOakSapling; + materials[27911] = Material.PottedOpenEyeblossom; + materials[9369] = Material.PottedOrangeTulip; + materials[9372] = Material.PottedOxeyeDaisy; + materials[9360] = Material.PottedPaleOakSapling; + materials[9371] = Material.PottedPinkTulip; + materials[9364] = Material.PottedPoppy; + materials[9376] = Material.PottedRedMushroom; + materials[9368] = Material.PottedRedTulip; + materials[9354] = Material.PottedSpruceSapling; + materials[9352] = Material.PottedTorchflower; + materials[20484] = Material.PottedWarpedFungus; + materials[20486] = Material.PottedWarpedRoots; + materials[9370] = Material.PottedWhiteTulip; + materials[9375] = Material.PottedWitherRose; + materials[23346] = Material.PowderSnow; + for (int i = 8187; i <= 8189; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1987; i <= 2010; i++) + materials[i] = Material.PoweredRail; + materials[11352] = Material.Prismarine; + for (int i = 11601; i <= 11606; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 11435; i <= 11514; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[11353] = Material.PrismarineBricks; + for (int i = 11595; i <= 11600; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 11355; i <= 11434; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 15507; i <= 15830; i++) + materials[i] = Material.PrismarineWall; + materials[7054] = Material.Pumpkin; + for (int i = 7064; i <= 7071; i++) + materials[i] = Material.PumpkinStem; + for (int i = 11808; i <= 11823; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1891; i <= 1906; i++) + materials[i] = Material.PurpleBed; + for (int i = 21929; i <= 21944; i++) + materials[i] = Material.PurpleCandle; + for (int i = 22047; i <= 22048; i++) + materials[i] = Material.PurpleCandleCake; + materials[11627] = Material.PurpleCarpet; + materials[13761] = Material.PurpleConcrete; + materials[13777] = Material.PurpleConcretePowder; + for (int i = 13727; i <= 13730; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 13651; i <= 13656; i++) + materials[i] = Material.PurpleShulkerBox; + materials[6134] = Material.PurpleStainedGlass; + for (int i = 10501; i <= 10532; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[10175] = Material.PurpleTerracotta; + for (int i = 11944; i <= 11947; i++) + materials[i] = Material.PurpleWallBanner; + materials[2103] = Material.PurpleWool; + materials[13433] = Material.PurpurBlock; + for (int i = 13434; i <= 13436; i++) + materials[i] = Material.PurpurPillar; + for (int i = 12195; i <= 12200; i++) + materials[i] = Material.PurpurSlab; + for (int i = 13437; i <= 13516; i++) + materials[i] = Material.PurpurStairs; + materials[10044] = Material.QuartzBlock; + materials[21752] = Material.QuartzBricks; + for (int i = 10046; i <= 10048; i++) + materials[i] = Material.QuartzPillar; + for (int i = 12177; i <= 12182; i++) + materials[i] = Material.QuartzSlab; + for (int i = 10049; i <= 10128; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4758; i <= 4777; i++) + materials[i] = Material.Rail; + materials[27619] = Material.RawCopperBlock; + materials[27620] = Material.RawGoldBlock; + materials[27618] = Material.RawIronBlock; + for (int i = 11872; i <= 11887; i++) + materials[i] = Material.RedBanner; + for (int i = 1955; i <= 1970; i++) + materials[i] = Material.RedBed; + for (int i = 21993; i <= 22008; i++) + materials[i] = Material.RedCandle; + for (int i = 22055; i <= 22056; i++) + materials[i] = Material.RedCandleCake; + materials[11631] = Material.RedCarpet; + materials[13765] = Material.RedConcrete; + materials[13781] = Material.RedConcretePowder; + for (int i = 13743; i <= 13746; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2136] = Material.RedMushroom; + for (int i = 6856; i <= 6919; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 15165; i <= 15170; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 14865; i <= 14944; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 18099; i <= 18422; i++) + materials[i] = Material.RedNetherBrickWall; + materials[13568] = Material.RedNetherBricks; + materials[123] = Material.RedSand; + materials[11968] = Material.RedSandstone; + for (int i = 12183; i <= 12188; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 11971; i <= 12050; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 15831; i <= 16154; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 13675; i <= 13680; i++) + materials[i] = Material.RedShulkerBox; + materials[6138] = Material.RedStainedGlass; + for (int i = 10629; i <= 10660; i++) + materials[i] = Material.RedStainedGlassPane; + materials[10179] = Material.RedTerracotta; + materials[2127] = Material.RedTulip; + for (int i = 11960; i <= 11963; i++) + materials[i] = Material.RedWallBanner; + materials[2107] = Material.RedWool; + materials[10032] = Material.RedstoneBlock; + for (int i = 8201; i <= 8202; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5912; i <= 5913; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5916; i <= 5917; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5918; i <= 5925; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 3042; i <= 4337; i++) + materials[i] = Material.RedstoneWire; + materials[27633] = Material.ReinforcedDeepslate; + for (int i = 6060; i <= 6123; i++) + materials[i] = Material.Repeater; + for (int i = 13538; i <= 13549; i++) + materials[i] = Material.RepeatingCommandBlock; + materials[7643] = Material.ResinBlock; + for (int i = 7725; i <= 7730; i++) + materials[i] = Material.ResinBrickSlab; + for (int i = 7645; i <= 7724; i++) + materials[i] = Material.ResinBrickStairs; + for (int i = 7731; i <= 8054; i++) + materials[i] = Material.ResinBrickWall; + materials[7644] = Material.ResinBricks; + for (int i = 7240; i <= 7367; i++) + materials[i] = Material.ResinClump; + for (int i = 20478; i <= 20482; i++) + materials[i] = Material.RespawnAnchor; + materials[25962] = Material.RootedDirt; + for (int i = 11640; i <= 11641; i++) + materials[i] = Material.RoseBush; + materials[118] = Material.Sand; + materials[578] = Material.Sandstone; + for (int i = 12129; i <= 12134; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 8215; i <= 8294; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 18423; i <= 18746; i++) + materials[i] = Material.SandstoneWall; + for (int i = 19395; i <= 19426; i++) + materials[i] = Material.Scaffolding; + materials[23827] = Material.Sculk; + for (int i = 23956; i <= 23957; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 23347; i <= 23442; i++) + materials[i] = Material.SculkSensor; + for (int i = 23958; i <= 23965; i++) + materials[i] = Material.SculkShrieker; + for (int i = 23828; i <= 23955; i++) + materials[i] = Material.SculkVein; + materials[11613] = Material.SeaLantern; + for (int i = 13956; i <= 13963; i++) + materials[i] = Material.SeaPickle; + materials[2054] = Material.Seagrass; + materials[2052] = Material.ShortDryGrass; + materials[2048] = Material.ShortGrass; + materials[19633] = Material.Shroomlight; + for (int i = 13585; i <= 13590; i++) + materials[i] = Material.ShulkerBox; + for (int i = 9636; i <= 9667; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 9668; i <= 9675; i++) + materials[i] = Material.SkeletonWallSkull; + materials[11253] = Material.SlimeBlock; + for (int i = 22097; i <= 22108; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 25944; i <= 25959; i++) + materials[i] = Material.SmallDripleaf; + materials[19489] = Material.SmithingTable; + for (int i = 19443; i <= 19450; i++) + materials[i] = Material.Smoker; + materials[27617] = Material.SmoothBasalt; + materials[12203] = Material.SmoothQuartz; + for (int i = 15147; i <= 15152; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 14625; i <= 14704; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[12204] = Material.SmoothRedSandstone; + for (int i = 15111; i <= 15116; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 14065; i <= 14144; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[12202] = Material.SmoothSandstone; + for (int i = 15141; i <= 15146; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 14545; i <= 14624; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[12201] = Material.SmoothStone; + for (int i = 12123; i <= 12128; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13823; i <= 13825; i++) + materials[i] = Material.SnifferEgg; + for (int i = 5950; i <= 5957; i++) + materials[i] = Material.Snow; + materials[5959] = Material.SnowBlock; + for (int i = 19566; i <= 19597; i++) + materials[i] = Material.SoulCampfire; + materials[2918] = Material.SoulFire; + for (int i = 19530; i <= 19533; i++) + materials[i] = Material.SoulLantern; + materials[6029] = Material.SoulSand; + materials[6030] = Material.SoulSoil; + materials[6037] = Material.SoulTorch; + for (int i = 6038; i <= 6041; i++) + materials[i] = Material.SoulWallTorch; + materials[2919] = Material.Spawner; + materials[560] = Material.Sponge; + materials[25851] = Material.SporeBlossom; + for (int i = 9420; i <= 9443; i++) + materials[i] = Material.SpruceButton; + for (int i = 12781; i <= 12844; i++) + materials[i] = Material.SpruceDoor; + for (int i = 12493; i <= 12524; i++) + materials[i] = Material.SpruceFence; + for (int i = 12205; i <= 12236; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 5002; i <= 5065; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5894; i <= 5895; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4398; i <= 4429; i++) + materials[i] = Material.SpruceSign; + for (int i = 12057; i <= 12062; i++) + materials[i] = Material.SpruceSlab; + for (int i = 8450; i <= 8529; i++) + materials[i] = Material.SpruceStairs; + for (int i = 6204; i <= 6267; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5714; i <= 5721; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4866; i <= 4873; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 2035; i <= 2046; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 12159; i <= 12164; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 7480; i <= 7559; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 16803; i <= 17126; i++) + materials[i] = Material.StoneBrickWall; + materials[6780] = Material.StoneBricks; + for (int i = 5926; i <= 5949; i++) + materials[i] = Material.StoneButton; + for (int i = 5826; i <= 5827; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 12117; i <= 12122; i++) + materials[i] = Material.StoneSlab; + for (int i = 14465; i <= 14544; i++) + materials[i] = Material.StoneStairs; + for (int i = 19490; i <= 19493; i++) + materials[i] = Material.Stonecutter; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 19628; i <= 19630; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 19622; i <= 19624; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 19611; i <= 19613; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 19605; i <= 19607; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 20379; i <= 20382; i++) + materials[i] = Material.StructureBlock; + materials[13572] = Material.StructureVoid; + for (int i = 5978; i <= 5993; i++) + materials[i] = Material.SugarCane; + for (int i = 11636; i <= 11637; i++) + materials[i] = Material.Sunflower; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 19598; i <= 19601; i++) + materials[i] = Material.SweetBerryBush; + materials[2053] = Material.TallDryGrass; + for (int i = 11644; i <= 11645; i++) + materials[i] = Material.TallGrass; + for (int i = 2055; i <= 2056; i++) + materials[i] = Material.TallSeagrass; + for (int i = 20409; i <= 20424; i++) + materials[i] = Material.Target; + materials[11633] = Material.Terracotta; + for (int i = 20395; i <= 20398; i++) + materials[i] = Material.TestBlock; + materials[20399] = Material.TestInstanceBlock; + materials[23345] = Material.TintedGlass; + for (int i = 2140; i <= 2141; i++) + materials[i] = Material.Tnt; + materials[2401] = Material.Torch; + materials[2122] = Material.Torchflower; + for (int i = 13518; i <= 13519; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 9928; i <= 9951; i++) + materials[i] = Material.TrappedChest; + for (int i = 27698; i <= 27709; i++) + materials[i] = Material.TrialSpawner; + for (int i = 8321; i <= 8448; i++) + materials[i] = Material.Tripwire; + for (int i = 8305; i <= 8320; i++) + materials[i] = Material.TripwireHook; + for (int i = 13846; i <= 13847; i++) + materials[i] = Material.TubeCoral; + materials[13831] = Material.TubeCoralBlock; + for (int i = 13866; i <= 13867; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 13916; i <= 13923; i++) + materials[i] = Material.TubeCoralWallFan; + materials[22109] = Material.Tuff; + for (int i = 22933; i <= 22938; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 22939; i <= 23018; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 23019; i <= 23342; i++) + materials[i] = Material.TuffBrickWall; + materials[22932] = Material.TuffBricks; + for (int i = 22110; i <= 22115; i++) + materials[i] = Material.TuffSlab; + for (int i = 22116; i <= 22195; i++) + materials[i] = Material.TuffStairs; + for (int i = 22196; i <= 22519; i++) + materials[i] = Material.TuffWall; + for (int i = 13811; i <= 13822; i++) + materials[i] = Material.TurtleEgg; + for (int i = 19661; i <= 19686; i++) + materials[i] = Material.TwistingVines; + materials[19687] = Material.TwistingVinesPlant; + for (int i = 27710; i <= 27741; i++) + materials[i] = Material.Vault; + for (int i = 27626; i <= 27628; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 7080; i <= 7111; i++) + materials[i] = Material.Vine; + materials[13981] = Material.VoidAir; + for (int i = 2402; i <= 2405; i++) + materials[i] = Material.WallTorch; + for (int i = 20147; i <= 20170; i++) + materials[i] = Material.WarpedButton; + for (int i = 20235; i <= 20298; i++) + materials[i] = Material.WarpedDoor; + for (int i = 19739; i <= 19770; i++) + materials[i] = Material.WarpedFence; + for (int i = 19931; i <= 19962; i++) + materials[i] = Material.WarpedFenceGate; + materials[19615] = Material.WarpedFungus; + for (int i = 5514; i <= 5577; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 19608; i <= 19610; i++) + materials[i] = Material.WarpedHyphae; + materials[19614] = Material.WarpedNylium; + materials[19690] = Material.WarpedPlanks; + for (int i = 19705; i <= 19706; i++) + materials[i] = Material.WarpedPressurePlate; + materials[19617] = Material.WarpedRoots; + for (int i = 20331; i <= 20362; i++) + materials[i] = Material.WarpedSign; + for (int i = 19697; i <= 19702; i++) + materials[i] = Material.WarpedSlab; + for (int i = 20043; i <= 20122; i++) + materials[i] = Material.WarpedStairs; + for (int i = 19602; i <= 19604; i++) + materials[i] = Material.WarpedStem; + for (int i = 19835; i <= 19898; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5786; i <= 5793; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 20371; i <= 20378; i++) + materials[i] = Material.WarpedWallSign; + materials[19616] = Material.WarpedWartBlock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 8183; i <= 8185; i++) + materials[i] = Material.WaterCauldron; + materials[23983] = Material.WaxedChiseledCopper; + materials[24328] = Material.WaxedCopperBlock; + for (int i = 25736; i <= 25739; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 24936; i <= 24999; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 25712; i <= 25713; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 25448; i <= 25511; i++) + materials[i] = Material.WaxedCopperTrapdoor; + materials[24335] = Material.WaxedCutCopper; + for (int i = 24674; i <= 24679; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 24576; i <= 24655; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[23982] = Material.WaxedExposedChiseledCopper; + materials[24330] = Material.WaxedExposedCopper; + for (int i = 25740; i <= 25743; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 25000; i <= 25063; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 25714; i <= 25715; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 25512; i <= 25575; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + materials[24334] = Material.WaxedExposedCutCopper; + for (int i = 24668; i <= 24673; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 24496; i <= 24575; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[23980] = Material.WaxedOxidizedChiseledCopper; + materials[24331] = Material.WaxedOxidizedCopper; + for (int i = 25748; i <= 25751; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 25064; i <= 25127; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 25718; i <= 25719; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 25576; i <= 25639; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + materials[24332] = Material.WaxedOxidizedCutCopper; + for (int i = 24656; i <= 24661; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 24336; i <= 24415; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[23981] = Material.WaxedWeatheredChiseledCopper; + materials[24329] = Material.WaxedWeatheredCopper; + for (int i = 25744; i <= 25747; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 25128; i <= 25191; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 25716; i <= 25717; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 25640; i <= 25703; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + materials[24333] = Material.WaxedWeatheredCutCopper; + for (int i = 24662; i <= 24667; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 24416; i <= 24495; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[23977] = Material.WeatheredChiseledCopper; + materials[23968] = Material.WeatheredCopper; + for (int i = 25728; i <= 25731; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 24872; i <= 24935; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 25708; i <= 25709; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 25384; i <= 25447; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + materials[23973] = Material.WeatheredCutCopper; + for (int i = 24310; i <= 24315; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 24064; i <= 24143; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 19634; i <= 19659; i++) + materials[i] = Material.WeepingVines; + materials[19660] = Material.WeepingVinesPlant; + materials[561] = Material.WetSponge; + for (int i = 4342; i <= 4349; i++) + materials[i] = Material.Wheat; + for (int i = 11648; i <= 11663; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1731; i <= 1746; i++) + materials[i] = Material.WhiteBed; + for (int i = 21769; i <= 21784; i++) + materials[i] = Material.WhiteCandle; + for (int i = 22027; i <= 22028; i++) + materials[i] = Material.WhiteCandleCake; + materials[11617] = Material.WhiteCarpet; + materials[13751] = Material.WhiteConcrete; + materials[13767] = Material.WhiteConcretePowder; + for (int i = 13687; i <= 13690; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 13591; i <= 13596; i++) + materials[i] = Material.WhiteShulkerBox; + materials[6124] = Material.WhiteStainedGlass; + for (int i = 10181; i <= 10212; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[10165] = Material.WhiteTerracotta; + materials[2129] = Material.WhiteTulip; + for (int i = 11904; i <= 11907; i++) + materials[i] = Material.WhiteWallBanner; + materials[2093] = Material.WhiteWool; + for (int i = 25871; i <= 25886; i++) + materials[i] = Material.Wildflowers; + materials[2133] = Material.WitherRose; + for (int i = 9676; i <= 9707; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 9708; i <= 9715; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 11712; i <= 11727; i++) + materials[i] = Material.YellowBanner; + for (int i = 1795; i <= 1810; i++) + materials[i] = Material.YellowBed; + for (int i = 21833; i <= 21848; i++) + materials[i] = Material.YellowCandle; + for (int i = 22035; i <= 22036; i++) + materials[i] = Material.YellowCandleCake; + materials[11621] = Material.YellowCarpet; + materials[13755] = Material.YellowConcrete; + materials[13771] = Material.YellowConcretePowder; + for (int i = 13703; i <= 13706; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 13615; i <= 13620; i++) + materials[i] = Material.YellowShulkerBox; + materials[6128] = Material.YellowStainedGlass; + for (int i = 10309; i <= 10340; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[10169] = Material.YellowTerracotta; + for (int i = 11920; i <= 11923; i++) + materials[i] = Material.YellowWallBanner; + materials[2097] = Material.YellowWool; + for (int i = 9716; i <= 9747; i++) + materials[i] = Material.ZombieHead; + for (int i = 9748; i <= 9755; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1216.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1216.cs new file mode 100644 index 00000000..f235183c --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1216.cs @@ -0,0 +1,1840 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1216 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1216() + { + for (int i = 9492; i <= 9515; i++) + materials[i] = Material.AcaciaButton; + for (int i = 12973; i <= 13036; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 12589; i <= 12620; i++) + materials[i] = Material.AcaciaFence; + for (int i = 12301; i <= 12332; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 5130; i <= 5193; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5900; i <= 5901; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4462; i <= 4493; i++) + materials[i] = Material.AcaciaSign; + for (int i = 12075; i <= 12080; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 10693; i <= 10772; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6396; i <= 6459; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5730; i <= 5737; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4882; i <= 4889; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 10129; i <= 10152; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2125] = Material.Allium; + materials[22091] = Material.AmethystBlock; + for (int i = 22093; i <= 22104; i++) + materials[i] = Material.AmethystCluster; + materials[20508] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 15191; i <= 15196; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 14817; i <= 14896; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 17807; i <= 18130; i++) + materials[i] = Material.AndesiteWall; + for (int i = 9916; i <= 9919; i++) + materials[i] = Material.Anvil; + for (int i = 7060; i <= 7063; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 7056; i <= 7059; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[25884] = Material.Azalea; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + materials[2126] = Material.AzureBluet; + for (int i = 14000; i <= 14011; i++) + materials[i] = Material.Bamboo; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 9612; i <= 9635; i++) + materials[i] = Material.BambooButton; + for (int i = 13293; i <= 13356; i++) + materials[i] = Material.BambooDoor; + for (int i = 12749; i <= 12780; i++) + materials[i] = Material.BambooFence; + for (int i = 12461; i <= 12492; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5642; i <= 5705; i++) + materials[i] = Material.BambooHangingSign; + materials[28] = Material.BambooMosaic; + for (int i = 12111; i <= 12116; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 11173; i <= 11252; i++) + materials[i] = Material.BambooMosaicStairs; + materials[27] = Material.BambooPlanks; + for (int i = 5910; i <= 5911; i++) + materials[i] = Material.BambooPressurePlate; + materials[13999] = Material.BambooSapling; + for (int i = 4654; i <= 4685; i++) + materials[i] = Material.BambooSign; + for (int i = 12105; i <= 12110; i++) + materials[i] = Material.BambooSlab; + for (int i = 11093; i <= 11172; i++) + materials[i] = Material.BambooStairs; + for (int i = 6716; i <= 6779; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5794; i <= 5801; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4930; i <= 4937; i++) + materials[i] = Material.BambooWallSign; + for (int i = 19463; i <= 19474; i++) + materials[i] = Material.Barrel; + for (int i = 11254; i <= 11255; i++) + materials[i] = Material.Barrier; + for (int i = 6031; i <= 6033; i++) + materials[i] = Material.Basalt; + materials[8702] = Material.Beacon; + materials[85] = Material.Bedrock; + for (int i = 20457; i <= 20480; i++) + materials[i] = Material.BeeNest; + for (int i = 20481; i <= 20504; i++) + materials[i] = Material.Beehive; + for (int i = 13532; i <= 13535; i++) + materials[i] = Material.Beetroots; + for (int i = 19526; i <= 19557; i++) + materials[i] = Material.Bell; + for (int i = 25936; i <= 25967; i++) + materials[i] = Material.BigDripleaf; + for (int i = 25968; i <= 25975; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 9444; i <= 9467; i++) + materials[i] = Material.BirchButton; + for (int i = 12845; i <= 12908; i++) + materials[i] = Material.BirchDoor; + for (int i = 12525; i <= 12556; i++) + materials[i] = Material.BirchFence; + for (int i = 12237; i <= 12268; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 5066; i <= 5129; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5896; i <= 5897; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 4430; i <= 4461; i++) + materials[i] = Material.BirchSign; + for (int i = 12063; i <= 12068; i++) + materials[i] = Material.BirchSlab; + for (int i = 8530; i <= 8609; i++) + materials[i] = Material.BirchStairs; + for (int i = 6268; i <= 6331; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5722; i <= 5729; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4874; i <= 4881; i++) + materials[i] = Material.BirchWallSign; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 11888; i <= 11903; i++) + materials[i] = Material.BlackBanner; + for (int i = 1971; i <= 1986; i++) + materials[i] = Material.BlackBed; + for (int i = 22041; i <= 22056; i++) + materials[i] = Material.BlackCandle; + for (int i = 22089; i <= 22090; i++) + materials[i] = Material.BlackCandleCake; + materials[11632] = Material.BlackCarpet; + materials[13766] = Material.BlackConcrete; + materials[13782] = Material.BlackConcretePowder; + for (int i = 13747; i <= 13750; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 13681; i <= 13686; i++) + materials[i] = Material.BlackShulkerBox; + materials[6139] = Material.BlackStainedGlass; + for (int i = 10661; i <= 10692; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[10180] = Material.BlackTerracotta; + for (int i = 11964; i <= 11967; i++) + materials[i] = Material.BlackWallBanner; + materials[2108] = Material.BlackWool; + materials[20520] = Material.Blackstone; + for (int i = 20925; i <= 20930; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 20521; i <= 20600; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 20601; i <= 20924; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 19483; i <= 19490; i++) + materials[i] = Material.BlastFurnace; + for (int i = 11824; i <= 11839; i++) + materials[i] = Material.BlueBanner; + for (int i = 1907; i <= 1922; i++) + materials[i] = Material.BlueBed; + for (int i = 21977; i <= 21992; i++) + materials[i] = Material.BlueCandle; + for (int i = 22081; i <= 22082; i++) + materials[i] = Material.BlueCandleCake; + materials[11628] = Material.BlueCarpet; + materials[13762] = Material.BlueConcrete; + materials[13778] = Material.BlueConcretePowder; + for (int i = 13731; i <= 13734; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[13996] = Material.BlueIce; + materials[2124] = Material.BlueOrchid; + for (int i = 13657; i <= 13662; i++) + materials[i] = Material.BlueShulkerBox; + materials[6135] = Material.BlueStainedGlass; + for (int i = 10533; i <= 10564; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[10176] = Material.BlueTerracotta; + for (int i = 11948; i <= 11951; i++) + materials[i] = Material.BlueWallBanner; + materials[2104] = Material.BlueWool; + for (int i = 13569; i <= 13571; i++) + materials[i] = Material.BoneBlock; + materials[2142] = Material.Bookshelf; + for (int i = 13880; i <= 13881; i++) + materials[i] = Material.BrainCoral; + materials[13864] = Material.BrainCoralBlock; + for (int i = 13900; i <= 13901; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 13956; i <= 13963; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 8174; i <= 8181; i++) + materials[i] = Material.BrewingStand; + for (int i = 12153; i <= 12158; i++) + materials[i] = Material.BrickSlab; + for (int i = 7400; i <= 7479; i++) + materials[i] = Material.BrickStairs; + for (int i = 15215; i <= 15538; i++) + materials[i] = Material.BrickWall; + materials[2139] = Material.Bricks; + for (int i = 11840; i <= 11855; i++) + materials[i] = Material.BrownBanner; + for (int i = 1923; i <= 1938; i++) + materials[i] = Material.BrownBed; + for (int i = 21993; i <= 22008; i++) + materials[i] = Material.BrownCandle; + for (int i = 22083; i <= 22084; i++) + materials[i] = Material.BrownCandleCake; + materials[11629] = Material.BrownCarpet; + materials[13763] = Material.BrownConcrete; + materials[13779] = Material.BrownConcretePowder; + for (int i = 13735; i <= 13738; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2135] = Material.BrownMushroom; + for (int i = 6792; i <= 6855; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 13663; i <= 13668; i++) + materials[i] = Material.BrownShulkerBox; + materials[6136] = Material.BrownStainedGlass; + for (int i = 10565; i <= 10596; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[10177] = Material.BrownTerracotta; + for (int i = 11952; i <= 11955; i++) + materials[i] = Material.BrownWallBanner; + materials[2105] = Material.BrownWool; + for (int i = 14015; i <= 14016; i++) + materials[i] = Material.BubbleColumn; + for (int i = 13882; i <= 13883; i++) + materials[i] = Material.BubbleCoral; + materials[13865] = Material.BubbleCoralBlock; + for (int i = 13902; i <= 13903; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 13964; i <= 13971; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[22092] = Material.BuddingAmethyst; + materials[2051] = Material.Bush; + for (int i = 5960; i <= 5975; i++) + materials[i] = Material.Cactus; + materials[5976] = Material.CactusFlower; + for (int i = 6053; i <= 6059; i++) + materials[i] = Material.Cake; + materials[23376] = Material.Calcite; + for (int i = 23475; i <= 23858; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 19566; i <= 19597; i++) + materials[i] = Material.Campfire; + for (int i = 21785; i <= 21800; i++) + materials[i] = Material.Candle; + for (int i = 22057; i <= 22058; i++) + materials[i] = Material.CandleCake; + for (int i = 9380; i <= 9387; i++) + materials[i] = Material.Carrots; + materials[19491] = Material.CartographyTable; + for (int i = 6045; i <= 6048; i++) + materials[i] = Material.CarvedPumpkin; + materials[8182] = Material.Cauldron; + materials[14014] = Material.CaveAir; + for (int i = 25829; i <= 25880; i++) + materials[i] = Material.CaveVines; + for (int i = 25881; i <= 25882; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 7016; i <= 7021; i++) + materials[i] = Material.Chain; + for (int i = 13550; i <= 13561; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 9516; i <= 9539; i++) + materials[i] = Material.CherryButton; + for (int i = 13037; i <= 13100; i++) + materials[i] = Material.CherryDoor; + for (int i = 12621; i <= 12652; i++) + materials[i] = Material.CherryFence; + for (int i = 12333; i <= 12364; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 5194; i <= 5257; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + materials[20] = Material.CherryPlanks; + for (int i = 5902; i <= 5903; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 4494; i <= 4525; i++) + materials[i] = Material.CherrySign; + for (int i = 12081; i <= 12086; i++) + materials[i] = Material.CherrySlab; + for (int i = 10773; i <= 10852; i++) + materials[i] = Material.CherryStairs; + for (int i = 6460; i <= 6523; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 5738; i <= 5745; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 4890; i <= 4897; i++) + materials[i] = Material.CherryWallSign; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 3018; i <= 3041; i++) + materials[i] = Material.Chest; + for (int i = 9920; i <= 9923; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2143; i <= 2398; i++) + materials[i] = Material.ChiseledBookshelf; + materials[24011] = Material.ChiseledCopper; + materials[27643] = Material.ChiseledDeepslate; + materials[21782] = Material.ChiseledNetherBricks; + materials[20934] = Material.ChiseledPolishedBlackstone; + materials[10045] = Material.ChiseledQuartzBlock; + materials[11969] = Material.ChiseledRedSandstone; + materials[8055] = Material.ChiseledResinBricks; + materials[579] = Material.ChiseledSandstone; + materials[6783] = Material.ChiseledStoneBricks; + materials[22963] = Material.ChiseledTuff; + materials[23375] = Material.ChiseledTuffBricks; + for (int i = 13427; i <= 13432; i++) + materials[i] = Material.ChorusFlower; + for (int i = 13363; i <= 13426; i++) + materials[i] = Material.ChorusPlant; + materials[5977] = Material.Clay; + materials[27942] = Material.ClosedEyeblossom; + materials[11634] = Material.CoalBlock; + materials[133] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[25999] = Material.CobbledDeepslate; + for (int i = 26080; i <= 26085; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 26000; i <= 26079; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 26086; i <= 26409; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 12147; i <= 12152; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4778; i <= 4857; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 8703; i <= 9026; i++) + materials[i] = Material.CobblestoneWall; + materials[2047] = Material.Cobweb; + for (int i = 8203; i <= 8214; i++) + materials[i] = Material.Cocoa; + for (int i = 8690; i <= 8701; i++) + materials[i] = Material.CommandBlock; + for (int i = 9984; i <= 9999; i++) + materials[i] = Material.Comparator; + for (int i = 20432; i <= 20440; i++) + materials[i] = Material.Composter; + for (int i = 13997; i <= 13998; i++) + materials[i] = Material.Conduit; + materials[23998] = Material.CopperBlock; + for (int i = 25752; i <= 25755; i++) + materials[i] = Material.CopperBulb; + for (int i = 24712; i <= 24775; i++) + materials[i] = Material.CopperDoor; + for (int i = 25736; i <= 25737; i++) + materials[i] = Material.CopperGrate; + materials[24002] = Material.CopperOre; + for (int i = 25224; i <= 25287; i++) + materials[i] = Material.CopperTrapdoor; + materials[2132] = Material.Cornflower; + materials[27644] = Material.CrackedDeepslateBricks; + materials[27645] = Material.CrackedDeepslateTiles; + materials[21783] = Material.CrackedNetherBricks; + materials[20933] = Material.CrackedPolishedBlackstoneBricks; + materials[6782] = Material.CrackedStoneBricks; + for (int i = 27682; i <= 27729; i++) + materials[i] = Material.Crafter; + materials[4341] = Material.CraftingTable; + for (int i = 2920; i <= 2937; i++) + materials[i] = Material.CreakingHeart; + for (int i = 9796; i <= 9827; i++) + materials[i] = Material.CreeperHead; + for (int i = 9828; i <= 9835; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 20155; i <= 20178; i++) + materials[i] = Material.CrimsonButton; + for (int i = 20203; i <= 20266; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 19739; i <= 19770; i++) + materials[i] = Material.CrimsonFence; + for (int i = 19931; i <= 19962; i++) + materials[i] = Material.CrimsonFenceGate; + materials[19664] = Material.CrimsonFungus; + for (int i = 5450; i <= 5513; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 19657; i <= 19659; i++) + materials[i] = Material.CrimsonHyphae; + materials[19663] = Material.CrimsonNylium; + materials[19721] = Material.CrimsonPlanks; + for (int i = 19735; i <= 19736; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[19720] = Material.CrimsonRoots; + for (int i = 20331; i <= 20362; i++) + materials[i] = Material.CrimsonSign; + for (int i = 19723; i <= 19728; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 19995; i <= 20074; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 19651; i <= 19653; i++) + materials[i] = Material.CrimsonStem; + for (int i = 19803; i <= 19866; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5778; i <= 5785; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 20395; i <= 20402; i++) + materials[i] = Material.CrimsonWallSign; + materials[20509] = Material.CryingObsidian; + materials[24007] = Material.CutCopper; + for (int i = 24354; i <= 24359; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 24256; i <= 24335; i++) + materials[i] = Material.CutCopperStairs; + materials[11970] = Material.CutRedSandstone; + for (int i = 12189; i <= 12194; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[580] = Material.CutSandstone; + for (int i = 12135; i <= 12140; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 11792; i <= 11807; i++) + materials[i] = Material.CyanBanner; + for (int i = 1875; i <= 1890; i++) + materials[i] = Material.CyanBed; + for (int i = 21945; i <= 21960; i++) + materials[i] = Material.CyanCandle; + for (int i = 22077; i <= 22078; i++) + materials[i] = Material.CyanCandleCake; + materials[11626] = Material.CyanCarpet; + materials[13760] = Material.CyanConcrete; + materials[13776] = Material.CyanConcretePowder; + for (int i = 13723; i <= 13726; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 13645; i <= 13650; i++) + materials[i] = Material.CyanShulkerBox; + materials[6133] = Material.CyanStainedGlass; + for (int i = 10469; i <= 10500; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[10174] = Material.CyanTerracotta; + for (int i = 11940; i <= 11943; i++) + materials[i] = Material.CyanWallBanner; + materials[2102] = Material.CyanWool; + for (int i = 9924; i <= 9927; i++) + materials[i] = Material.DamagedAnvil; + materials[2121] = Material.Dandelion; + for (int i = 9540; i <= 9563; i++) + materials[i] = Material.DarkOakButton; + for (int i = 13101; i <= 13164; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 12653; i <= 12684; i++) + materials[i] = Material.DarkOakFence; + for (int i = 12365; i <= 12396; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5322; i <= 5385; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + materials[21] = Material.DarkOakPlanks; + for (int i = 5904; i <= 5905; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4558; i <= 4589; i++) + materials[i] = Material.DarkOakSign; + for (int i = 12087; i <= 12092; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 10853; i <= 10932; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6524; i <= 6587; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5754; i <= 5761; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4906; i <= 4913; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + materials[11354] = Material.DarkPrismarine; + for (int i = 11607; i <= 11612; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 11515; i <= 11594; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 10000; i <= 10031; i++) + materials[i] = Material.DaylightDetector; + for (int i = 13870; i <= 13871; i++) + materials[i] = Material.DeadBrainCoral; + materials[13859] = Material.DeadBrainCoralBlock; + for (int i = 13890; i <= 13891; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 13916; i <= 13923; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 13872; i <= 13873; i++) + materials[i] = Material.DeadBubbleCoral; + materials[13860] = Material.DeadBubbleCoralBlock; + for (int i = 13892; i <= 13893; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 13924; i <= 13931; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[2050] = Material.DeadBush; + for (int i = 13874; i <= 13875; i++) + materials[i] = Material.DeadFireCoral; + materials[13861] = Material.DeadFireCoralBlock; + for (int i = 13894; i <= 13895; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 13932; i <= 13939; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 13876; i <= 13877; i++) + materials[i] = Material.DeadHornCoral; + materials[13862] = Material.DeadHornCoralBlock; + for (int i = 13896; i <= 13897; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 13940; i <= 13947; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 13868; i <= 13869; i++) + materials[i] = Material.DeadTubeCoral; + materials[13858] = Material.DeadTubeCoralBlock; + for (int i = 13888; i <= 13889; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 13908; i <= 13915; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 27666; i <= 27681; i++) + materials[i] = Material.DecoratedPot; + for (int i = 25996; i <= 25998; i++) + materials[i] = Material.Deepslate; + for (int i = 27313; i <= 27318; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 27233; i <= 27312; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 27319; i <= 27642; i++) + materials[i] = Material.DeepslateBrickWall; + materials[27232] = Material.DeepslateBricks; + materials[134] = Material.DeepslateCoalOre; + materials[24003] = Material.DeepslateCopperOre; + materials[4339] = Material.DeepslateDiamondOre; + materials[8296] = Material.DeepslateEmeraldOre; + materials[130] = Material.DeepslateGoldOre; + materials[132] = Material.DeepslateIronOre; + materials[564] = Material.DeepslateLapisOre; + for (int i = 5914; i <= 5915; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 26902; i <= 26907; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 26822; i <= 26901; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 26908; i <= 27231; i++) + materials[i] = Material.DeepslateTileWall; + materials[26821] = Material.DeepslateTiles; + for (int i = 2011; i <= 2034; i++) + materials[i] = Material.DetectorRail; + materials[4340] = Material.DiamondBlock; + materials[4338] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 15209; i <= 15214; i++) + materials[i] = Material.DioriteSlab; + for (int i = 15057; i <= 15136; i++) + materials[i] = Material.DioriteStairs; + for (int i = 19103; i <= 19426; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[13536] = Material.DirtPath; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + materials[8200] = Material.DragonEgg; + for (int i = 9836; i <= 9867; i++) + materials[i] = Material.DragonHead; + for (int i = 9868; i <= 9875; i++) + materials[i] = Material.DragonWallHead; + for (int i = 13826; i <= 13857; i++) + materials[i] = Material.DriedGhast; + materials[13810] = Material.DriedKelpBlock; + materials[25828] = Material.DripstoneBlock; + for (int i = 10153; i <= 10164; i++) + materials[i] = Material.Dropper; + materials[8449] = Material.EmeraldBlock; + materials[8295] = Material.EmeraldOre; + materials[8173] = Material.EnchantingTable; + materials[13537] = Material.EndGateway; + materials[8190] = Material.EndPortal; + for (int i = 8191; i <= 8198; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 13357; i <= 13362; i++) + materials[i] = Material.EndRod; + materials[8199] = Material.EndStone; + for (int i = 15167; i <= 15172; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 14417; i <= 14496; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 18779; i <= 19102; i++) + materials[i] = Material.EndStoneBrickWall; + materials[13517] = Material.EndStoneBricks; + for (int i = 8297; i <= 8304; i++) + materials[i] = Material.EnderChest; + materials[24010] = Material.ExposedChiseledCopper; + materials[23999] = Material.ExposedCopper; + for (int i = 25756; i <= 25759; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 24776; i <= 24839; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 25738; i <= 25739; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 25288; i <= 25351; i++) + materials[i] = Material.ExposedCopperTrapdoor; + materials[24006] = Material.ExposedCutCopper; + for (int i = 24348; i <= 24353; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 24176; i <= 24255; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4350; i <= 4357; i++) + materials[i] = Material.Farmland; + materials[2049] = Material.Fern; + for (int i = 2406; i <= 2917; i++) + materials[i] = Material.Fire; + for (int i = 13884; i <= 13885; i++) + materials[i] = Material.FireCoral; + materials[13866] = Material.FireCoralBlock; + for (int i = 13904; i <= 13905; i++) + materials[i] = Material.FireCoralFan; + for (int i = 13972; i <= 13979; i++) + materials[i] = Material.FireCoralWallFan; + materials[27945] = Material.FireflyBush; + materials[19492] = Material.FletchingTable; + materials[9351] = Material.FlowerPot; + materials[25885] = Material.FloweringAzalea; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[27664] = Material.Frogspawn; + for (int i = 13562; i <= 13565; i++) + materials[i] = Material.FrostedIce; + for (int i = 4358; i <= 4365; i++) + materials[i] = Material.Furnace; + materials[21345] = Material.GildedBlackstone; + materials[562] = Material.Glass; + for (int i = 7022; i <= 7053; i++) + materials[i] = Material.GlassPane; + for (int i = 7112; i <= 7239; i++) + materials[i] = Material.GlowLichen; + materials[6042] = Material.Glowstone; + materials[2137] = Material.GoldBlock; + materials[129] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 15185; i <= 15190; i++) + materials[i] = Material.GraniteSlab; + for (int i = 14737; i <= 14816; i++) + materials[i] = Material.GraniteStairs; + for (int i = 16511; i <= 16834; i++) + materials[i] = Material.GraniteWall; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[124] = Material.Gravel; + for (int i = 11760; i <= 11775; i++) + materials[i] = Material.GrayBanner; + for (int i = 1843; i <= 1858; i++) + materials[i] = Material.GrayBed; + for (int i = 21913; i <= 21928; i++) + materials[i] = Material.GrayCandle; + for (int i = 22073; i <= 22074; i++) + materials[i] = Material.GrayCandleCake; + materials[11624] = Material.GrayCarpet; + materials[13758] = Material.GrayConcrete; + materials[13774] = Material.GrayConcretePowder; + for (int i = 13715; i <= 13718; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 13633; i <= 13638; i++) + materials[i] = Material.GrayShulkerBox; + materials[6131] = Material.GrayStainedGlass; + for (int i = 10405; i <= 10436; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[10172] = Material.GrayTerracotta; + for (int i = 11932; i <= 11935; i++) + materials[i] = Material.GrayWallBanner; + materials[2100] = Material.GrayWool; + for (int i = 11856; i <= 11871; i++) + materials[i] = Material.GreenBanner; + for (int i = 1939; i <= 1954; i++) + materials[i] = Material.GreenBed; + for (int i = 22009; i <= 22024; i++) + materials[i] = Material.GreenCandle; + for (int i = 22085; i <= 22086; i++) + materials[i] = Material.GreenCandleCake; + materials[11630] = Material.GreenCarpet; + materials[13764] = Material.GreenConcrete; + materials[13780] = Material.GreenConcretePowder; + for (int i = 13739; i <= 13742; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 13669; i <= 13674; i++) + materials[i] = Material.GreenShulkerBox; + materials[6137] = Material.GreenStainedGlass; + for (int i = 10597; i <= 10628; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[10178] = Material.GreenTerracotta; + for (int i = 11956; i <= 11959; i++) + materials[i] = Material.GreenWallBanner; + materials[2106] = Material.GreenWool; + for (int i = 19493; i <= 19504; i++) + materials[i] = Material.Grindstone; + for (int i = 25992; i <= 25993; i++) + materials[i] = Material.HangingRoots; + for (int i = 11614; i <= 11616; i++) + materials[i] = Material.HayBlock; + for (int i = 27774; i <= 27775; i++) + materials[i] = Material.HeavyCore; + for (int i = 9968; i <= 9983; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[20505] = Material.HoneyBlock; + materials[20506] = Material.HoneycombBlock; + for (int i = 10034; i <= 10043; i++) + materials[i] = Material.Hopper; + for (int i = 13886; i <= 13887; i++) + materials[i] = Material.HornCoral; + materials[13867] = Material.HornCoralBlock; + for (int i = 13906; i <= 13907; i++) + materials[i] = Material.HornCoralFan; + for (int i = 13980; i <= 13987; i++) + materials[i] = Material.HornCoralWallFan; + materials[5958] = Material.Ice; + materials[6791] = Material.InfestedChiseledStoneBricks; + materials[6787] = Material.InfestedCobblestone; + materials[6790] = Material.InfestedCrackedStoneBricks; + for (int i = 27646; i <= 27648; i++) + materials[i] = Material.InfestedDeepslate; + materials[6789] = Material.InfestedMossyStoneBricks; + materials[6786] = Material.InfestedStone; + materials[6788] = Material.InfestedStoneBricks; + for (int i = 6984; i <= 7015; i++) + materials[i] = Material.IronBars; + materials[2138] = Material.IronBlock; + for (int i = 5828; i <= 5891; i++) + materials[i] = Material.IronDoor; + materials[131] = Material.IronOre; + for (int i = 11288; i <= 11351; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 6049; i <= 6052; i++) + materials[i] = Material.JackOLantern; + for (int i = 20415; i <= 20426; i++) + materials[i] = Material.Jigsaw; + for (int i = 5994; i <= 5995; i++) + materials[i] = Material.Jukebox; + for (int i = 9468; i <= 9491; i++) + materials[i] = Material.JungleButton; + for (int i = 12909; i <= 12972; i++) + materials[i] = Material.JungleDoor; + for (int i = 12557; i <= 12588; i++) + materials[i] = Material.JungleFence; + for (int i = 12269; i <= 12300; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 5258; i <= 5321; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5898; i <= 5899; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 4526; i <= 4557; i++) + materials[i] = Material.JungleSign; + for (int i = 12069; i <= 12074; i++) + materials[i] = Material.JungleSlab; + for (int i = 8610; i <= 8689; i++) + materials[i] = Material.JungleStairs; + for (int i = 6332; i <= 6395; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5746; i <= 5753; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4898; i <= 4905; i++) + materials[i] = Material.JungleWallSign; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 13783; i <= 13808; i++) + materials[i] = Material.Kelp; + materials[13809] = Material.KelpPlant; + for (int i = 4750; i <= 4757; i++) + materials[i] = Material.Ladder; + for (int i = 19558; i <= 19561; i++) + materials[i] = Material.Lantern; + materials[565] = Material.LapisBlock; + materials[563] = Material.LapisOre; + for (int i = 22105; i <= 22116; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 11646; i <= 11647; i++) + materials[i] = Material.LargeFern; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + materials[8186] = Material.LavaCauldron; + for (int i = 25919; i <= 25934; i++) + materials[i] = Material.LeafLitter; + for (int i = 19505; i <= 19520; i++) + materials[i] = Material.Lectern; + for (int i = 5802; i <= 5825; i++) + materials[i] = Material.Lever; + for (int i = 11256; i <= 11287; i++) + materials[i] = Material.Light; + for (int i = 11696; i <= 11711; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1779; i <= 1794; i++) + materials[i] = Material.LightBlueBed; + for (int i = 21849; i <= 21864; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 22065; i <= 22066; i++) + materials[i] = Material.LightBlueCandleCake; + materials[11620] = Material.LightBlueCarpet; + materials[13754] = Material.LightBlueConcrete; + materials[13770] = Material.LightBlueConcretePowder; + for (int i = 13699; i <= 13702; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 13609; i <= 13614; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[6127] = Material.LightBlueStainedGlass; + for (int i = 10277; i <= 10308; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[10168] = Material.LightBlueTerracotta; + for (int i = 11916; i <= 11919; i++) + materials[i] = Material.LightBlueWallBanner; + materials[2096] = Material.LightBlueWool; + for (int i = 11776; i <= 11791; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1859; i <= 1874; i++) + materials[i] = Material.LightGrayBed; + for (int i = 21929; i <= 21944; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 22075; i <= 22076; i++) + materials[i] = Material.LightGrayCandleCake; + materials[11625] = Material.LightGrayCarpet; + materials[13759] = Material.LightGrayConcrete; + materials[13775] = Material.LightGrayConcretePowder; + for (int i = 13719; i <= 13722; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 13639; i <= 13644; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[6132] = Material.LightGrayStainedGlass; + for (int i = 10437; i <= 10468; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[10173] = Material.LightGrayTerracotta; + for (int i = 11936; i <= 11939; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2101] = Material.LightGrayWool; + for (int i = 9952; i <= 9967; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 25784; i <= 25807; i++) + materials[i] = Material.LightningRod; + for (int i = 11638; i <= 11639; i++) + materials[i] = Material.Lilac; + materials[2134] = Material.LilyOfTheValley; + materials[7642] = Material.LilyPad; + for (int i = 11728; i <= 11743; i++) + materials[i] = Material.LimeBanner; + for (int i = 1811; i <= 1826; i++) + materials[i] = Material.LimeBed; + for (int i = 21881; i <= 21896; i++) + materials[i] = Material.LimeCandle; + for (int i = 22069; i <= 22070; i++) + materials[i] = Material.LimeCandleCake; + materials[11622] = Material.LimeCarpet; + materials[13756] = Material.LimeConcrete; + materials[13772] = Material.LimeConcretePowder; + for (int i = 13707; i <= 13710; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 13621; i <= 13626; i++) + materials[i] = Material.LimeShulkerBox; + materials[6129] = Material.LimeStainedGlass; + for (int i = 10341; i <= 10372; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[10170] = Material.LimeTerracotta; + for (int i = 11924; i <= 11927; i++) + materials[i] = Material.LimeWallBanner; + materials[2098] = Material.LimeWool; + materials[20519] = Material.Lodestone; + for (int i = 19459; i <= 19462; i++) + materials[i] = Material.Loom; + for (int i = 11680; i <= 11695; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1763; i <= 1778; i++) + materials[i] = Material.MagentaBed; + for (int i = 21833; i <= 21848; i++) + materials[i] = Material.MagentaCandle; + for (int i = 22063; i <= 22064; i++) + materials[i] = Material.MagentaCandleCake; + materials[11619] = Material.MagentaCarpet; + materials[13753] = Material.MagentaConcrete; + materials[13769] = Material.MagentaConcretePowder; + for (int i = 13695; i <= 13698; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 13603; i <= 13608; i++) + materials[i] = Material.MagentaShulkerBox; + materials[6126] = Material.MagentaStainedGlass; + for (int i = 10245; i <= 10276; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[10167] = Material.MagentaTerracotta; + for (int i = 11912; i <= 11915; i++) + materials[i] = Material.MagentaWallBanner; + materials[2095] = Material.MagentaWool; + materials[13566] = Material.MagmaBlock; + for (int i = 9588; i <= 9611; i++) + materials[i] = Material.MangroveButton; + for (int i = 13229; i <= 13292; i++) + materials[i] = Material.MangroveDoor; + for (int i = 12717; i <= 12748; i++) + materials[i] = Material.MangroveFence; + for (int i = 12429; i <= 12460; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5578; i <= 5641; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + materials[26] = Material.MangrovePlanks; + for (int i = 5908; i <= 5909; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4622; i <= 4653; i++) + materials[i] = Material.MangroveSign; + for (int i = 12099; i <= 12104; i++) + materials[i] = Material.MangroveSlab; + for (int i = 11013; i <= 11092; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6652; i <= 6715; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5770; i <= 5777; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4922; i <= 4929; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 22117; i <= 22128; i++) + materials[i] = Material.MediumAmethystBud; + materials[7055] = Material.Melon; + for (int i = 7072; i <= 7079; i++) + materials[i] = Material.MelonStem; + materials[25935] = Material.MossBlock; + materials[25886] = Material.MossCarpet; + materials[2399] = Material.MossyCobblestone; + for (int i = 15161; i <= 15166; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 14337; i <= 14416; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 9027; i <= 9350; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 15149; i <= 15154; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 14177; i <= 14256; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 16187; i <= 16510; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6781] = Material.MossyStoneBricks; + for (int i = 2109; i <= 2120; i++) + materials[i] = Material.MovingPiston; + materials[25995] = Material.Mud; + for (int i = 12165; i <= 12170; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 7560; i <= 7639; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 17159; i <= 17482; i++) + materials[i] = Material.MudBrickWall; + materials[6785] = Material.MudBricks; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6920; i <= 6983; i++) + materials[i] = Material.MushroomStem; + for (int i = 7640; i <= 7641; i++) + materials[i] = Material.Mycelium; + for (int i = 8057; i <= 8088; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 12171; i <= 12176; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 8089; i <= 8168; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 17483; i <= 17806; i++) + materials[i] = Material.NetherBrickWall; + materials[8056] = Material.NetherBricks; + materials[135] = Material.NetherGoldOre; + for (int i = 6043; i <= 6044; i++) + materials[i] = Material.NetherPortal; + materials[10033] = Material.NetherQuartzOre; + materials[19650] = Material.NetherSprouts; + for (int i = 8169; i <= 8172; i++) + materials[i] = Material.NetherWart; + materials[13567] = Material.NetherWartBlock; + materials[20507] = Material.NetheriteBlock; + materials[6028] = Material.Netherrack; + for (int i = 581; i <= 1730; i++) + materials[i] = Material.NoteBlock; + for (int i = 9396; i <= 9419; i++) + materials[i] = Material.OakButton; + for (int i = 4686; i <= 4749; i++) + materials[i] = Material.OakDoor; + for (int i = 5996; i <= 6027; i++) + materials[i] = Material.OakFence; + for (int i = 7368; i <= 7399; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4938; i <= 5001; i++) + materials[i] = Material.OakHangingSign; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5892; i <= 5893; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 4366; i <= 4397; i++) + materials[i] = Material.OakSign; + for (int i = 12051; i <= 12056; i++) + materials[i] = Material.OakSlab; + for (int i = 2938; i <= 3017; i++) + materials[i] = Material.OakStairs; + for (int i = 6140; i <= 6203; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5706; i <= 5713; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4858; i <= 4865; i++) + materials[i] = Material.OakWallSign; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 13573; i <= 13584; i++) + materials[i] = Material.Observer; + materials[2400] = Material.Obsidian; + for (int i = 27655; i <= 27657; i++) + materials[i] = Material.OchreFroglight; + materials[27941] = Material.OpenEyeblossom; + for (int i = 11664; i <= 11679; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1747; i <= 1762; i++) + materials[i] = Material.OrangeBed; + for (int i = 21817; i <= 21832; i++) + materials[i] = Material.OrangeCandle; + for (int i = 22061; i <= 22062; i++) + materials[i] = Material.OrangeCandleCake; + materials[11618] = Material.OrangeCarpet; + materials[13752] = Material.OrangeConcrete; + materials[13768] = Material.OrangeConcretePowder; + for (int i = 13691; i <= 13694; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 13597; i <= 13602; i++) + materials[i] = Material.OrangeShulkerBox; + materials[6125] = Material.OrangeStainedGlass; + for (int i = 10213; i <= 10244; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[10166] = Material.OrangeTerracotta; + materials[2128] = Material.OrangeTulip; + for (int i = 11908; i <= 11911; i++) + materials[i] = Material.OrangeWallBanner; + materials[2094] = Material.OrangeWool; + materials[2131] = Material.OxeyeDaisy; + materials[24008] = Material.OxidizedChiseledCopper; + materials[24001] = Material.OxidizedCopper; + for (int i = 25764; i <= 25767; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 24840; i <= 24903; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 25742; i <= 25743; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 25352; i <= 25415; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + materials[24004] = Material.OxidizedCutCopper; + for (int i = 24336; i <= 24341; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 24016; i <= 24095; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[11635] = Material.PackedIce; + materials[6784] = Material.PackedMud; + for (int i = 27939; i <= 27940; i++) + materials[i] = Material.PaleHangingMoss; + materials[27776] = Material.PaleMossBlock; + for (int i = 27777; i <= 27938; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 9564; i <= 9587; i++) + materials[i] = Material.PaleOakButton; + for (int i = 13165; i <= 13228; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 12685; i <= 12716; i++) + materials[i] = Material.PaleOakFence; + for (int i = 12397; i <= 12428; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 5386; i <= 5449; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + materials[25] = Material.PaleOakPlanks; + for (int i = 5906; i <= 5907; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 4590; i <= 4621; i++) + materials[i] = Material.PaleOakSign; + for (int i = 12093; i <= 12098; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 10933; i <= 11012; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 6588; i <= 6651; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 5762; i <= 5769; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 4914; i <= 4921; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 27661; i <= 27663; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 11642; i <= 11643; i++) + materials[i] = Material.Peony; + for (int i = 12141; i <= 12146; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 9876; i <= 9907; i++) + materials[i] = Material.PiglinHead; + for (int i = 9908; i <= 9915; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 11744; i <= 11759; i++) + materials[i] = Material.PinkBanner; + for (int i = 1827; i <= 1842; i++) + materials[i] = Material.PinkBed; + for (int i = 21897; i <= 21912; i++) + materials[i] = Material.PinkCandle; + for (int i = 22071; i <= 22072; i++) + materials[i] = Material.PinkCandleCake; + materials[11623] = Material.PinkCarpet; + materials[13757] = Material.PinkConcrete; + materials[13773] = Material.PinkConcretePowder; + for (int i = 13711; i <= 13714; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 25887; i <= 25902; i++) + materials[i] = Material.PinkPetals; + for (int i = 13627; i <= 13632; i++) + materials[i] = Material.PinkShulkerBox; + materials[6130] = Material.PinkStainedGlass; + for (int i = 10373; i <= 10404; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[10171] = Material.PinkTerracotta; + materials[2130] = Material.PinkTulip; + for (int i = 11928; i <= 11931; i++) + materials[i] = Material.PinkWallBanner; + materials[2099] = Material.PinkWool; + for (int i = 2057; i <= 2068; i++) + materials[i] = Material.Piston; + for (int i = 2069; i <= 2092; i++) + materials[i] = Material.PistonHead; + for (int i = 13520; i <= 13529; i++) + materials[i] = Material.PitcherCrop; + for (int i = 13530; i <= 13531; i++) + materials[i] = Material.PitcherPlant; + for (int i = 9756; i <= 9787; i++) + materials[i] = Material.PlayerHead; + for (int i = 9788; i <= 9795; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 25808; i <= 25827; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 15203; i <= 15208; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 14977; i <= 15056; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 6034; i <= 6036; i++) + materials[i] = Material.PolishedBasalt; + materials[20931] = Material.PolishedBlackstone; + for (int i = 20935; i <= 20940; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 20941; i <= 21020; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 21021; i <= 21344; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[20932] = Material.PolishedBlackstoneBricks; + for (int i = 21434; i <= 21457; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 21432; i <= 21433; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 21426; i <= 21431; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 21346; i <= 21425; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 21458; i <= 21781; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[26410] = Material.PolishedDeepslate; + for (int i = 26491; i <= 26496; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 26411; i <= 26490; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 26497; i <= 26820; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 15155; i <= 15160; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 14257; i <= 14336; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 15137; i <= 15142; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 14017; i <= 14096; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[22552] = Material.PolishedTuff; + for (int i = 22553; i <= 22558; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 22559; i <= 22638; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 22639; i <= 22962; i++) + materials[i] = Material.PolishedTuffWall; + materials[2123] = Material.Poppy; + for (int i = 9388; i <= 9395; i++) + materials[i] = Material.Potatoes; + materials[9357] = Material.PottedAcaciaSapling; + materials[9366] = Material.PottedAllium; + materials[27653] = Material.PottedAzaleaBush; + materials[9367] = Material.PottedAzureBluet; + materials[14012] = Material.PottedBamboo; + materials[9355] = Material.PottedBirchSapling; + materials[9365] = Material.PottedBlueOrchid; + materials[9377] = Material.PottedBrownMushroom; + materials[9379] = Material.PottedCactus; + materials[9358] = Material.PottedCherrySapling; + materials[27944] = Material.PottedClosedEyeblossom; + materials[9373] = Material.PottedCornflower; + materials[20515] = Material.PottedCrimsonFungus; + materials[20517] = Material.PottedCrimsonRoots; + materials[9363] = Material.PottedDandelion; + materials[9359] = Material.PottedDarkOakSapling; + materials[9378] = Material.PottedDeadBush; + materials[9362] = Material.PottedFern; + materials[27654] = Material.PottedFloweringAzaleaBush; + materials[9356] = Material.PottedJungleSapling; + materials[9374] = Material.PottedLilyOfTheValley; + materials[9361] = Material.PottedMangrovePropagule; + materials[9353] = Material.PottedOakSapling; + materials[27943] = Material.PottedOpenEyeblossom; + materials[9369] = Material.PottedOrangeTulip; + materials[9372] = Material.PottedOxeyeDaisy; + materials[9360] = Material.PottedPaleOakSapling; + materials[9371] = Material.PottedPinkTulip; + materials[9364] = Material.PottedPoppy; + materials[9376] = Material.PottedRedMushroom; + materials[9368] = Material.PottedRedTulip; + materials[9354] = Material.PottedSpruceSapling; + materials[9352] = Material.PottedTorchflower; + materials[20516] = Material.PottedWarpedFungus; + materials[20518] = Material.PottedWarpedRoots; + materials[9370] = Material.PottedWhiteTulip; + materials[9375] = Material.PottedWitherRose; + materials[23378] = Material.PowderSnow; + for (int i = 8187; i <= 8189; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1987; i <= 2010; i++) + materials[i] = Material.PoweredRail; + materials[11352] = Material.Prismarine; + for (int i = 11601; i <= 11606; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 11435; i <= 11514; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[11353] = Material.PrismarineBricks; + for (int i = 11595; i <= 11600; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 11355; i <= 11434; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 15539; i <= 15862; i++) + materials[i] = Material.PrismarineWall; + materials[7054] = Material.Pumpkin; + for (int i = 7064; i <= 7071; i++) + materials[i] = Material.PumpkinStem; + for (int i = 11808; i <= 11823; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1891; i <= 1906; i++) + materials[i] = Material.PurpleBed; + for (int i = 21961; i <= 21976; i++) + materials[i] = Material.PurpleCandle; + for (int i = 22079; i <= 22080; i++) + materials[i] = Material.PurpleCandleCake; + materials[11627] = Material.PurpleCarpet; + materials[13761] = Material.PurpleConcrete; + materials[13777] = Material.PurpleConcretePowder; + for (int i = 13727; i <= 13730; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 13651; i <= 13656; i++) + materials[i] = Material.PurpleShulkerBox; + materials[6134] = Material.PurpleStainedGlass; + for (int i = 10501; i <= 10532; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[10175] = Material.PurpleTerracotta; + for (int i = 11944; i <= 11947; i++) + materials[i] = Material.PurpleWallBanner; + materials[2103] = Material.PurpleWool; + materials[13433] = Material.PurpurBlock; + for (int i = 13434; i <= 13436; i++) + materials[i] = Material.PurpurPillar; + for (int i = 12195; i <= 12200; i++) + materials[i] = Material.PurpurSlab; + for (int i = 13437; i <= 13516; i++) + materials[i] = Material.PurpurStairs; + materials[10044] = Material.QuartzBlock; + materials[21784] = Material.QuartzBricks; + for (int i = 10046; i <= 10048; i++) + materials[i] = Material.QuartzPillar; + for (int i = 12177; i <= 12182; i++) + materials[i] = Material.QuartzSlab; + for (int i = 10049; i <= 10128; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4758; i <= 4777; i++) + materials[i] = Material.Rail; + materials[27651] = Material.RawCopperBlock; + materials[27652] = Material.RawGoldBlock; + materials[27650] = Material.RawIronBlock; + for (int i = 11872; i <= 11887; i++) + materials[i] = Material.RedBanner; + for (int i = 1955; i <= 1970; i++) + materials[i] = Material.RedBed; + for (int i = 22025; i <= 22040; i++) + materials[i] = Material.RedCandle; + for (int i = 22087; i <= 22088; i++) + materials[i] = Material.RedCandleCake; + materials[11631] = Material.RedCarpet; + materials[13765] = Material.RedConcrete; + materials[13781] = Material.RedConcretePowder; + for (int i = 13743; i <= 13746; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2136] = Material.RedMushroom; + for (int i = 6856; i <= 6919; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 15197; i <= 15202; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 14897; i <= 14976; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 18131; i <= 18454; i++) + materials[i] = Material.RedNetherBrickWall; + materials[13568] = Material.RedNetherBricks; + materials[123] = Material.RedSand; + materials[11968] = Material.RedSandstone; + for (int i = 12183; i <= 12188; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 11971; i <= 12050; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 15863; i <= 16186; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 13675; i <= 13680; i++) + materials[i] = Material.RedShulkerBox; + materials[6138] = Material.RedStainedGlass; + for (int i = 10629; i <= 10660; i++) + materials[i] = Material.RedStainedGlassPane; + materials[10179] = Material.RedTerracotta; + materials[2127] = Material.RedTulip; + for (int i = 11960; i <= 11963; i++) + materials[i] = Material.RedWallBanner; + materials[2107] = Material.RedWool; + materials[10032] = Material.RedstoneBlock; + for (int i = 8201; i <= 8202; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5912; i <= 5913; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5916; i <= 5917; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5918; i <= 5925; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 3042; i <= 4337; i++) + materials[i] = Material.RedstoneWire; + materials[27665] = Material.ReinforcedDeepslate; + for (int i = 6060; i <= 6123; i++) + materials[i] = Material.Repeater; + for (int i = 13538; i <= 13549; i++) + materials[i] = Material.RepeatingCommandBlock; + materials[7643] = Material.ResinBlock; + for (int i = 7725; i <= 7730; i++) + materials[i] = Material.ResinBrickSlab; + for (int i = 7645; i <= 7724; i++) + materials[i] = Material.ResinBrickStairs; + for (int i = 7731; i <= 8054; i++) + materials[i] = Material.ResinBrickWall; + materials[7644] = Material.ResinBricks; + for (int i = 7240; i <= 7367; i++) + materials[i] = Material.ResinClump; + for (int i = 20510; i <= 20514; i++) + materials[i] = Material.RespawnAnchor; + materials[25994] = Material.RootedDirt; + for (int i = 11640; i <= 11641; i++) + materials[i] = Material.RoseBush; + materials[118] = Material.Sand; + materials[578] = Material.Sandstone; + for (int i = 12129; i <= 12134; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 8215; i <= 8294; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 18455; i <= 18778; i++) + materials[i] = Material.SandstoneWall; + for (int i = 19427; i <= 19458; i++) + materials[i] = Material.Scaffolding; + materials[23859] = Material.Sculk; + for (int i = 23988; i <= 23989; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 23379; i <= 23474; i++) + materials[i] = Material.SculkSensor; + for (int i = 23990; i <= 23997; i++) + materials[i] = Material.SculkShrieker; + for (int i = 23860; i <= 23987; i++) + materials[i] = Material.SculkVein; + materials[11613] = Material.SeaLantern; + for (int i = 13988; i <= 13995; i++) + materials[i] = Material.SeaPickle; + materials[2054] = Material.Seagrass; + materials[2052] = Material.ShortDryGrass; + materials[2048] = Material.ShortGrass; + materials[19665] = Material.Shroomlight; + for (int i = 13585; i <= 13590; i++) + materials[i] = Material.ShulkerBox; + for (int i = 9636; i <= 9667; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 9668; i <= 9675; i++) + materials[i] = Material.SkeletonWallSkull; + materials[11253] = Material.SlimeBlock; + for (int i = 22129; i <= 22140; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 25976; i <= 25991; i++) + materials[i] = Material.SmallDripleaf; + materials[19521] = Material.SmithingTable; + for (int i = 19475; i <= 19482; i++) + materials[i] = Material.Smoker; + materials[27649] = Material.SmoothBasalt; + materials[12203] = Material.SmoothQuartz; + for (int i = 15179; i <= 15184; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 14657; i <= 14736; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[12204] = Material.SmoothRedSandstone; + for (int i = 15143; i <= 15148; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 14097; i <= 14176; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[12202] = Material.SmoothSandstone; + for (int i = 15173; i <= 15178; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 14577; i <= 14656; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[12201] = Material.SmoothStone; + for (int i = 12123; i <= 12128; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13823; i <= 13825; i++) + materials[i] = Material.SnifferEgg; + for (int i = 5950; i <= 5957; i++) + materials[i] = Material.Snow; + materials[5959] = Material.SnowBlock; + for (int i = 19598; i <= 19629; i++) + materials[i] = Material.SoulCampfire; + materials[2918] = Material.SoulFire; + for (int i = 19562; i <= 19565; i++) + materials[i] = Material.SoulLantern; + materials[6029] = Material.SoulSand; + materials[6030] = Material.SoulSoil; + materials[6037] = Material.SoulTorch; + for (int i = 6038; i <= 6041; i++) + materials[i] = Material.SoulWallTorch; + materials[2919] = Material.Spawner; + materials[560] = Material.Sponge; + materials[25883] = Material.SporeBlossom; + for (int i = 9420; i <= 9443; i++) + materials[i] = Material.SpruceButton; + for (int i = 12781; i <= 12844; i++) + materials[i] = Material.SpruceDoor; + for (int i = 12493; i <= 12524; i++) + materials[i] = Material.SpruceFence; + for (int i = 12205; i <= 12236; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 5002; i <= 5065; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5894; i <= 5895; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4398; i <= 4429; i++) + materials[i] = Material.SpruceSign; + for (int i = 12057; i <= 12062; i++) + materials[i] = Material.SpruceSlab; + for (int i = 8450; i <= 8529; i++) + materials[i] = Material.SpruceStairs; + for (int i = 6204; i <= 6267; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5714; i <= 5721; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4866; i <= 4873; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 2035; i <= 2046; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 12159; i <= 12164; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 7480; i <= 7559; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 16835; i <= 17158; i++) + materials[i] = Material.StoneBrickWall; + materials[6780] = Material.StoneBricks; + for (int i = 5926; i <= 5949; i++) + materials[i] = Material.StoneButton; + for (int i = 5826; i <= 5827; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 12117; i <= 12122; i++) + materials[i] = Material.StoneSlab; + for (int i = 14497; i <= 14576; i++) + materials[i] = Material.StoneStairs; + for (int i = 19522; i <= 19525; i++) + materials[i] = Material.Stonecutter; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 19660; i <= 19662; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 19654; i <= 19656; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 19643; i <= 19645; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 19637; i <= 19639; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 20411; i <= 20414; i++) + materials[i] = Material.StructureBlock; + materials[13572] = Material.StructureVoid; + for (int i = 5978; i <= 5993; i++) + materials[i] = Material.SugarCane; + for (int i = 11636; i <= 11637; i++) + materials[i] = Material.Sunflower; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 19630; i <= 19633; i++) + materials[i] = Material.SweetBerryBush; + materials[2053] = Material.TallDryGrass; + for (int i = 11644; i <= 11645; i++) + materials[i] = Material.TallGrass; + for (int i = 2055; i <= 2056; i++) + materials[i] = Material.TallSeagrass; + for (int i = 20441; i <= 20456; i++) + materials[i] = Material.Target; + materials[11633] = Material.Terracotta; + for (int i = 20427; i <= 20430; i++) + materials[i] = Material.TestBlock; + materials[20431] = Material.TestInstanceBlock; + materials[23377] = Material.TintedGlass; + for (int i = 2140; i <= 2141; i++) + materials[i] = Material.Tnt; + materials[2401] = Material.Torch; + materials[2122] = Material.Torchflower; + for (int i = 13518; i <= 13519; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 9928; i <= 9951; i++) + materials[i] = Material.TrappedChest; + for (int i = 27730; i <= 27741; i++) + materials[i] = Material.TrialSpawner; + for (int i = 8321; i <= 8448; i++) + materials[i] = Material.Tripwire; + for (int i = 8305; i <= 8320; i++) + materials[i] = Material.TripwireHook; + for (int i = 13878; i <= 13879; i++) + materials[i] = Material.TubeCoral; + materials[13863] = Material.TubeCoralBlock; + for (int i = 13898; i <= 13899; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 13948; i <= 13955; i++) + materials[i] = Material.TubeCoralWallFan; + materials[22141] = Material.Tuff; + for (int i = 22965; i <= 22970; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 22971; i <= 23050; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 23051; i <= 23374; i++) + materials[i] = Material.TuffBrickWall; + materials[22964] = Material.TuffBricks; + for (int i = 22142; i <= 22147; i++) + materials[i] = Material.TuffSlab; + for (int i = 22148; i <= 22227; i++) + materials[i] = Material.TuffStairs; + for (int i = 22228; i <= 22551; i++) + materials[i] = Material.TuffWall; + for (int i = 13811; i <= 13822; i++) + materials[i] = Material.TurtleEgg; + for (int i = 19693; i <= 19718; i++) + materials[i] = Material.TwistingVines; + materials[19719] = Material.TwistingVinesPlant; + for (int i = 27742; i <= 27773; i++) + materials[i] = Material.Vault; + for (int i = 27658; i <= 27660; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 7080; i <= 7111; i++) + materials[i] = Material.Vine; + materials[14013] = Material.VoidAir; + for (int i = 2402; i <= 2405; i++) + materials[i] = Material.WallTorch; + for (int i = 20179; i <= 20202; i++) + materials[i] = Material.WarpedButton; + for (int i = 20267; i <= 20330; i++) + materials[i] = Material.WarpedDoor; + for (int i = 19771; i <= 19802; i++) + materials[i] = Material.WarpedFence; + for (int i = 19963; i <= 19994; i++) + materials[i] = Material.WarpedFenceGate; + materials[19647] = Material.WarpedFungus; + for (int i = 5514; i <= 5577; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 19640; i <= 19642; i++) + materials[i] = Material.WarpedHyphae; + materials[19646] = Material.WarpedNylium; + materials[19722] = Material.WarpedPlanks; + for (int i = 19737; i <= 19738; i++) + materials[i] = Material.WarpedPressurePlate; + materials[19649] = Material.WarpedRoots; + for (int i = 20363; i <= 20394; i++) + materials[i] = Material.WarpedSign; + for (int i = 19729; i <= 19734; i++) + materials[i] = Material.WarpedSlab; + for (int i = 20075; i <= 20154; i++) + materials[i] = Material.WarpedStairs; + for (int i = 19634; i <= 19636; i++) + materials[i] = Material.WarpedStem; + for (int i = 19867; i <= 19930; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5786; i <= 5793; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 20403; i <= 20410; i++) + materials[i] = Material.WarpedWallSign; + materials[19648] = Material.WarpedWartBlock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 8183; i <= 8185; i++) + materials[i] = Material.WaterCauldron; + materials[24015] = Material.WaxedChiseledCopper; + materials[24360] = Material.WaxedCopperBlock; + for (int i = 25768; i <= 25771; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 24968; i <= 25031; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 25744; i <= 25745; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 25480; i <= 25543; i++) + materials[i] = Material.WaxedCopperTrapdoor; + materials[24367] = Material.WaxedCutCopper; + for (int i = 24706; i <= 24711; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 24608; i <= 24687; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[24014] = Material.WaxedExposedChiseledCopper; + materials[24362] = Material.WaxedExposedCopper; + for (int i = 25772; i <= 25775; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 25032; i <= 25095; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 25746; i <= 25747; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 25544; i <= 25607; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + materials[24366] = Material.WaxedExposedCutCopper; + for (int i = 24700; i <= 24705; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 24528; i <= 24607; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[24012] = Material.WaxedOxidizedChiseledCopper; + materials[24363] = Material.WaxedOxidizedCopper; + for (int i = 25780; i <= 25783; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 25096; i <= 25159; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 25750; i <= 25751; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 25608; i <= 25671; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + materials[24364] = Material.WaxedOxidizedCutCopper; + for (int i = 24688; i <= 24693; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 24368; i <= 24447; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[24013] = Material.WaxedWeatheredChiseledCopper; + materials[24361] = Material.WaxedWeatheredCopper; + for (int i = 25776; i <= 25779; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 25160; i <= 25223; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 25748; i <= 25749; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 25672; i <= 25735; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + materials[24365] = Material.WaxedWeatheredCutCopper; + for (int i = 24694; i <= 24699; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 24448; i <= 24527; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[24009] = Material.WeatheredChiseledCopper; + materials[24000] = Material.WeatheredCopper; + for (int i = 25760; i <= 25763; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 24904; i <= 24967; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 25740; i <= 25741; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 25416; i <= 25479; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + materials[24005] = Material.WeatheredCutCopper; + for (int i = 24342; i <= 24347; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 24096; i <= 24175; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 19666; i <= 19691; i++) + materials[i] = Material.WeepingVines; + materials[19692] = Material.WeepingVinesPlant; + materials[561] = Material.WetSponge; + for (int i = 4342; i <= 4349; i++) + materials[i] = Material.Wheat; + for (int i = 11648; i <= 11663; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1731; i <= 1746; i++) + materials[i] = Material.WhiteBed; + for (int i = 21801; i <= 21816; i++) + materials[i] = Material.WhiteCandle; + for (int i = 22059; i <= 22060; i++) + materials[i] = Material.WhiteCandleCake; + materials[11617] = Material.WhiteCarpet; + materials[13751] = Material.WhiteConcrete; + materials[13767] = Material.WhiteConcretePowder; + for (int i = 13687; i <= 13690; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 13591; i <= 13596; i++) + materials[i] = Material.WhiteShulkerBox; + materials[6124] = Material.WhiteStainedGlass; + for (int i = 10181; i <= 10212; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[10165] = Material.WhiteTerracotta; + materials[2129] = Material.WhiteTulip; + for (int i = 11904; i <= 11907; i++) + materials[i] = Material.WhiteWallBanner; + materials[2093] = Material.WhiteWool; + for (int i = 25903; i <= 25918; i++) + materials[i] = Material.Wildflowers; + materials[2133] = Material.WitherRose; + for (int i = 9676; i <= 9707; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 9708; i <= 9715; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 11712; i <= 11727; i++) + materials[i] = Material.YellowBanner; + for (int i = 1795; i <= 1810; i++) + materials[i] = Material.YellowBed; + for (int i = 21865; i <= 21880; i++) + materials[i] = Material.YellowCandle; + for (int i = 22067; i <= 22068; i++) + materials[i] = Material.YellowCandleCake; + materials[11621] = Material.YellowCarpet; + materials[13755] = Material.YellowConcrete; + materials[13771] = Material.YellowConcretePowder; + for (int i = 13703; i <= 13706; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 13615; i <= 13620; i++) + materials[i] = Material.YellowShulkerBox; + materials[6128] = Material.YellowStainedGlass; + for (int i = 10309; i <= 10340; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[10169] = Material.YellowTerracotta; + for (int i = 11920; i <= 11923; i++) + materials[i] = Material.YellowWallBanner; + materials[2097] = Material.YellowWool; + for (int i = 9716; i <= 9747; i++) + materials[i] = Material.ZombieHead; + for (int i = 9748; i <= 9755; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1219.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1219.cs new file mode 100644 index 00000000..9994f3c3 --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1219.cs @@ -0,0 +1,2350 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1219 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1219() + { + for (int i = 0; i <= 0; i++) + materials[i] = Material.Air; + for (int i = 1; i <= 1; i++) + materials[i] = Material.Stone; + for (int i = 2; i <= 2; i++) + materials[i] = Material.Granite; + for (int i = 3; i <= 3; i++) + materials[i] = Material.PolishedGranite; + for (int i = 4; i <= 4; i++) + materials[i] = Material.Diorite; + for (int i = 5; i <= 5; i++) + materials[i] = Material.PolishedDiorite; + for (int i = 6; i <= 6; i++) + materials[i] = Material.Andesite; + for (int i = 7; i <= 7; i++) + materials[i] = Material.PolishedAndesite; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + for (int i = 10; i <= 10; i++) + materials[i] = Material.Dirt; + for (int i = 11; i <= 11; i++) + materials[i] = Material.CoarseDirt; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 14; i <= 14; i++) + materials[i] = Material.Cobblestone; + for (int i = 15; i <= 15; i++) + materials[i] = Material.OakPlanks; + for (int i = 16; i <= 16; i++) + materials[i] = Material.SprucePlanks; + for (int i = 17; i <= 17; i++) + materials[i] = Material.BirchPlanks; + for (int i = 18; i <= 18; i++) + materials[i] = Material.JunglePlanks; + for (int i = 19; i <= 19; i++) + materials[i] = Material.AcaciaPlanks; + for (int i = 20; i <= 20; i++) + materials[i] = Material.CherryPlanks; + for (int i = 21; i <= 21; i++) + materials[i] = Material.DarkOakPlanks; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 25; i <= 25; i++) + materials[i] = Material.PaleOakPlanks; + for (int i = 26; i <= 26; i++) + materials[i] = Material.MangrovePlanks; + for (int i = 27; i <= 27; i++) + materials[i] = Material.BambooPlanks; + for (int i = 28; i <= 28; i++) + materials[i] = Material.BambooMosaic; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 85; i <= 85; i++) + materials[i] = Material.Bedrock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + for (int i = 118; i <= 118; i++) + materials[i] = Material.Sand; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 123; i <= 123; i++) + materials[i] = Material.RedSand; + for (int i = 124; i <= 124; i++) + materials[i] = Material.Gravel; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 129; i <= 129; i++) + materials[i] = Material.GoldOre; + for (int i = 130; i <= 130; i++) + materials[i] = Material.DeepslateGoldOre; + for (int i = 131; i <= 131; i++) + materials[i] = Material.IronOre; + for (int i = 132; i <= 132; i++) + materials[i] = Material.DeepslateIronOre; + for (int i = 133; i <= 133; i++) + materials[i] = Material.CoalOre; + for (int i = 134; i <= 134; i++) + materials[i] = Material.DeepslateCoalOre; + for (int i = 135; i <= 135; i++) + materials[i] = Material.NetherGoldOre; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + for (int i = 560; i <= 560; i++) + materials[i] = Material.Sponge; + for (int i = 561; i <= 561; i++) + materials[i] = Material.WetSponge; + for (int i = 562; i <= 562; i++) + materials[i] = Material.Glass; + for (int i = 563; i <= 563; i++) + materials[i] = Material.LapisOre; + for (int i = 564; i <= 564; i++) + materials[i] = Material.DeepslateLapisOre; + for (int i = 565; i <= 565; i++) + materials[i] = Material.LapisBlock; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + for (int i = 578; i <= 578; i++) + materials[i] = Material.Sandstone; + for (int i = 579; i <= 579; i++) + materials[i] = Material.ChiseledSandstone; + for (int i = 580; i <= 580; i++) + materials[i] = Material.CutSandstone; + for (int i = 581; i <= 1730; i++) + materials[i] = Material.NoteBlock; + for (int i = 1731; i <= 1746; i++) + materials[i] = Material.WhiteBed; + for (int i = 1747; i <= 1762; i++) + materials[i] = Material.OrangeBed; + for (int i = 1763; i <= 1778; i++) + materials[i] = Material.MagentaBed; + for (int i = 1779; i <= 1794; i++) + materials[i] = Material.LightBlueBed; + for (int i = 1795; i <= 1810; i++) + materials[i] = Material.YellowBed; + for (int i = 1811; i <= 1826; i++) + materials[i] = Material.LimeBed; + for (int i = 1827; i <= 1842; i++) + materials[i] = Material.PinkBed; + for (int i = 1843; i <= 1858; i++) + materials[i] = Material.GrayBed; + for (int i = 1859; i <= 1874; i++) + materials[i] = Material.LightGrayBed; + for (int i = 1875; i <= 1890; i++) + materials[i] = Material.CyanBed; + for (int i = 1891; i <= 1906; i++) + materials[i] = Material.PurpleBed; + for (int i = 1907; i <= 1922; i++) + materials[i] = Material.BlueBed; + for (int i = 1923; i <= 1938; i++) + materials[i] = Material.BrownBed; + for (int i = 1939; i <= 1954; i++) + materials[i] = Material.GreenBed; + for (int i = 1955; i <= 1970; i++) + materials[i] = Material.RedBed; + for (int i = 1971; i <= 1986; i++) + materials[i] = Material.BlackBed; + for (int i = 1987; i <= 2010; i++) + materials[i] = Material.PoweredRail; + for (int i = 2011; i <= 2034; i++) + materials[i] = Material.DetectorRail; + for (int i = 2035; i <= 2046; i++) + materials[i] = Material.StickyPiston; + for (int i = 2047; i <= 2047; i++) + materials[i] = Material.Cobweb; + for (int i = 2048; i <= 2048; i++) + materials[i] = Material.ShortGrass; + for (int i = 2049; i <= 2049; i++) + materials[i] = Material.Fern; + for (int i = 2050; i <= 2050; i++) + materials[i] = Material.DeadBush; + for (int i = 2051; i <= 2051; i++) + materials[i] = Material.Bush; + for (int i = 2052; i <= 2052; i++) + materials[i] = Material.ShortDryGrass; + for (int i = 2053; i <= 2053; i++) + materials[i] = Material.TallDryGrass; + for (int i = 2054; i <= 2054; i++) + materials[i] = Material.Seagrass; + for (int i = 2055; i <= 2056; i++) + materials[i] = Material.TallSeagrass; + for (int i = 2057; i <= 2068; i++) + materials[i] = Material.Piston; + for (int i = 2069; i <= 2092; i++) + materials[i] = Material.PistonHead; + for (int i = 2093; i <= 2093; i++) + materials[i] = Material.WhiteWool; + for (int i = 2094; i <= 2094; i++) + materials[i] = Material.OrangeWool; + for (int i = 2095; i <= 2095; i++) + materials[i] = Material.MagentaWool; + for (int i = 2096; i <= 2096; i++) + materials[i] = Material.LightBlueWool; + for (int i = 2097; i <= 2097; i++) + materials[i] = Material.YellowWool; + for (int i = 2098; i <= 2098; i++) + materials[i] = Material.LimeWool; + for (int i = 2099; i <= 2099; i++) + materials[i] = Material.PinkWool; + for (int i = 2100; i <= 2100; i++) + materials[i] = Material.GrayWool; + for (int i = 2101; i <= 2101; i++) + materials[i] = Material.LightGrayWool; + for (int i = 2102; i <= 2102; i++) + materials[i] = Material.CyanWool; + for (int i = 2103; i <= 2103; i++) + materials[i] = Material.PurpleWool; + for (int i = 2104; i <= 2104; i++) + materials[i] = Material.BlueWool; + for (int i = 2105; i <= 2105; i++) + materials[i] = Material.BrownWool; + for (int i = 2106; i <= 2106; i++) + materials[i] = Material.GreenWool; + for (int i = 2107; i <= 2107; i++) + materials[i] = Material.RedWool; + for (int i = 2108; i <= 2108; i++) + materials[i] = Material.BlackWool; + for (int i = 2109; i <= 2120; i++) + materials[i] = Material.MovingPiston; + for (int i = 2121; i <= 2121; i++) + materials[i] = Material.Dandelion; + for (int i = 2122; i <= 2122; i++) + materials[i] = Material.Torchflower; + for (int i = 2123; i <= 2123; i++) + materials[i] = Material.Poppy; + for (int i = 2124; i <= 2124; i++) + materials[i] = Material.BlueOrchid; + for (int i = 2125; i <= 2125; i++) + materials[i] = Material.Allium; + for (int i = 2126; i <= 2126; i++) + materials[i] = Material.AzureBluet; + for (int i = 2127; i <= 2127; i++) + materials[i] = Material.RedTulip; + for (int i = 2128; i <= 2128; i++) + materials[i] = Material.OrangeTulip; + for (int i = 2129; i <= 2129; i++) + materials[i] = Material.WhiteTulip; + for (int i = 2130; i <= 2130; i++) + materials[i] = Material.PinkTulip; + for (int i = 2131; i <= 2131; i++) + materials[i] = Material.OxeyeDaisy; + for (int i = 2132; i <= 2132; i++) + materials[i] = Material.Cornflower; + for (int i = 2133; i <= 2133; i++) + materials[i] = Material.WitherRose; + for (int i = 2134; i <= 2134; i++) + materials[i] = Material.LilyOfTheValley; + for (int i = 2135; i <= 2135; i++) + materials[i] = Material.BrownMushroom; + for (int i = 2136; i <= 2136; i++) + materials[i] = Material.RedMushroom; + for (int i = 2137; i <= 2137; i++) + materials[i] = Material.GoldBlock; + for (int i = 2138; i <= 2138; i++) + materials[i] = Material.IronBlock; + for (int i = 2139; i <= 2139; i++) + materials[i] = Material.Bricks; + for (int i = 2140; i <= 2141; i++) + materials[i] = Material.Tnt; + for (int i = 2142; i <= 2142; i++) + materials[i] = Material.Bookshelf; + for (int i = 2143; i <= 2398; i++) + materials[i] = Material.ChiseledBookshelf; + for (int i = 2399; i <= 2462; i++) + materials[i] = Material.AcaciaShelf; + for (int i = 2463; i <= 2526; i++) + materials[i] = Material.BambooShelf; + for (int i = 2527; i <= 2590; i++) + materials[i] = Material.BirchShelf; + for (int i = 2591; i <= 2654; i++) + materials[i] = Material.CherryShelf; + for (int i = 2655; i <= 2718; i++) + materials[i] = Material.CrimsonShelf; + for (int i = 2719; i <= 2782; i++) + materials[i] = Material.DarkOakShelf; + for (int i = 2783; i <= 2846; i++) + materials[i] = Material.JungleShelf; + for (int i = 2847; i <= 2910; i++) + materials[i] = Material.MangroveShelf; + for (int i = 2911; i <= 2974; i++) + materials[i] = Material.OakShelf; + for (int i = 2975; i <= 3038; i++) + materials[i] = Material.PaleOakShelf; + for (int i = 3039; i <= 3102; i++) + materials[i] = Material.SpruceShelf; + for (int i = 3103; i <= 3166; i++) + materials[i] = Material.WarpedShelf; + for (int i = 3167; i <= 3167; i++) + materials[i] = Material.MossyCobblestone; + for (int i = 3168; i <= 3168; i++) + materials[i] = Material.Obsidian; + for (int i = 3169; i <= 3169; i++) + materials[i] = Material.Torch; + for (int i = 3170; i <= 3173; i++) + materials[i] = Material.WallTorch; + for (int i = 3174; i <= 3685; i++) + materials[i] = Material.Fire; + for (int i = 3686; i <= 3686; i++) + materials[i] = Material.SoulFire; + for (int i = 3687; i <= 3687; i++) + materials[i] = Material.Spawner; + for (int i = 3688; i <= 3705; i++) + materials[i] = Material.CreakingHeart; + for (int i = 3706; i <= 3785; i++) + materials[i] = Material.OakStairs; + for (int i = 3786; i <= 3809; i++) + materials[i] = Material.Chest; + for (int i = 3810; i <= 5105; i++) + materials[i] = Material.RedstoneWire; + for (int i = 5106; i <= 5106; i++) + materials[i] = Material.DiamondOre; + for (int i = 5107; i <= 5107; i++) + materials[i] = Material.DeepslateDiamondOre; + for (int i = 5108; i <= 5108; i++) + materials[i] = Material.DiamondBlock; + for (int i = 5109; i <= 5109; i++) + materials[i] = Material.CraftingTable; + for (int i = 5110; i <= 5117; i++) + materials[i] = Material.Wheat; + for (int i = 5118; i <= 5125; i++) + materials[i] = Material.Farmland; + for (int i = 5126; i <= 5133; i++) + materials[i] = Material.Furnace; + for (int i = 5134; i <= 5165; i++) + materials[i] = Material.OakSign; + for (int i = 5166; i <= 5197; i++) + materials[i] = Material.SpruceSign; + for (int i = 5198; i <= 5229; i++) + materials[i] = Material.BirchSign; + for (int i = 5230; i <= 5261; i++) + materials[i] = Material.AcaciaSign; + for (int i = 5262; i <= 5293; i++) + materials[i] = Material.CherrySign; + for (int i = 5294; i <= 5325; i++) + materials[i] = Material.JungleSign; + for (int i = 5326; i <= 5357; i++) + materials[i] = Material.DarkOakSign; + for (int i = 5358; i <= 5389; i++) + materials[i] = Material.PaleOakSign; + for (int i = 5390; i <= 5421; i++) + materials[i] = Material.MangroveSign; + for (int i = 5422; i <= 5453; i++) + materials[i] = Material.BambooSign; + for (int i = 5454; i <= 5517; i++) + materials[i] = Material.OakDoor; + for (int i = 5518; i <= 5525; i++) + materials[i] = Material.Ladder; + for (int i = 5526; i <= 5545; i++) + materials[i] = Material.Rail; + for (int i = 5546; i <= 5625; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 5626; i <= 5633; i++) + materials[i] = Material.OakWallSign; + for (int i = 5634; i <= 5641; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 5642; i <= 5649; i++) + materials[i] = Material.BirchWallSign; + for (int i = 5650; i <= 5657; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 5658; i <= 5665; i++) + materials[i] = Material.CherryWallSign; + for (int i = 5666; i <= 5673; i++) + materials[i] = Material.JungleWallSign; + for (int i = 5674; i <= 5681; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 5682; i <= 5689; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 5690; i <= 5697; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 5698; i <= 5705; i++) + materials[i] = Material.BambooWallSign; + for (int i = 5706; i <= 5769; i++) + materials[i] = Material.OakHangingSign; + for (int i = 5770; i <= 5833; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 5834; i <= 5897; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 5898; i <= 5961; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 5962; i <= 6025; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 6026; i <= 6089; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 6090; i <= 6153; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 6154; i <= 6217; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 6218; i <= 6281; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 6282; i <= 6345; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 6346; i <= 6409; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 6410; i <= 6473; i++) + materials[i] = Material.BambooHangingSign; + for (int i = 6474; i <= 6481; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 6482; i <= 6489; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 6490; i <= 6497; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 6498; i <= 6505; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 6506; i <= 6513; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 6514; i <= 6521; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 6522; i <= 6529; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 6530; i <= 6537; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 6538; i <= 6545; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 6546; i <= 6553; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 6554; i <= 6561; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 6562; i <= 6569; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 6570; i <= 6593; i++) + materials[i] = Material.Lever; + for (int i = 6594; i <= 6595; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 6596; i <= 6659; i++) + materials[i] = Material.IronDoor; + for (int i = 6660; i <= 6661; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 6662; i <= 6663; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 6664; i <= 6665; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 6666; i <= 6667; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 6668; i <= 6669; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 6670; i <= 6671; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 6672; i <= 6673; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 6674; i <= 6675; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 6676; i <= 6677; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 6678; i <= 6679; i++) + materials[i] = Material.BambooPressurePlate; + for (int i = 6680; i <= 6681; i++) + materials[i] = Material.RedstoneOre; + for (int i = 6682; i <= 6683; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 6684; i <= 6685; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 6686; i <= 6693; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 6694; i <= 6717; i++) + materials[i] = Material.StoneButton; + for (int i = 6718; i <= 6725; i++) + materials[i] = Material.Snow; + for (int i = 6726; i <= 6726; i++) + materials[i] = Material.Ice; + for (int i = 6727; i <= 6727; i++) + materials[i] = Material.SnowBlock; + for (int i = 6728; i <= 6743; i++) + materials[i] = Material.Cactus; + for (int i = 6744; i <= 6744; i++) + materials[i] = Material.CactusFlower; + for (int i = 6745; i <= 6745; i++) + materials[i] = Material.Clay; + for (int i = 6746; i <= 6761; i++) + materials[i] = Material.SugarCane; + for (int i = 6762; i <= 6763; i++) + materials[i] = Material.Jukebox; + for (int i = 6764; i <= 6795; i++) + materials[i] = Material.OakFence; + for (int i = 6796; i <= 6796; i++) + materials[i] = Material.Netherrack; + for (int i = 6797; i <= 6797; i++) + materials[i] = Material.SoulSand; + for (int i = 6798; i <= 6798; i++) + materials[i] = Material.SoulSoil; + for (int i = 6799; i <= 6801; i++) + materials[i] = Material.Basalt; + for (int i = 6802; i <= 6804; i++) + materials[i] = Material.PolishedBasalt; + for (int i = 6805; i <= 6805; i++) + materials[i] = Material.SoulTorch; + for (int i = 6806; i <= 6809; i++) + materials[i] = Material.SoulWallTorch; + for (int i = 6810; i <= 6810; i++) + materials[i] = Material.CopperTorch; + for (int i = 6811; i <= 6814; i++) + materials[i] = Material.CopperWallTorch; + for (int i = 6815; i <= 6815; i++) + materials[i] = Material.Glowstone; + for (int i = 6816; i <= 6817; i++) + materials[i] = Material.NetherPortal; + for (int i = 6818; i <= 6821; i++) + materials[i] = Material.CarvedPumpkin; + for (int i = 6822; i <= 6825; i++) + materials[i] = Material.JackOLantern; + for (int i = 6826; i <= 6832; i++) + materials[i] = Material.Cake; + for (int i = 6833; i <= 6896; i++) + materials[i] = Material.Repeater; + for (int i = 6897; i <= 6897; i++) + materials[i] = Material.WhiteStainedGlass; + for (int i = 6898; i <= 6898; i++) + materials[i] = Material.OrangeStainedGlass; + for (int i = 6899; i <= 6899; i++) + materials[i] = Material.MagentaStainedGlass; + for (int i = 6900; i <= 6900; i++) + materials[i] = Material.LightBlueStainedGlass; + for (int i = 6901; i <= 6901; i++) + materials[i] = Material.YellowStainedGlass; + for (int i = 6902; i <= 6902; i++) + materials[i] = Material.LimeStainedGlass; + for (int i = 6903; i <= 6903; i++) + materials[i] = Material.PinkStainedGlass; + for (int i = 6904; i <= 6904; i++) + materials[i] = Material.GrayStainedGlass; + for (int i = 6905; i <= 6905; i++) + materials[i] = Material.LightGrayStainedGlass; + for (int i = 6906; i <= 6906; i++) + materials[i] = Material.CyanStainedGlass; + for (int i = 6907; i <= 6907; i++) + materials[i] = Material.PurpleStainedGlass; + for (int i = 6908; i <= 6908; i++) + materials[i] = Material.BlueStainedGlass; + for (int i = 6909; i <= 6909; i++) + materials[i] = Material.BrownStainedGlass; + for (int i = 6910; i <= 6910; i++) + materials[i] = Material.GreenStainedGlass; + for (int i = 6911; i <= 6911; i++) + materials[i] = Material.RedStainedGlass; + for (int i = 6912; i <= 6912; i++) + materials[i] = Material.BlackStainedGlass; + for (int i = 6913; i <= 6976; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 6977; i <= 7040; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 7041; i <= 7104; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 7105; i <= 7168; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 7169; i <= 7232; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 7233; i <= 7296; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 7297; i <= 7360; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 7361; i <= 7424; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 7425; i <= 7488; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 7489; i <= 7552; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 7553; i <= 7553; i++) + materials[i] = Material.StoneBricks; + for (int i = 7554; i <= 7554; i++) + materials[i] = Material.MossyStoneBricks; + for (int i = 7555; i <= 7555; i++) + materials[i] = Material.CrackedStoneBricks; + for (int i = 7556; i <= 7556; i++) + materials[i] = Material.ChiseledStoneBricks; + for (int i = 7557; i <= 7557; i++) + materials[i] = Material.PackedMud; + for (int i = 7558; i <= 7558; i++) + materials[i] = Material.MudBricks; + for (int i = 7559; i <= 7559; i++) + materials[i] = Material.InfestedStone; + for (int i = 7560; i <= 7560; i++) + materials[i] = Material.InfestedCobblestone; + for (int i = 7561; i <= 7561; i++) + materials[i] = Material.InfestedStoneBricks; + for (int i = 7562; i <= 7562; i++) + materials[i] = Material.InfestedMossyStoneBricks; + for (int i = 7563; i <= 7563; i++) + materials[i] = Material.InfestedCrackedStoneBricks; + for (int i = 7564; i <= 7564; i++) + materials[i] = Material.InfestedChiseledStoneBricks; + for (int i = 7565; i <= 7628; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 7629; i <= 7692; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 7693; i <= 7756; i++) + materials[i] = Material.MushroomStem; + for (int i = 7757; i <= 7788; i++) + materials[i] = Material.IronBars; + for (int i = 7789; i <= 7820; i++) + materials[i] = Material.CopperBars; + for (int i = 7821; i <= 7852; i++) + materials[i] = Material.ExposedCopperBars; + for (int i = 7853; i <= 7884; i++) + materials[i] = Material.WeatheredCopperBars; + for (int i = 7885; i <= 7916; i++) + materials[i] = Material.OxidizedCopperBars; + for (int i = 7917; i <= 7948; i++) + materials[i] = Material.WaxedCopperBars; + for (int i = 7949; i <= 7980; i++) + materials[i] = Material.WaxedExposedCopperBars; + for (int i = 7981; i <= 8012; i++) + materials[i] = Material.WaxedWeatheredCopperBars; + for (int i = 8013; i <= 8044; i++) + materials[i] = Material.WaxedOxidizedCopperBars; + for (int i = 8045; i <= 8050; i++) + materials[i] = Material.IronChain; + for (int i = 8051; i <= 8056; i++) + materials[i] = Material.CopperChain; + for (int i = 8057; i <= 8062; i++) + materials[i] = Material.ExposedCopperChain; + for (int i = 8063; i <= 8068; i++) + materials[i] = Material.WeatheredCopperChain; + for (int i = 8069; i <= 8074; i++) + materials[i] = Material.OxidizedCopperChain; + for (int i = 8075; i <= 8080; i++) + materials[i] = Material.WaxedCopperChain; + for (int i = 8081; i <= 8086; i++) + materials[i] = Material.WaxedExposedCopperChain; + for (int i = 8087; i <= 8092; i++) + materials[i] = Material.WaxedWeatheredCopperChain; + for (int i = 8093; i <= 8098; i++) + materials[i] = Material.WaxedOxidizedCopperChain; + for (int i = 8099; i <= 8130; i++) + materials[i] = Material.GlassPane; + for (int i = 8131; i <= 8131; i++) + materials[i] = Material.Pumpkin; + for (int i = 8132; i <= 8132; i++) + materials[i] = Material.Melon; + for (int i = 8133; i <= 8136; i++) + materials[i] = Material.AttachedPumpkinStem; + for (int i = 8137; i <= 8140; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 8141; i <= 8148; i++) + materials[i] = Material.PumpkinStem; + for (int i = 8149; i <= 8156; i++) + materials[i] = Material.MelonStem; + for (int i = 8157; i <= 8188; i++) + materials[i] = Material.Vine; + for (int i = 8189; i <= 8316; i++) + materials[i] = Material.GlowLichen; + for (int i = 8317; i <= 8444; i++) + materials[i] = Material.ResinClump; + for (int i = 8445; i <= 8476; i++) + materials[i] = Material.OakFenceGate; + for (int i = 8477; i <= 8556; i++) + materials[i] = Material.BrickStairs; + for (int i = 8557; i <= 8636; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 8637; i <= 8716; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 8717; i <= 8718; i++) + materials[i] = Material.Mycelium; + for (int i = 8719; i <= 8719; i++) + materials[i] = Material.LilyPad; + for (int i = 8720; i <= 8720; i++) + materials[i] = Material.ResinBlock; + for (int i = 8721; i <= 8721; i++) + materials[i] = Material.ResinBricks; + for (int i = 8722; i <= 8801; i++) + materials[i] = Material.ResinBrickStairs; + for (int i = 8802; i <= 8807; i++) + materials[i] = Material.ResinBrickSlab; + for (int i = 8808; i <= 9131; i++) + materials[i] = Material.ResinBrickWall; + for (int i = 9132; i <= 9132; i++) + materials[i] = Material.ChiseledResinBricks; + for (int i = 9133; i <= 9133; i++) + materials[i] = Material.NetherBricks; + for (int i = 9134; i <= 9165; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 9166; i <= 9245; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 9246; i <= 9249; i++) + materials[i] = Material.NetherWart; + for (int i = 9250; i <= 9250; i++) + materials[i] = Material.EnchantingTable; + for (int i = 9251; i <= 9258; i++) + materials[i] = Material.BrewingStand; + for (int i = 9259; i <= 9259; i++) + materials[i] = Material.Cauldron; + for (int i = 9260; i <= 9262; i++) + materials[i] = Material.WaterCauldron; + for (int i = 9263; i <= 9263; i++) + materials[i] = Material.LavaCauldron; + for (int i = 9264; i <= 9266; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 9267; i <= 9267; i++) + materials[i] = Material.EndPortal; + for (int i = 9268; i <= 9275; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 9276; i <= 9276; i++) + materials[i] = Material.EndStone; + for (int i = 9277; i <= 9277; i++) + materials[i] = Material.DragonEgg; + for (int i = 9278; i <= 9279; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 9280; i <= 9291; i++) + materials[i] = Material.Cocoa; + for (int i = 9292; i <= 9371; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 9372; i <= 9372; i++) + materials[i] = Material.EmeraldOre; + for (int i = 9373; i <= 9373; i++) + materials[i] = Material.DeepslateEmeraldOre; + for (int i = 9374; i <= 9381; i++) + materials[i] = Material.EnderChest; + for (int i = 9382; i <= 9397; i++) + materials[i] = Material.TripwireHook; + for (int i = 9398; i <= 9525; i++) + materials[i] = Material.Tripwire; + for (int i = 9526; i <= 9526; i++) + materials[i] = Material.EmeraldBlock; + for (int i = 9527; i <= 9606; i++) + materials[i] = Material.SpruceStairs; + for (int i = 9607; i <= 9686; i++) + materials[i] = Material.BirchStairs; + for (int i = 9687; i <= 9766; i++) + materials[i] = Material.JungleStairs; + for (int i = 9767; i <= 9778; i++) + materials[i] = Material.CommandBlock; + for (int i = 9779; i <= 9779; i++) + materials[i] = Material.Beacon; + for (int i = 9780; i <= 10103; i++) + materials[i] = Material.CobblestoneWall; + for (int i = 10104; i <= 10427; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 10428; i <= 10428; i++) + materials[i] = Material.FlowerPot; + for (int i = 10429; i <= 10429; i++) + materials[i] = Material.PottedTorchflower; + for (int i = 10430; i <= 10430; i++) + materials[i] = Material.PottedOakSapling; + for (int i = 10431; i <= 10431; i++) + materials[i] = Material.PottedSpruceSapling; + for (int i = 10432; i <= 10432; i++) + materials[i] = Material.PottedBirchSapling; + for (int i = 10433; i <= 10433; i++) + materials[i] = Material.PottedJungleSapling; + for (int i = 10434; i <= 10434; i++) + materials[i] = Material.PottedAcaciaSapling; + for (int i = 10435; i <= 10435; i++) + materials[i] = Material.PottedCherrySapling; + for (int i = 10436; i <= 10436; i++) + materials[i] = Material.PottedDarkOakSapling; + for (int i = 10437; i <= 10437; i++) + materials[i] = Material.PottedPaleOakSapling; + for (int i = 10438; i <= 10438; i++) + materials[i] = Material.PottedMangrovePropagule; + for (int i = 10439; i <= 10439; i++) + materials[i] = Material.PottedFern; + for (int i = 10440; i <= 10440; i++) + materials[i] = Material.PottedDandelion; + for (int i = 10441; i <= 10441; i++) + materials[i] = Material.PottedPoppy; + for (int i = 10442; i <= 10442; i++) + materials[i] = Material.PottedBlueOrchid; + for (int i = 10443; i <= 10443; i++) + materials[i] = Material.PottedAllium; + for (int i = 10444; i <= 10444; i++) + materials[i] = Material.PottedAzureBluet; + for (int i = 10445; i <= 10445; i++) + materials[i] = Material.PottedRedTulip; + for (int i = 10446; i <= 10446; i++) + materials[i] = Material.PottedOrangeTulip; + for (int i = 10447; i <= 10447; i++) + materials[i] = Material.PottedWhiteTulip; + for (int i = 10448; i <= 10448; i++) + materials[i] = Material.PottedPinkTulip; + for (int i = 10449; i <= 10449; i++) + materials[i] = Material.PottedOxeyeDaisy; + for (int i = 10450; i <= 10450; i++) + materials[i] = Material.PottedCornflower; + for (int i = 10451; i <= 10451; i++) + materials[i] = Material.PottedLilyOfTheValley; + for (int i = 10452; i <= 10452; i++) + materials[i] = Material.PottedWitherRose; + for (int i = 10453; i <= 10453; i++) + materials[i] = Material.PottedRedMushroom; + for (int i = 10454; i <= 10454; i++) + materials[i] = Material.PottedBrownMushroom; + for (int i = 10455; i <= 10455; i++) + materials[i] = Material.PottedDeadBush; + for (int i = 10456; i <= 10456; i++) + materials[i] = Material.PottedCactus; + for (int i = 10457; i <= 10464; i++) + materials[i] = Material.Carrots; + for (int i = 10465; i <= 10472; i++) + materials[i] = Material.Potatoes; + for (int i = 10473; i <= 10496; i++) + materials[i] = Material.OakButton; + for (int i = 10497; i <= 10520; i++) + materials[i] = Material.SpruceButton; + for (int i = 10521; i <= 10544; i++) + materials[i] = Material.BirchButton; + for (int i = 10545; i <= 10568; i++) + materials[i] = Material.JungleButton; + for (int i = 10569; i <= 10592; i++) + materials[i] = Material.AcaciaButton; + for (int i = 10593; i <= 10616; i++) + materials[i] = Material.CherryButton; + for (int i = 10617; i <= 10640; i++) + materials[i] = Material.DarkOakButton; + for (int i = 10641; i <= 10664; i++) + materials[i] = Material.PaleOakButton; + for (int i = 10665; i <= 10688; i++) + materials[i] = Material.MangroveButton; + for (int i = 10689; i <= 10712; i++) + materials[i] = Material.BambooButton; + for (int i = 10713; i <= 10744; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 10745; i <= 10752; i++) + materials[i] = Material.SkeletonWallSkull; + for (int i = 10753; i <= 10784; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 10785; i <= 10792; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 10793; i <= 10824; i++) + materials[i] = Material.ZombieHead; + for (int i = 10825; i <= 10832; i++) + materials[i] = Material.ZombieWallHead; + for (int i = 10833; i <= 10864; i++) + materials[i] = Material.PlayerHead; + for (int i = 10865; i <= 10872; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 10873; i <= 10904; i++) + materials[i] = Material.CreeperHead; + for (int i = 10905; i <= 10912; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 10913; i <= 10944; i++) + materials[i] = Material.DragonHead; + for (int i = 10945; i <= 10952; i++) + materials[i] = Material.DragonWallHead; + for (int i = 10953; i <= 10984; i++) + materials[i] = Material.PiglinHead; + for (int i = 10985; i <= 10992; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 10993; i <= 10996; i++) + materials[i] = Material.Anvil; + for (int i = 10997; i <= 11000; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 11001; i <= 11004; i++) + materials[i] = Material.DamagedAnvil; + for (int i = 11005; i <= 11028; i++) + materials[i] = Material.TrappedChest; + for (int i = 11029; i <= 11044; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 11045; i <= 11060; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + for (int i = 11061; i <= 11076; i++) + materials[i] = Material.Comparator; + for (int i = 11077; i <= 11108; i++) + materials[i] = Material.DaylightDetector; + for (int i = 11109; i <= 11109; i++) + materials[i] = Material.RedstoneBlock; + for (int i = 11110; i <= 11110; i++) + materials[i] = Material.NetherQuartzOre; + for (int i = 11111; i <= 11120; i++) + materials[i] = Material.Hopper; + for (int i = 11121; i <= 11121; i++) + materials[i] = Material.QuartzBlock; + for (int i = 11122; i <= 11122; i++) + materials[i] = Material.ChiseledQuartzBlock; + for (int i = 11123; i <= 11125; i++) + materials[i] = Material.QuartzPillar; + for (int i = 11126; i <= 11205; i++) + materials[i] = Material.QuartzStairs; + for (int i = 11206; i <= 11229; i++) + materials[i] = Material.ActivatorRail; + for (int i = 11230; i <= 11241; i++) + materials[i] = Material.Dropper; + for (int i = 11242; i <= 11242; i++) + materials[i] = Material.WhiteTerracotta; + for (int i = 11243; i <= 11243; i++) + materials[i] = Material.OrangeTerracotta; + for (int i = 11244; i <= 11244; i++) + materials[i] = Material.MagentaTerracotta; + for (int i = 11245; i <= 11245; i++) + materials[i] = Material.LightBlueTerracotta; + for (int i = 11246; i <= 11246; i++) + materials[i] = Material.YellowTerracotta; + for (int i = 11247; i <= 11247; i++) + materials[i] = Material.LimeTerracotta; + for (int i = 11248; i <= 11248; i++) + materials[i] = Material.PinkTerracotta; + for (int i = 11249; i <= 11249; i++) + materials[i] = Material.GrayTerracotta; + for (int i = 11250; i <= 11250; i++) + materials[i] = Material.LightGrayTerracotta; + for (int i = 11251; i <= 11251; i++) + materials[i] = Material.CyanTerracotta; + for (int i = 11252; i <= 11252; i++) + materials[i] = Material.PurpleTerracotta; + for (int i = 11253; i <= 11253; i++) + materials[i] = Material.BlueTerracotta; + for (int i = 11254; i <= 11254; i++) + materials[i] = Material.BrownTerracotta; + for (int i = 11255; i <= 11255; i++) + materials[i] = Material.GreenTerracotta; + for (int i = 11256; i <= 11256; i++) + materials[i] = Material.RedTerracotta; + for (int i = 11257; i <= 11257; i++) + materials[i] = Material.BlackTerracotta; + for (int i = 11258; i <= 11289; i++) + materials[i] = Material.WhiteStainedGlassPane; + for (int i = 11290; i <= 11321; i++) + materials[i] = Material.OrangeStainedGlassPane; + for (int i = 11322; i <= 11353; i++) + materials[i] = Material.MagentaStainedGlassPane; + for (int i = 11354; i <= 11385; i++) + materials[i] = Material.LightBlueStainedGlassPane; + for (int i = 11386; i <= 11417; i++) + materials[i] = Material.YellowStainedGlassPane; + for (int i = 11418; i <= 11449; i++) + materials[i] = Material.LimeStainedGlassPane; + for (int i = 11450; i <= 11481; i++) + materials[i] = Material.PinkStainedGlassPane; + for (int i = 11482; i <= 11513; i++) + materials[i] = Material.GrayStainedGlassPane; + for (int i = 11514; i <= 11545; i++) + materials[i] = Material.LightGrayStainedGlassPane; + for (int i = 11546; i <= 11577; i++) + materials[i] = Material.CyanStainedGlassPane; + for (int i = 11578; i <= 11609; i++) + materials[i] = Material.PurpleStainedGlassPane; + for (int i = 11610; i <= 11641; i++) + materials[i] = Material.BlueStainedGlassPane; + for (int i = 11642; i <= 11673; i++) + materials[i] = Material.BrownStainedGlassPane; + for (int i = 11674; i <= 11705; i++) + materials[i] = Material.GreenStainedGlassPane; + for (int i = 11706; i <= 11737; i++) + materials[i] = Material.RedStainedGlassPane; + for (int i = 11738; i <= 11769; i++) + materials[i] = Material.BlackStainedGlassPane; + for (int i = 11770; i <= 11849; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 11850; i <= 11929; i++) + materials[i] = Material.CherryStairs; + for (int i = 11930; i <= 12009; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 12010; i <= 12089; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 12090; i <= 12169; i++) + materials[i] = Material.MangroveStairs; + for (int i = 12170; i <= 12249; i++) + materials[i] = Material.BambooStairs; + for (int i = 12250; i <= 12329; i++) + materials[i] = Material.BambooMosaicStairs; + for (int i = 12330; i <= 12330; i++) + materials[i] = Material.SlimeBlock; + for (int i = 12331; i <= 12332; i++) + materials[i] = Material.Barrier; + for (int i = 12333; i <= 12364; i++) + materials[i] = Material.Light; + for (int i = 12365; i <= 12428; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 12429; i <= 12429; i++) + materials[i] = Material.Prismarine; + for (int i = 12430; i <= 12430; i++) + materials[i] = Material.PrismarineBricks; + for (int i = 12431; i <= 12431; i++) + materials[i] = Material.DarkPrismarine; + for (int i = 12432; i <= 12511; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 12512; i <= 12591; i++) + materials[i] = Material.PrismarineBrickStairs; + for (int i = 12592; i <= 12671; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 12672; i <= 12677; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 12678; i <= 12683; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 12684; i <= 12689; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 12690; i <= 12690; i++) + materials[i] = Material.SeaLantern; + for (int i = 12691; i <= 12693; i++) + materials[i] = Material.HayBlock; + for (int i = 12694; i <= 12694; i++) + materials[i] = Material.WhiteCarpet; + for (int i = 12695; i <= 12695; i++) + materials[i] = Material.OrangeCarpet; + for (int i = 12696; i <= 12696; i++) + materials[i] = Material.MagentaCarpet; + for (int i = 12697; i <= 12697; i++) + materials[i] = Material.LightBlueCarpet; + for (int i = 12698; i <= 12698; i++) + materials[i] = Material.YellowCarpet; + for (int i = 12699; i <= 12699; i++) + materials[i] = Material.LimeCarpet; + for (int i = 12700; i <= 12700; i++) + materials[i] = Material.PinkCarpet; + for (int i = 12701; i <= 12701; i++) + materials[i] = Material.GrayCarpet; + for (int i = 12702; i <= 12702; i++) + materials[i] = Material.LightGrayCarpet; + for (int i = 12703; i <= 12703; i++) + materials[i] = Material.CyanCarpet; + for (int i = 12704; i <= 12704; i++) + materials[i] = Material.PurpleCarpet; + for (int i = 12705; i <= 12705; i++) + materials[i] = Material.BlueCarpet; + for (int i = 12706; i <= 12706; i++) + materials[i] = Material.BrownCarpet; + for (int i = 12707; i <= 12707; i++) + materials[i] = Material.GreenCarpet; + for (int i = 12708; i <= 12708; i++) + materials[i] = Material.RedCarpet; + for (int i = 12709; i <= 12709; i++) + materials[i] = Material.BlackCarpet; + for (int i = 12710; i <= 12710; i++) + materials[i] = Material.Terracotta; + for (int i = 12711; i <= 12711; i++) + materials[i] = Material.CoalBlock; + for (int i = 12712; i <= 12712; i++) + materials[i] = Material.PackedIce; + for (int i = 12713; i <= 12714; i++) + materials[i] = Material.Sunflower; + for (int i = 12715; i <= 12716; i++) + materials[i] = Material.Lilac; + for (int i = 12717; i <= 12718; i++) + materials[i] = Material.RoseBush; + for (int i = 12719; i <= 12720; i++) + materials[i] = Material.Peony; + for (int i = 12721; i <= 12722; i++) + materials[i] = Material.TallGrass; + for (int i = 12723; i <= 12724; i++) + materials[i] = Material.LargeFern; + for (int i = 12725; i <= 12740; i++) + materials[i] = Material.WhiteBanner; + for (int i = 12741; i <= 12756; i++) + materials[i] = Material.OrangeBanner; + for (int i = 12757; i <= 12772; i++) + materials[i] = Material.MagentaBanner; + for (int i = 12773; i <= 12788; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 12789; i <= 12804; i++) + materials[i] = Material.YellowBanner; + for (int i = 12805; i <= 12820; i++) + materials[i] = Material.LimeBanner; + for (int i = 12821; i <= 12836; i++) + materials[i] = Material.PinkBanner; + for (int i = 12837; i <= 12852; i++) + materials[i] = Material.GrayBanner; + for (int i = 12853; i <= 12868; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 12869; i <= 12884; i++) + materials[i] = Material.CyanBanner; + for (int i = 12885; i <= 12900; i++) + materials[i] = Material.PurpleBanner; + for (int i = 12901; i <= 12916; i++) + materials[i] = Material.BlueBanner; + for (int i = 12917; i <= 12932; i++) + materials[i] = Material.BrownBanner; + for (int i = 12933; i <= 12948; i++) + materials[i] = Material.GreenBanner; + for (int i = 12949; i <= 12964; i++) + materials[i] = Material.RedBanner; + for (int i = 12965; i <= 12980; i++) + materials[i] = Material.BlackBanner; + for (int i = 12981; i <= 12984; i++) + materials[i] = Material.WhiteWallBanner; + for (int i = 12985; i <= 12988; i++) + materials[i] = Material.OrangeWallBanner; + for (int i = 12989; i <= 12992; i++) + materials[i] = Material.MagentaWallBanner; + for (int i = 12993; i <= 12996; i++) + materials[i] = Material.LightBlueWallBanner; + for (int i = 12997; i <= 13000; i++) + materials[i] = Material.YellowWallBanner; + for (int i = 13001; i <= 13004; i++) + materials[i] = Material.LimeWallBanner; + for (int i = 13005; i <= 13008; i++) + materials[i] = Material.PinkWallBanner; + for (int i = 13009; i <= 13012; i++) + materials[i] = Material.GrayWallBanner; + for (int i = 13013; i <= 13016; i++) + materials[i] = Material.LightGrayWallBanner; + for (int i = 13017; i <= 13020; i++) + materials[i] = Material.CyanWallBanner; + for (int i = 13021; i <= 13024; i++) + materials[i] = Material.PurpleWallBanner; + for (int i = 13025; i <= 13028; i++) + materials[i] = Material.BlueWallBanner; + for (int i = 13029; i <= 13032; i++) + materials[i] = Material.BrownWallBanner; + for (int i = 13033; i <= 13036; i++) + materials[i] = Material.GreenWallBanner; + for (int i = 13037; i <= 13040; i++) + materials[i] = Material.RedWallBanner; + for (int i = 13041; i <= 13044; i++) + materials[i] = Material.BlackWallBanner; + for (int i = 13045; i <= 13045; i++) + materials[i] = Material.RedSandstone; + for (int i = 13046; i <= 13046; i++) + materials[i] = Material.ChiseledRedSandstone; + for (int i = 13047; i <= 13047; i++) + materials[i] = Material.CutRedSandstone; + for (int i = 13048; i <= 13127; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 13128; i <= 13133; i++) + materials[i] = Material.OakSlab; + for (int i = 13134; i <= 13139; i++) + materials[i] = Material.SpruceSlab; + for (int i = 13140; i <= 13145; i++) + materials[i] = Material.BirchSlab; + for (int i = 13146; i <= 13151; i++) + materials[i] = Material.JungleSlab; + for (int i = 13152; i <= 13157; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 13158; i <= 13163; i++) + materials[i] = Material.CherrySlab; + for (int i = 13164; i <= 13169; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 13170; i <= 13175; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 13176; i <= 13181; i++) + materials[i] = Material.MangroveSlab; + for (int i = 13182; i <= 13187; i++) + materials[i] = Material.BambooSlab; + for (int i = 13188; i <= 13193; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 13194; i <= 13199; i++) + materials[i] = Material.StoneSlab; + for (int i = 13200; i <= 13205; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13206; i <= 13211; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 13212; i <= 13217; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 13218; i <= 13223; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 13224; i <= 13229; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 13230; i <= 13235; i++) + materials[i] = Material.BrickSlab; + for (int i = 13236; i <= 13241; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 13242; i <= 13247; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 13248; i <= 13253; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 13254; i <= 13259; i++) + materials[i] = Material.QuartzSlab; + for (int i = 13260; i <= 13265; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 13266; i <= 13271; i++) + materials[i] = Material.CutRedSandstoneSlab; + for (int i = 13272; i <= 13277; i++) + materials[i] = Material.PurpurSlab; + for (int i = 13278; i <= 13278; i++) + materials[i] = Material.SmoothStone; + for (int i = 13279; i <= 13279; i++) + materials[i] = Material.SmoothSandstone; + for (int i = 13280; i <= 13280; i++) + materials[i] = Material.SmoothQuartz; + for (int i = 13281; i <= 13281; i++) + materials[i] = Material.SmoothRedSandstone; + for (int i = 13282; i <= 13313; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 13314; i <= 13345; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 13346; i <= 13377; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 13378; i <= 13409; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 13410; i <= 13441; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 13442; i <= 13473; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 13474; i <= 13505; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 13506; i <= 13537; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 13538; i <= 13569; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 13570; i <= 13601; i++) + materials[i] = Material.SpruceFence; + for (int i = 13602; i <= 13633; i++) + materials[i] = Material.BirchFence; + for (int i = 13634; i <= 13665; i++) + materials[i] = Material.JungleFence; + for (int i = 13666; i <= 13697; i++) + materials[i] = Material.AcaciaFence; + for (int i = 13698; i <= 13729; i++) + materials[i] = Material.CherryFence; + for (int i = 13730; i <= 13761; i++) + materials[i] = Material.DarkOakFence; + for (int i = 13762; i <= 13793; i++) + materials[i] = Material.PaleOakFence; + for (int i = 13794; i <= 13825; i++) + materials[i] = Material.MangroveFence; + for (int i = 13826; i <= 13857; i++) + materials[i] = Material.BambooFence; + for (int i = 13858; i <= 13921; i++) + materials[i] = Material.SpruceDoor; + for (int i = 13922; i <= 13985; i++) + materials[i] = Material.BirchDoor; + for (int i = 13986; i <= 14049; i++) + materials[i] = Material.JungleDoor; + for (int i = 14050; i <= 14113; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 14114; i <= 14177; i++) + materials[i] = Material.CherryDoor; + for (int i = 14178; i <= 14241; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 14242; i <= 14305; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 14306; i <= 14369; i++) + materials[i] = Material.MangroveDoor; + for (int i = 14370; i <= 14433; i++) + materials[i] = Material.BambooDoor; + for (int i = 14434; i <= 14439; i++) + materials[i] = Material.EndRod; + for (int i = 14440; i <= 14503; i++) + materials[i] = Material.ChorusPlant; + for (int i = 14504; i <= 14509; i++) + materials[i] = Material.ChorusFlower; + for (int i = 14510; i <= 14510; i++) + materials[i] = Material.PurpurBlock; + for (int i = 14511; i <= 14513; i++) + materials[i] = Material.PurpurPillar; + for (int i = 14514; i <= 14593; i++) + materials[i] = Material.PurpurStairs; + for (int i = 14594; i <= 14594; i++) + materials[i] = Material.EndStoneBricks; + for (int i = 14595; i <= 14596; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 14597; i <= 14606; i++) + materials[i] = Material.PitcherCrop; + for (int i = 14607; i <= 14608; i++) + materials[i] = Material.PitcherPlant; + for (int i = 14609; i <= 14612; i++) + materials[i] = Material.Beetroots; + for (int i = 14613; i <= 14613; i++) + materials[i] = Material.DirtPath; + for (int i = 14614; i <= 14614; i++) + materials[i] = Material.EndGateway; + for (int i = 14615; i <= 14626; i++) + materials[i] = Material.RepeatingCommandBlock; + for (int i = 14627; i <= 14638; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 14639; i <= 14642; i++) + materials[i] = Material.FrostedIce; + for (int i = 14643; i <= 14643; i++) + materials[i] = Material.MagmaBlock; + for (int i = 14644; i <= 14644; i++) + materials[i] = Material.NetherWartBlock; + for (int i = 14645; i <= 14645; i++) + materials[i] = Material.RedNetherBricks; + for (int i = 14646; i <= 14648; i++) + materials[i] = Material.BoneBlock; + for (int i = 14649; i <= 14649; i++) + materials[i] = Material.StructureVoid; + for (int i = 14650; i <= 14661; i++) + materials[i] = Material.Observer; + for (int i = 14662; i <= 14667; i++) + materials[i] = Material.ShulkerBox; + for (int i = 14668; i <= 14673; i++) + materials[i] = Material.WhiteShulkerBox; + for (int i = 14674; i <= 14679; i++) + materials[i] = Material.OrangeShulkerBox; + for (int i = 14680; i <= 14685; i++) + materials[i] = Material.MagentaShulkerBox; + for (int i = 14686; i <= 14691; i++) + materials[i] = Material.LightBlueShulkerBox; + for (int i = 14692; i <= 14697; i++) + materials[i] = Material.YellowShulkerBox; + for (int i = 14698; i <= 14703; i++) + materials[i] = Material.LimeShulkerBox; + for (int i = 14704; i <= 14709; i++) + materials[i] = Material.PinkShulkerBox; + for (int i = 14710; i <= 14715; i++) + materials[i] = Material.GrayShulkerBox; + for (int i = 14716; i <= 14721; i++) + materials[i] = Material.LightGrayShulkerBox; + for (int i = 14722; i <= 14727; i++) + materials[i] = Material.CyanShulkerBox; + for (int i = 14728; i <= 14733; i++) + materials[i] = Material.PurpleShulkerBox; + for (int i = 14734; i <= 14739; i++) + materials[i] = Material.BlueShulkerBox; + for (int i = 14740; i <= 14745; i++) + materials[i] = Material.BrownShulkerBox; + for (int i = 14746; i <= 14751; i++) + materials[i] = Material.GreenShulkerBox; + for (int i = 14752; i <= 14757; i++) + materials[i] = Material.RedShulkerBox; + for (int i = 14758; i <= 14763; i++) + materials[i] = Material.BlackShulkerBox; + for (int i = 14764; i <= 14767; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 14768; i <= 14771; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 14772; i <= 14775; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 14776; i <= 14779; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 14780; i <= 14783; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 14784; i <= 14787; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 14788; i <= 14791; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 14792; i <= 14795; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 14796; i <= 14799; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 14800; i <= 14803; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 14804; i <= 14807; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 14808; i <= 14811; i++) + materials[i] = Material.BlueGlazedTerracotta; + for (int i = 14812; i <= 14815; i++) + materials[i] = Material.BrownGlazedTerracotta; + for (int i = 14816; i <= 14819; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 14820; i <= 14823; i++) + materials[i] = Material.RedGlazedTerracotta; + for (int i = 14824; i <= 14827; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 14828; i <= 14828; i++) + materials[i] = Material.WhiteConcrete; + for (int i = 14829; i <= 14829; i++) + materials[i] = Material.OrangeConcrete; + for (int i = 14830; i <= 14830; i++) + materials[i] = Material.MagentaConcrete; + for (int i = 14831; i <= 14831; i++) + materials[i] = Material.LightBlueConcrete; + for (int i = 14832; i <= 14832; i++) + materials[i] = Material.YellowConcrete; + for (int i = 14833; i <= 14833; i++) + materials[i] = Material.LimeConcrete; + for (int i = 14834; i <= 14834; i++) + materials[i] = Material.PinkConcrete; + for (int i = 14835; i <= 14835; i++) + materials[i] = Material.GrayConcrete; + for (int i = 14836; i <= 14836; i++) + materials[i] = Material.LightGrayConcrete; + for (int i = 14837; i <= 14837; i++) + materials[i] = Material.CyanConcrete; + for (int i = 14838; i <= 14838; i++) + materials[i] = Material.PurpleConcrete; + for (int i = 14839; i <= 14839; i++) + materials[i] = Material.BlueConcrete; + for (int i = 14840; i <= 14840; i++) + materials[i] = Material.BrownConcrete; + for (int i = 14841; i <= 14841; i++) + materials[i] = Material.GreenConcrete; + for (int i = 14842; i <= 14842; i++) + materials[i] = Material.RedConcrete; + for (int i = 14843; i <= 14843; i++) + materials[i] = Material.BlackConcrete; + for (int i = 14844; i <= 14844; i++) + materials[i] = Material.WhiteConcretePowder; + for (int i = 14845; i <= 14845; i++) + materials[i] = Material.OrangeConcretePowder; + for (int i = 14846; i <= 14846; i++) + materials[i] = Material.MagentaConcretePowder; + for (int i = 14847; i <= 14847; i++) + materials[i] = Material.LightBlueConcretePowder; + for (int i = 14848; i <= 14848; i++) + materials[i] = Material.YellowConcretePowder; + for (int i = 14849; i <= 14849; i++) + materials[i] = Material.LimeConcretePowder; + for (int i = 14850; i <= 14850; i++) + materials[i] = Material.PinkConcretePowder; + for (int i = 14851; i <= 14851; i++) + materials[i] = Material.GrayConcretePowder; + for (int i = 14852; i <= 14852; i++) + materials[i] = Material.LightGrayConcretePowder; + for (int i = 14853; i <= 14853; i++) + materials[i] = Material.CyanConcretePowder; + for (int i = 14854; i <= 14854; i++) + materials[i] = Material.PurpleConcretePowder; + for (int i = 14855; i <= 14855; i++) + materials[i] = Material.BlueConcretePowder; + for (int i = 14856; i <= 14856; i++) + materials[i] = Material.BrownConcretePowder; + for (int i = 14857; i <= 14857; i++) + materials[i] = Material.GreenConcretePowder; + for (int i = 14858; i <= 14858; i++) + materials[i] = Material.RedConcretePowder; + for (int i = 14859; i <= 14859; i++) + materials[i] = Material.BlackConcretePowder; + for (int i = 14860; i <= 14885; i++) + materials[i] = Material.Kelp; + for (int i = 14886; i <= 14886; i++) + materials[i] = Material.KelpPlant; + for (int i = 14887; i <= 14887; i++) + materials[i] = Material.DriedKelpBlock; + for (int i = 14888; i <= 14899; i++) + materials[i] = Material.TurtleEgg; + for (int i = 14900; i <= 14902; i++) + materials[i] = Material.SnifferEgg; + for (int i = 14903; i <= 14934; i++) + materials[i] = Material.DriedGhast; + for (int i = 14935; i <= 14935; i++) + materials[i] = Material.DeadTubeCoralBlock; + for (int i = 14936; i <= 14936; i++) + materials[i] = Material.DeadBrainCoralBlock; + for (int i = 14937; i <= 14937; i++) + materials[i] = Material.DeadBubbleCoralBlock; + for (int i = 14938; i <= 14938; i++) + materials[i] = Material.DeadFireCoralBlock; + for (int i = 14939; i <= 14939; i++) + materials[i] = Material.DeadHornCoralBlock; + for (int i = 14940; i <= 14940; i++) + materials[i] = Material.TubeCoralBlock; + for (int i = 14941; i <= 14941; i++) + materials[i] = Material.BrainCoralBlock; + for (int i = 14942; i <= 14942; i++) + materials[i] = Material.BubbleCoralBlock; + for (int i = 14943; i <= 14943; i++) + materials[i] = Material.FireCoralBlock; + for (int i = 14944; i <= 14944; i++) + materials[i] = Material.HornCoralBlock; + for (int i = 14945; i <= 14946; i++) + materials[i] = Material.DeadTubeCoral; + for (int i = 14947; i <= 14948; i++) + materials[i] = Material.DeadBrainCoral; + for (int i = 14949; i <= 14950; i++) + materials[i] = Material.DeadBubbleCoral; + for (int i = 14951; i <= 14952; i++) + materials[i] = Material.DeadFireCoral; + for (int i = 14953; i <= 14954; i++) + materials[i] = Material.DeadHornCoral; + for (int i = 14955; i <= 14956; i++) + materials[i] = Material.TubeCoral; + for (int i = 14957; i <= 14958; i++) + materials[i] = Material.BrainCoral; + for (int i = 14959; i <= 14960; i++) + materials[i] = Material.BubbleCoral; + for (int i = 14961; i <= 14962; i++) + materials[i] = Material.FireCoral; + for (int i = 14963; i <= 14964; i++) + materials[i] = Material.HornCoral; + for (int i = 14965; i <= 14966; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 14967; i <= 14968; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 14969; i <= 14970; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 14971; i <= 14972; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 14973; i <= 14974; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 14975; i <= 14976; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 14977; i <= 14978; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 14979; i <= 14980; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 14981; i <= 14982; i++) + materials[i] = Material.FireCoralFan; + for (int i = 14983; i <= 14984; i++) + materials[i] = Material.HornCoralFan; + for (int i = 14985; i <= 14992; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 14993; i <= 15000; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 15001; i <= 15008; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + for (int i = 15009; i <= 15016; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 15017; i <= 15024; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 15025; i <= 15032; i++) + materials[i] = Material.TubeCoralWallFan; + for (int i = 15033; i <= 15040; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 15041; i <= 15048; i++) + materials[i] = Material.BubbleCoralWallFan; + for (int i = 15049; i <= 15056; i++) + materials[i] = Material.FireCoralWallFan; + for (int i = 15057; i <= 15064; i++) + materials[i] = Material.HornCoralWallFan; + for (int i = 15065; i <= 15072; i++) + materials[i] = Material.SeaPickle; + for (int i = 15073; i <= 15073; i++) + materials[i] = Material.BlueIce; + for (int i = 15074; i <= 15075; i++) + materials[i] = Material.Conduit; + for (int i = 15076; i <= 15076; i++) + materials[i] = Material.BambooSapling; + for (int i = 15077; i <= 15088; i++) + materials[i] = Material.Bamboo; + for (int i = 15089; i <= 15089; i++) + materials[i] = Material.PottedBamboo; + for (int i = 15090; i <= 15090; i++) + materials[i] = Material.VoidAir; + for (int i = 15091; i <= 15091; i++) + materials[i] = Material.CaveAir; + for (int i = 15092; i <= 15093; i++) + materials[i] = Material.BubbleColumn; + for (int i = 15094; i <= 15173; i++) + materials[i] = Material.PolishedGraniteStairs; + for (int i = 15174; i <= 15253; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + for (int i = 15254; i <= 15333; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 15334; i <= 15413; i++) + materials[i] = Material.PolishedDioriteStairs; + for (int i = 15414; i <= 15493; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 15494; i <= 15573; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 15574; i <= 15653; i++) + materials[i] = Material.StoneStairs; + for (int i = 15654; i <= 15733; i++) + materials[i] = Material.SmoothSandstoneStairs; + for (int i = 15734; i <= 15813; i++) + materials[i] = Material.SmoothQuartzStairs; + for (int i = 15814; i <= 15893; i++) + materials[i] = Material.GraniteStairs; + for (int i = 15894; i <= 15973; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 15974; i <= 16053; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 16054; i <= 16133; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 16134; i <= 16213; i++) + materials[i] = Material.DioriteStairs; + for (int i = 16214; i <= 16219; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 16220; i <= 16225; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 16226; i <= 16231; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 16232; i <= 16237; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 16238; i <= 16243; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 16244; i <= 16249; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 16250; i <= 16255; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 16256; i <= 16261; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 16262; i <= 16267; i++) + materials[i] = Material.GraniteSlab; + for (int i = 16268; i <= 16273; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 16274; i <= 16279; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 16280; i <= 16285; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 16286; i <= 16291; i++) + materials[i] = Material.DioriteSlab; + for (int i = 16292; i <= 16615; i++) + materials[i] = Material.BrickWall; + for (int i = 16616; i <= 16939; i++) + materials[i] = Material.PrismarineWall; + for (int i = 16940; i <= 17263; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 17264; i <= 17587; i++) + materials[i] = Material.MossyStoneBrickWall; + for (int i = 17588; i <= 17911; i++) + materials[i] = Material.GraniteWall; + for (int i = 17912; i <= 18235; i++) + materials[i] = Material.StoneBrickWall; + for (int i = 18236; i <= 18559; i++) + materials[i] = Material.MudBrickWall; + for (int i = 18560; i <= 18883; i++) + materials[i] = Material.NetherBrickWall; + for (int i = 18884; i <= 19207; i++) + materials[i] = Material.AndesiteWall; + for (int i = 19208; i <= 19531; i++) + materials[i] = Material.RedNetherBrickWall; + for (int i = 19532; i <= 19855; i++) + materials[i] = Material.SandstoneWall; + for (int i = 19856; i <= 20179; i++) + materials[i] = Material.EndStoneBrickWall; + for (int i = 20180; i <= 20503; i++) + materials[i] = Material.DioriteWall; + for (int i = 20504; i <= 20535; i++) + materials[i] = Material.Scaffolding; + for (int i = 20536; i <= 20539; i++) + materials[i] = Material.Loom; + for (int i = 20540; i <= 20551; i++) + materials[i] = Material.Barrel; + for (int i = 20552; i <= 20559; i++) + materials[i] = Material.Smoker; + for (int i = 20560; i <= 20567; i++) + materials[i] = Material.BlastFurnace; + for (int i = 20568; i <= 20568; i++) + materials[i] = Material.CartographyTable; + for (int i = 20569; i <= 20569; i++) + materials[i] = Material.FletchingTable; + for (int i = 20570; i <= 20581; i++) + materials[i] = Material.Grindstone; + for (int i = 20582; i <= 20597; i++) + materials[i] = Material.Lectern; + for (int i = 20598; i <= 20598; i++) + materials[i] = Material.SmithingTable; + for (int i = 20599; i <= 20602; i++) + materials[i] = Material.Stonecutter; + for (int i = 20603; i <= 20634; i++) + materials[i] = Material.Bell; + for (int i = 20635; i <= 20638; i++) + materials[i] = Material.Lantern; + for (int i = 20639; i <= 20642; i++) + materials[i] = Material.SoulLantern; + for (int i = 20643; i <= 20646; i++) + materials[i] = Material.CopperLantern; + for (int i = 20647; i <= 20650; i++) + materials[i] = Material.ExposedCopperLantern; + for (int i = 20651; i <= 20654; i++) + materials[i] = Material.WeatheredCopperLantern; + for (int i = 20655; i <= 20658; i++) + materials[i] = Material.OxidizedCopperLantern; + for (int i = 20659; i <= 20662; i++) + materials[i] = Material.WaxedCopperLantern; + for (int i = 20663; i <= 20666; i++) + materials[i] = Material.WaxedExposedCopperLantern; + for (int i = 20667; i <= 20670; i++) + materials[i] = Material.WaxedWeatheredCopperLantern; + for (int i = 20671; i <= 20674; i++) + materials[i] = Material.WaxedOxidizedCopperLantern; + for (int i = 20675; i <= 20706; i++) + materials[i] = Material.Campfire; + for (int i = 20707; i <= 20738; i++) + materials[i] = Material.SoulCampfire; + for (int i = 20739; i <= 20742; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 20743; i <= 20745; i++) + materials[i] = Material.WarpedStem; + for (int i = 20746; i <= 20748; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 20749; i <= 20751; i++) + materials[i] = Material.WarpedHyphae; + for (int i = 20752; i <= 20754; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 20755; i <= 20755; i++) + materials[i] = Material.WarpedNylium; + for (int i = 20756; i <= 20756; i++) + materials[i] = Material.WarpedFungus; + for (int i = 20757; i <= 20757; i++) + materials[i] = Material.WarpedWartBlock; + for (int i = 20758; i <= 20758; i++) + materials[i] = Material.WarpedRoots; + for (int i = 20759; i <= 20759; i++) + materials[i] = Material.NetherSprouts; + for (int i = 20760; i <= 20762; i++) + materials[i] = Material.CrimsonStem; + for (int i = 20763; i <= 20765; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 20766; i <= 20768; i++) + materials[i] = Material.CrimsonHyphae; + for (int i = 20769; i <= 20771; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 20772; i <= 20772; i++) + materials[i] = Material.CrimsonNylium; + for (int i = 20773; i <= 20773; i++) + materials[i] = Material.CrimsonFungus; + for (int i = 20774; i <= 20774; i++) + materials[i] = Material.Shroomlight; + for (int i = 20775; i <= 20800; i++) + materials[i] = Material.WeepingVines; + for (int i = 20801; i <= 20801; i++) + materials[i] = Material.WeepingVinesPlant; + for (int i = 20802; i <= 20827; i++) + materials[i] = Material.TwistingVines; + for (int i = 20828; i <= 20828; i++) + materials[i] = Material.TwistingVinesPlant; + for (int i = 20829; i <= 20829; i++) + materials[i] = Material.CrimsonRoots; + for (int i = 20830; i <= 20830; i++) + materials[i] = Material.CrimsonPlanks; + for (int i = 20831; i <= 20831; i++) + materials[i] = Material.WarpedPlanks; + for (int i = 20832; i <= 20837; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 20838; i <= 20843; i++) + materials[i] = Material.WarpedSlab; + for (int i = 20844; i <= 20845; i++) + materials[i] = Material.CrimsonPressurePlate; + for (int i = 20846; i <= 20847; i++) + materials[i] = Material.WarpedPressurePlate; + for (int i = 20848; i <= 20879; i++) + materials[i] = Material.CrimsonFence; + for (int i = 20880; i <= 20911; i++) + materials[i] = Material.WarpedFence; + for (int i = 20912; i <= 20975; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 20976; i <= 21039; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 21040; i <= 21071; i++) + materials[i] = Material.CrimsonFenceGate; + for (int i = 21072; i <= 21103; i++) + materials[i] = Material.WarpedFenceGate; + for (int i = 21104; i <= 21183; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 21184; i <= 21263; i++) + materials[i] = Material.WarpedStairs; + for (int i = 21264; i <= 21287; i++) + materials[i] = Material.CrimsonButton; + for (int i = 21288; i <= 21311; i++) + materials[i] = Material.WarpedButton; + for (int i = 21312; i <= 21375; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 21376; i <= 21439; i++) + materials[i] = Material.WarpedDoor; + for (int i = 21440; i <= 21471; i++) + materials[i] = Material.CrimsonSign; + for (int i = 21472; i <= 21503; i++) + materials[i] = Material.WarpedSign; + for (int i = 21504; i <= 21511; i++) + materials[i] = Material.CrimsonWallSign; + for (int i = 21512; i <= 21519; i++) + materials[i] = Material.WarpedWallSign; + for (int i = 21520; i <= 21523; i++) + materials[i] = Material.StructureBlock; + for (int i = 21524; i <= 21535; i++) + materials[i] = Material.Jigsaw; + for (int i = 21536; i <= 21539; i++) + materials[i] = Material.TestBlock; + for (int i = 21540; i <= 21540; i++) + materials[i] = Material.TestInstanceBlock; + for (int i = 21541; i <= 21549; i++) + materials[i] = Material.Composter; + for (int i = 21550; i <= 21565; i++) + materials[i] = Material.Target; + for (int i = 21566; i <= 21589; i++) + materials[i] = Material.BeeNest; + for (int i = 21590; i <= 21613; i++) + materials[i] = Material.Beehive; + for (int i = 21614; i <= 21614; i++) + materials[i] = Material.HoneyBlock; + for (int i = 21615; i <= 21615; i++) + materials[i] = Material.HoneycombBlock; + for (int i = 21616; i <= 21616; i++) + materials[i] = Material.NetheriteBlock; + for (int i = 21617; i <= 21617; i++) + materials[i] = Material.AncientDebris; + for (int i = 21618; i <= 21618; i++) + materials[i] = Material.CryingObsidian; + for (int i = 21619; i <= 21623; i++) + materials[i] = Material.RespawnAnchor; + for (int i = 21624; i <= 21624; i++) + materials[i] = Material.PottedCrimsonFungus; + for (int i = 21625; i <= 21625; i++) + materials[i] = Material.PottedWarpedFungus; + for (int i = 21626; i <= 21626; i++) + materials[i] = Material.PottedCrimsonRoots; + for (int i = 21627; i <= 21627; i++) + materials[i] = Material.PottedWarpedRoots; + for (int i = 21628; i <= 21628; i++) + materials[i] = Material.Lodestone; + for (int i = 21629; i <= 21629; i++) + materials[i] = Material.Blackstone; + for (int i = 21630; i <= 21709; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 21710; i <= 22033; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 22034; i <= 22039; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 22040; i <= 22040; i++) + materials[i] = Material.PolishedBlackstone; + for (int i = 22041; i <= 22041; i++) + materials[i] = Material.PolishedBlackstoneBricks; + for (int i = 22042; i <= 22042; i++) + materials[i] = Material.CrackedPolishedBlackstoneBricks; + for (int i = 22043; i <= 22043; i++) + materials[i] = Material.ChiseledPolishedBlackstone; + for (int i = 22044; i <= 22049; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 22050; i <= 22129; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 22130; i <= 22453; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + for (int i = 22454; i <= 22454; i++) + materials[i] = Material.GildedBlackstone; + for (int i = 22455; i <= 22534; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 22535; i <= 22540; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 22541; i <= 22542; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 22543; i <= 22566; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 22567; i <= 22890; i++) + materials[i] = Material.PolishedBlackstoneWall; + for (int i = 22891; i <= 22891; i++) + materials[i] = Material.ChiseledNetherBricks; + for (int i = 22892; i <= 22892; i++) + materials[i] = Material.CrackedNetherBricks; + for (int i = 22893; i <= 22893; i++) + materials[i] = Material.QuartzBricks; + for (int i = 22894; i <= 22909; i++) + materials[i] = Material.Candle; + for (int i = 22910; i <= 22925; i++) + materials[i] = Material.WhiteCandle; + for (int i = 22926; i <= 22941; i++) + materials[i] = Material.OrangeCandle; + for (int i = 22942; i <= 22957; i++) + materials[i] = Material.MagentaCandle; + for (int i = 22958; i <= 22973; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 22974; i <= 22989; i++) + materials[i] = Material.YellowCandle; + for (int i = 22990; i <= 23005; i++) + materials[i] = Material.LimeCandle; + for (int i = 23006; i <= 23021; i++) + materials[i] = Material.PinkCandle; + for (int i = 23022; i <= 23037; i++) + materials[i] = Material.GrayCandle; + for (int i = 23038; i <= 23053; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 23054; i <= 23069; i++) + materials[i] = Material.CyanCandle; + for (int i = 23070; i <= 23085; i++) + materials[i] = Material.PurpleCandle; + for (int i = 23086; i <= 23101; i++) + materials[i] = Material.BlueCandle; + for (int i = 23102; i <= 23117; i++) + materials[i] = Material.BrownCandle; + for (int i = 23118; i <= 23133; i++) + materials[i] = Material.GreenCandle; + for (int i = 23134; i <= 23149; i++) + materials[i] = Material.RedCandle; + for (int i = 23150; i <= 23165; i++) + materials[i] = Material.BlackCandle; + for (int i = 23166; i <= 23167; i++) + materials[i] = Material.CandleCake; + for (int i = 23168; i <= 23169; i++) + materials[i] = Material.WhiteCandleCake; + for (int i = 23170; i <= 23171; i++) + materials[i] = Material.OrangeCandleCake; + for (int i = 23172; i <= 23173; i++) + materials[i] = Material.MagentaCandleCake; + for (int i = 23174; i <= 23175; i++) + materials[i] = Material.LightBlueCandleCake; + for (int i = 23176; i <= 23177; i++) + materials[i] = Material.YellowCandleCake; + for (int i = 23178; i <= 23179; i++) + materials[i] = Material.LimeCandleCake; + for (int i = 23180; i <= 23181; i++) + materials[i] = Material.PinkCandleCake; + for (int i = 23182; i <= 23183; i++) + materials[i] = Material.GrayCandleCake; + for (int i = 23184; i <= 23185; i++) + materials[i] = Material.LightGrayCandleCake; + for (int i = 23186; i <= 23187; i++) + materials[i] = Material.CyanCandleCake; + for (int i = 23188; i <= 23189; i++) + materials[i] = Material.PurpleCandleCake; + for (int i = 23190; i <= 23191; i++) + materials[i] = Material.BlueCandleCake; + for (int i = 23192; i <= 23193; i++) + materials[i] = Material.BrownCandleCake; + for (int i = 23194; i <= 23195; i++) + materials[i] = Material.GreenCandleCake; + for (int i = 23196; i <= 23197; i++) + materials[i] = Material.RedCandleCake; + for (int i = 23198; i <= 23199; i++) + materials[i] = Material.BlackCandleCake; + for (int i = 23200; i <= 23200; i++) + materials[i] = Material.AmethystBlock; + for (int i = 23201; i <= 23201; i++) + materials[i] = Material.BuddingAmethyst; + for (int i = 23202; i <= 23213; i++) + materials[i] = Material.AmethystCluster; + for (int i = 23214; i <= 23225; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 23226; i <= 23237; i++) + materials[i] = Material.MediumAmethystBud; + for (int i = 23238; i <= 23249; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 23250; i <= 23250; i++) + materials[i] = Material.Tuff; + for (int i = 23251; i <= 23256; i++) + materials[i] = Material.TuffSlab; + for (int i = 23257; i <= 23336; i++) + materials[i] = Material.TuffStairs; + for (int i = 23337; i <= 23660; i++) + materials[i] = Material.TuffWall; + for (int i = 23661; i <= 23661; i++) + materials[i] = Material.PolishedTuff; + for (int i = 23662; i <= 23667; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 23668; i <= 23747; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 23748; i <= 24071; i++) + materials[i] = Material.PolishedTuffWall; + for (int i = 24072; i <= 24072; i++) + materials[i] = Material.ChiseledTuff; + for (int i = 24073; i <= 24073; i++) + materials[i] = Material.TuffBricks; + for (int i = 24074; i <= 24079; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 24080; i <= 24159; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 24160; i <= 24483; i++) + materials[i] = Material.TuffBrickWall; + for (int i = 24484; i <= 24484; i++) + materials[i] = Material.ChiseledTuffBricks; + for (int i = 24485; i <= 24485; i++) + materials[i] = Material.Calcite; + for (int i = 24486; i <= 24486; i++) + materials[i] = Material.TintedGlass; + for (int i = 24487; i <= 24487; i++) + materials[i] = Material.PowderSnow; + for (int i = 24488; i <= 24583; i++) + materials[i] = Material.SculkSensor; + for (int i = 24584; i <= 24967; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 24968; i <= 24968; i++) + materials[i] = Material.Sculk; + for (int i = 24969; i <= 25096; i++) + materials[i] = Material.SculkVein; + for (int i = 25097; i <= 25098; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 25099; i <= 25106; i++) + materials[i] = Material.SculkShrieker; + for (int i = 25107; i <= 25107; i++) + materials[i] = Material.CopperBlock; + for (int i = 25108; i <= 25108; i++) + materials[i] = Material.ExposedCopper; + for (int i = 25109; i <= 25109; i++) + materials[i] = Material.WeatheredCopper; + for (int i = 25110; i <= 25110; i++) + materials[i] = Material.OxidizedCopper; + for (int i = 25111; i <= 25111; i++) + materials[i] = Material.CopperOre; + for (int i = 25112; i <= 25112; i++) + materials[i] = Material.DeepslateCopperOre; + for (int i = 25113; i <= 25113; i++) + materials[i] = Material.OxidizedCutCopper; + for (int i = 25114; i <= 25114; i++) + materials[i] = Material.WeatheredCutCopper; + for (int i = 25115; i <= 25115; i++) + materials[i] = Material.ExposedCutCopper; + for (int i = 25116; i <= 25116; i++) + materials[i] = Material.CutCopper; + for (int i = 25117; i <= 25117; i++) + materials[i] = Material.OxidizedChiseledCopper; + for (int i = 25118; i <= 25118; i++) + materials[i] = Material.WeatheredChiseledCopper; + for (int i = 25119; i <= 25119; i++) + materials[i] = Material.ExposedChiseledCopper; + for (int i = 25120; i <= 25120; i++) + materials[i] = Material.ChiseledCopper; + for (int i = 25121; i <= 25121; i++) + materials[i] = Material.WaxedOxidizedChiseledCopper; + for (int i = 25122; i <= 25122; i++) + materials[i] = Material.WaxedWeatheredChiseledCopper; + for (int i = 25123; i <= 25123; i++) + materials[i] = Material.WaxedExposedChiseledCopper; + for (int i = 25124; i <= 25124; i++) + materials[i] = Material.WaxedChiseledCopper; + for (int i = 25125; i <= 25204; i++) + materials[i] = Material.OxidizedCutCopperStairs; + for (int i = 25205; i <= 25284; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 25285; i <= 25364; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 25365; i <= 25444; i++) + materials[i] = Material.CutCopperStairs; + for (int i = 25445; i <= 25450; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 25451; i <= 25456; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 25457; i <= 25462; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 25463; i <= 25468; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 25469; i <= 25469; i++) + materials[i] = Material.WaxedCopperBlock; + for (int i = 25470; i <= 25470; i++) + materials[i] = Material.WaxedWeatheredCopper; + for (int i = 25471; i <= 25471; i++) + materials[i] = Material.WaxedExposedCopper; + for (int i = 25472; i <= 25472; i++) + materials[i] = Material.WaxedOxidizedCopper; + for (int i = 25473; i <= 25473; i++) + materials[i] = Material.WaxedOxidizedCutCopper; + for (int i = 25474; i <= 25474; i++) + materials[i] = Material.WaxedWeatheredCutCopper; + for (int i = 25475; i <= 25475; i++) + materials[i] = Material.WaxedExposedCutCopper; + for (int i = 25476; i <= 25476; i++) + materials[i] = Material.WaxedCutCopper; + for (int i = 25477; i <= 25556; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + for (int i = 25557; i <= 25636; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + for (int i = 25637; i <= 25716; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + for (int i = 25717; i <= 25796; i++) + materials[i] = Material.WaxedCutCopperStairs; + for (int i = 25797; i <= 25802; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 25803; i <= 25808; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 25809; i <= 25814; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 25815; i <= 25820; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 25821; i <= 25884; i++) + materials[i] = Material.CopperDoor; + for (int i = 25885; i <= 25948; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 25949; i <= 26012; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 26013; i <= 26076; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 26077; i <= 26140; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 26141; i <= 26204; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 26205; i <= 26268; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 26269; i <= 26332; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 26333; i <= 26396; i++) + materials[i] = Material.CopperTrapdoor; + for (int i = 26397; i <= 26460; i++) + materials[i] = Material.ExposedCopperTrapdoor; + for (int i = 26461; i <= 26524; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + for (int i = 26525; i <= 26588; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + for (int i = 26589; i <= 26652; i++) + materials[i] = Material.WaxedCopperTrapdoor; + for (int i = 26653; i <= 26716; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + for (int i = 26717; i <= 26780; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + for (int i = 26781; i <= 26844; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + for (int i = 26845; i <= 26846; i++) + materials[i] = Material.CopperGrate; + for (int i = 26847; i <= 26848; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 26849; i <= 26850; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 26851; i <= 26852; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 26853; i <= 26854; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 26855; i <= 26856; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 26857; i <= 26858; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 26859; i <= 26860; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 26861; i <= 26864; i++) + materials[i] = Material.CopperBulb; + for (int i = 26865; i <= 26868; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 26869; i <= 26872; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 26873; i <= 26876; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 26877; i <= 26880; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 26881; i <= 26884; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 26885; i <= 26888; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 26889; i <= 26892; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 26893; i <= 26916; i++) + materials[i] = Material.CopperChest; + for (int i = 26917; i <= 26940; i++) + materials[i] = Material.ExposedCopperChest; + for (int i = 26941; i <= 26964; i++) + materials[i] = Material.WeatheredCopperChest; + for (int i = 26965; i <= 26988; i++) + materials[i] = Material.OxidizedCopperChest; + for (int i = 26989; i <= 27012; i++) + materials[i] = Material.WaxedCopperChest; + for (int i = 27013; i <= 27036; i++) + materials[i] = Material.WaxedExposedCopperChest; + for (int i = 27037; i <= 27060; i++) + materials[i] = Material.WaxedWeatheredCopperChest; + for (int i = 27061; i <= 27084; i++) + materials[i] = Material.WaxedOxidizedCopperChest; + for (int i = 27085; i <= 27116; i++) + materials[i] = Material.CopperGolemStatue; + for (int i = 27117; i <= 27148; i++) + materials[i] = Material.ExposedCopperGolemStatue; + for (int i = 27149; i <= 27180; i++) + materials[i] = Material.WeatheredCopperGolemStatue; + for (int i = 27181; i <= 27212; i++) + materials[i] = Material.OxidizedCopperGolemStatue; + for (int i = 27213; i <= 27244; i++) + materials[i] = Material.WaxedCopperGolemStatue; + for (int i = 27245; i <= 27276; i++) + materials[i] = Material.WaxedExposedCopperGolemStatue; + for (int i = 27277; i <= 27308; i++) + materials[i] = Material.WaxedWeatheredCopperGolemStatue; + for (int i = 27309; i <= 27340; i++) + materials[i] = Material.WaxedOxidizedCopperGolemStatue; + for (int i = 27341; i <= 27364; i++) + materials[i] = Material.LightningRod; + for (int i = 27365; i <= 27388; i++) + materials[i] = Material.ExposedLightningRod; + for (int i = 27389; i <= 27412; i++) + materials[i] = Material.WeatheredLightningRod; + for (int i = 27413; i <= 27436; i++) + materials[i] = Material.OxidizedLightningRod; + for (int i = 27437; i <= 27460; i++) + materials[i] = Material.WaxedLightningRod; + for (int i = 27461; i <= 27484; i++) + materials[i] = Material.WaxedExposedLightningRod; + for (int i = 27485; i <= 27508; i++) + materials[i] = Material.WaxedWeatheredLightningRod; + for (int i = 27509; i <= 27532; i++) + materials[i] = Material.WaxedOxidizedLightningRod; + for (int i = 27533; i <= 27552; i++) + materials[i] = Material.PointedDripstone; + for (int i = 27553; i <= 27553; i++) + materials[i] = Material.DripstoneBlock; + for (int i = 27554; i <= 27605; i++) + materials[i] = Material.CaveVines; + for (int i = 27606; i <= 27607; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 27608; i <= 27608; i++) + materials[i] = Material.SporeBlossom; + for (int i = 27609; i <= 27609; i++) + materials[i] = Material.Azalea; + for (int i = 27610; i <= 27610; i++) + materials[i] = Material.FloweringAzalea; + for (int i = 27611; i <= 27611; i++) + materials[i] = Material.MossCarpet; + for (int i = 27612; i <= 27627; i++) + materials[i] = Material.PinkPetals; + for (int i = 27628; i <= 27643; i++) + materials[i] = Material.Wildflowers; + for (int i = 27644; i <= 27659; i++) + materials[i] = Material.LeafLitter; + for (int i = 27660; i <= 27660; i++) + materials[i] = Material.MossBlock; + for (int i = 27661; i <= 27692; i++) + materials[i] = Material.BigDripleaf; + for (int i = 27693; i <= 27700; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 27701; i <= 27716; i++) + materials[i] = Material.SmallDripleaf; + for (int i = 27717; i <= 27718; i++) + materials[i] = Material.HangingRoots; + for (int i = 27719; i <= 27719; i++) + materials[i] = Material.RootedDirt; + for (int i = 27720; i <= 27720; i++) + materials[i] = Material.Mud; + for (int i = 27721; i <= 27723; i++) + materials[i] = Material.Deepslate; + for (int i = 27724; i <= 27724; i++) + materials[i] = Material.CobbledDeepslate; + for (int i = 27725; i <= 27804; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 27805; i <= 27810; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 27811; i <= 28134; i++) + materials[i] = Material.CobbledDeepslateWall; + for (int i = 28135; i <= 28135; i++) + materials[i] = Material.PolishedDeepslate; + for (int i = 28136; i <= 28215; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 28216; i <= 28221; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 28222; i <= 28545; i++) + materials[i] = Material.PolishedDeepslateWall; + for (int i = 28546; i <= 28546; i++) + materials[i] = Material.DeepslateTiles; + for (int i = 28547; i <= 28626; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 28627; i <= 28632; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 28633; i <= 28956; i++) + materials[i] = Material.DeepslateTileWall; + for (int i = 28957; i <= 28957; i++) + materials[i] = Material.DeepslateBricks; + for (int i = 28958; i <= 29037; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 29038; i <= 29043; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 29044; i <= 29367; i++) + materials[i] = Material.DeepslateBrickWall; + for (int i = 29368; i <= 29368; i++) + materials[i] = Material.ChiseledDeepslate; + for (int i = 29369; i <= 29369; i++) + materials[i] = Material.CrackedDeepslateBricks; + for (int i = 29370; i <= 29370; i++) + materials[i] = Material.CrackedDeepslateTiles; + for (int i = 29371; i <= 29373; i++) + materials[i] = Material.InfestedDeepslate; + for (int i = 29374; i <= 29374; i++) + materials[i] = Material.SmoothBasalt; + for (int i = 29375; i <= 29375; i++) + materials[i] = Material.RawIronBlock; + for (int i = 29376; i <= 29376; i++) + materials[i] = Material.RawCopperBlock; + for (int i = 29377; i <= 29377; i++) + materials[i] = Material.RawGoldBlock; + for (int i = 29378; i <= 29378; i++) + materials[i] = Material.PottedAzaleaBush; + for (int i = 29379; i <= 29379; i++) + materials[i] = Material.PottedFloweringAzaleaBush; + for (int i = 29380; i <= 29382; i++) + materials[i] = Material.OchreFroglight; + for (int i = 29383; i <= 29385; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 29386; i <= 29388; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 29389; i <= 29389; i++) + materials[i] = Material.Frogspawn; + for (int i = 29390; i <= 29390; i++) + materials[i] = Material.ReinforcedDeepslate; + for (int i = 29391; i <= 29406; i++) + materials[i] = Material.DecoratedPot; + for (int i = 29407; i <= 29454; i++) + materials[i] = Material.Crafter; + for (int i = 29455; i <= 29466; i++) + materials[i] = Material.TrialSpawner; + for (int i = 29467; i <= 29498; i++) + materials[i] = Material.Vault; + for (int i = 29499; i <= 29500; i++) + materials[i] = Material.HeavyCore; + for (int i = 29501; i <= 29501; i++) + materials[i] = Material.PaleMossBlock; + for (int i = 29502; i <= 29663; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 29664; i <= 29665; i++) + materials[i] = Material.PaleHangingMoss; + for (int i = 29666; i <= 29666; i++) + materials[i] = Material.OpenEyeblossom; + for (int i = 29667; i <= 29667; i++) + materials[i] = Material.ClosedEyeblossom; + for (int i = 29668; i <= 29668; i++) + materials[i] = Material.PottedOpenEyeblossom; + for (int i = 29669; i <= 29669; i++) + materials[i] = Material.PottedClosedEyeblossom; + for (int i = 29670; i <= 29670; i++) + materials[i] = Material.FireflyBush; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette261.cs b/MinecraftClient/Mapping/BlockPalettes/Palette261.cs new file mode 100644 index 00000000..5a2b7ac0 --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette261.cs @@ -0,0 +1,2354 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette261 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette261() + { + for (int i = 0; i <= 0; i++) + materials[i] = Material.Air; + for (int i = 1; i <= 1; i++) + materials[i] = Material.Stone; + for (int i = 2; i <= 2; i++) + materials[i] = Material.Granite; + for (int i = 3; i <= 3; i++) + materials[i] = Material.PolishedGranite; + for (int i = 4; i <= 4; i++) + materials[i] = Material.Diorite; + for (int i = 5; i <= 5; i++) + materials[i] = Material.PolishedDiorite; + for (int i = 6; i <= 6; i++) + materials[i] = Material.Andesite; + for (int i = 7; i <= 7; i++) + materials[i] = Material.PolishedAndesite; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + for (int i = 10; i <= 10; i++) + materials[i] = Material.Dirt; + for (int i = 11; i <= 11; i++) + materials[i] = Material.CoarseDirt; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 14; i <= 14; i++) + materials[i] = Material.Cobblestone; + for (int i = 15; i <= 15; i++) + materials[i] = Material.OakPlanks; + for (int i = 16; i <= 16; i++) + materials[i] = Material.SprucePlanks; + for (int i = 17; i <= 17; i++) + materials[i] = Material.BirchPlanks; + for (int i = 18; i <= 18; i++) + materials[i] = Material.JunglePlanks; + for (int i = 19; i <= 19; i++) + materials[i] = Material.AcaciaPlanks; + for (int i = 20; i <= 20; i++) + materials[i] = Material.CherryPlanks; + for (int i = 21; i <= 21; i++) + materials[i] = Material.DarkOakPlanks; + for (int i = 22; i <= 24; i++) + materials[i] = Material.PaleOakWood; + for (int i = 25; i <= 25; i++) + materials[i] = Material.PaleOakPlanks; + for (int i = 26; i <= 26; i++) + materials[i] = Material.MangrovePlanks; + for (int i = 27; i <= 27; i++) + materials[i] = Material.BambooPlanks; + for (int i = 28; i <= 28; i++) + materials[i] = Material.BambooMosaic; + for (int i = 29; i <= 30; i++) + materials[i] = Material.OakSapling; + for (int i = 31; i <= 32; i++) + materials[i] = Material.SpruceSapling; + for (int i = 33; i <= 34; i++) + materials[i] = Material.BirchSapling; + for (int i = 35; i <= 36; i++) + materials[i] = Material.JungleSapling; + for (int i = 37; i <= 38; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 39; i <= 40; i++) + materials[i] = Material.CherrySapling; + for (int i = 41; i <= 42; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 43; i <= 44; i++) + materials[i] = Material.PaleOakSapling; + for (int i = 45; i <= 84; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 85; i <= 85; i++) + materials[i] = Material.Bedrock; + for (int i = 86; i <= 101; i++) + materials[i] = Material.Water; + for (int i = 102; i <= 117; i++) + materials[i] = Material.Lava; + for (int i = 118; i <= 118; i++) + materials[i] = Material.Sand; + for (int i = 119; i <= 122; i++) + materials[i] = Material.SuspiciousSand; + for (int i = 123; i <= 123; i++) + materials[i] = Material.RedSand; + for (int i = 124; i <= 124; i++) + materials[i] = Material.Gravel; + for (int i = 125; i <= 128; i++) + materials[i] = Material.SuspiciousGravel; + for (int i = 129; i <= 129; i++) + materials[i] = Material.GoldOre; + for (int i = 130; i <= 130; i++) + materials[i] = Material.DeepslateGoldOre; + for (int i = 131; i <= 131; i++) + materials[i] = Material.IronOre; + for (int i = 132; i <= 132; i++) + materials[i] = Material.DeepslateIronOre; + for (int i = 133; i <= 133; i++) + materials[i] = Material.CoalOre; + for (int i = 134; i <= 134; i++) + materials[i] = Material.DeepslateCoalOre; + for (int i = 135; i <= 135; i++) + materials[i] = Material.NetherGoldOre; + for (int i = 136; i <= 138; i++) + materials[i] = Material.OakLog; + for (int i = 139; i <= 141; i++) + materials[i] = Material.SpruceLog; + for (int i = 142; i <= 144; i++) + materials[i] = Material.BirchLog; + for (int i = 145; i <= 147; i++) + materials[i] = Material.JungleLog; + for (int i = 148; i <= 150; i++) + materials[i] = Material.AcaciaLog; + for (int i = 151; i <= 153; i++) + materials[i] = Material.CherryLog; + for (int i = 154; i <= 156; i++) + materials[i] = Material.DarkOakLog; + for (int i = 157; i <= 159; i++) + materials[i] = Material.PaleOakLog; + for (int i = 160; i <= 162; i++) + materials[i] = Material.MangroveLog; + for (int i = 163; i <= 164; i++) + materials[i] = Material.MangroveRoots; + for (int i = 165; i <= 167; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 168; i <= 170; i++) + materials[i] = Material.BambooBlock; + for (int i = 171; i <= 173; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 174; i <= 176; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 177; i <= 179; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 180; i <= 182; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 183; i <= 185; i++) + materials[i] = Material.StrippedCherryLog; + for (int i = 186; i <= 188; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 189; i <= 191; i++) + materials[i] = Material.StrippedPaleOakLog; + for (int i = 192; i <= 194; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 195; i <= 197; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 198; i <= 200; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 201; i <= 203; i++) + materials[i] = Material.OakWood; + for (int i = 204; i <= 206; i++) + materials[i] = Material.SpruceWood; + for (int i = 207; i <= 209; i++) + materials[i] = Material.BirchWood; + for (int i = 210; i <= 212; i++) + materials[i] = Material.JungleWood; + for (int i = 213; i <= 215; i++) + materials[i] = Material.AcaciaWood; + for (int i = 216; i <= 218; i++) + materials[i] = Material.CherryWood; + for (int i = 219; i <= 221; i++) + materials[i] = Material.DarkOakWood; + for (int i = 222; i <= 224; i++) + materials[i] = Material.MangroveWood; + for (int i = 225; i <= 227; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 228; i <= 230; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 231; i <= 233; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 234; i <= 236; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 237; i <= 239; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 240; i <= 242; i++) + materials[i] = Material.StrippedCherryWood; + for (int i = 243; i <= 245; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 246; i <= 248; i++) + materials[i] = Material.StrippedPaleOakWood; + for (int i = 249; i <= 251; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 252; i <= 279; i++) + materials[i] = Material.OakLeaves; + for (int i = 280; i <= 307; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 308; i <= 335; i++) + materials[i] = Material.BirchLeaves; + for (int i = 336; i <= 363; i++) + materials[i] = Material.JungleLeaves; + for (int i = 364; i <= 391; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 392; i <= 419; i++) + materials[i] = Material.CherryLeaves; + for (int i = 420; i <= 447; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 448; i <= 475; i++) + materials[i] = Material.PaleOakLeaves; + for (int i = 476; i <= 503; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 504; i <= 531; i++) + materials[i] = Material.AzaleaLeaves; + for (int i = 532; i <= 559; i++) + materials[i] = Material.FloweringAzaleaLeaves; + for (int i = 560; i <= 560; i++) + materials[i] = Material.Sponge; + for (int i = 561; i <= 561; i++) + materials[i] = Material.WetSponge; + for (int i = 562; i <= 562; i++) + materials[i] = Material.Glass; + for (int i = 563; i <= 563; i++) + materials[i] = Material.LapisOre; + for (int i = 564; i <= 564; i++) + materials[i] = Material.DeepslateLapisOre; + for (int i = 565; i <= 565; i++) + materials[i] = Material.LapisBlock; + for (int i = 566; i <= 577; i++) + materials[i] = Material.Dispenser; + for (int i = 578; i <= 578; i++) + materials[i] = Material.Sandstone; + for (int i = 579; i <= 579; i++) + materials[i] = Material.ChiseledSandstone; + for (int i = 580; i <= 580; i++) + materials[i] = Material.CutSandstone; + for (int i = 581; i <= 1930; i++) + materials[i] = Material.NoteBlock; + for (int i = 1931; i <= 1946; i++) + materials[i] = Material.WhiteBed; + for (int i = 1947; i <= 1962; i++) + materials[i] = Material.OrangeBed; + for (int i = 1963; i <= 1978; i++) + materials[i] = Material.MagentaBed; + for (int i = 1979; i <= 1994; i++) + materials[i] = Material.LightBlueBed; + for (int i = 1995; i <= 2010; i++) + materials[i] = Material.YellowBed; + for (int i = 2011; i <= 2026; i++) + materials[i] = Material.LimeBed; + for (int i = 2027; i <= 2042; i++) + materials[i] = Material.PinkBed; + for (int i = 2043; i <= 2058; i++) + materials[i] = Material.GrayBed; + for (int i = 2059; i <= 2074; i++) + materials[i] = Material.LightGrayBed; + for (int i = 2075; i <= 2090; i++) + materials[i] = Material.CyanBed; + for (int i = 2091; i <= 2106; i++) + materials[i] = Material.PurpleBed; + for (int i = 2107; i <= 2122; i++) + materials[i] = Material.BlueBed; + for (int i = 2123; i <= 2138; i++) + materials[i] = Material.BrownBed; + for (int i = 2139; i <= 2154; i++) + materials[i] = Material.GreenBed; + for (int i = 2155; i <= 2170; i++) + materials[i] = Material.RedBed; + for (int i = 2171; i <= 2186; i++) + materials[i] = Material.BlackBed; + for (int i = 2187; i <= 2210; i++) + materials[i] = Material.PoweredRail; + for (int i = 2211; i <= 2234; i++) + materials[i] = Material.DetectorRail; + for (int i = 2235; i <= 2246; i++) + materials[i] = Material.StickyPiston; + for (int i = 2247; i <= 2247; i++) + materials[i] = Material.Cobweb; + for (int i = 2248; i <= 2248; i++) + materials[i] = Material.ShortGrass; + for (int i = 2249; i <= 2249; i++) + materials[i] = Material.Fern; + for (int i = 2250; i <= 2250; i++) + materials[i] = Material.DeadBush; + for (int i = 2251; i <= 2251; i++) + materials[i] = Material.Bush; + for (int i = 2252; i <= 2252; i++) + materials[i] = Material.ShortDryGrass; + for (int i = 2253; i <= 2253; i++) + materials[i] = Material.TallDryGrass; + for (int i = 2254; i <= 2254; i++) + materials[i] = Material.Seagrass; + for (int i = 2255; i <= 2256; i++) + materials[i] = Material.TallSeagrass; + for (int i = 2257; i <= 2268; i++) + materials[i] = Material.Piston; + for (int i = 2269; i <= 2292; i++) + materials[i] = Material.PistonHead; + for (int i = 2293; i <= 2293; i++) + materials[i] = Material.WhiteWool; + for (int i = 2294; i <= 2294; i++) + materials[i] = Material.OrangeWool; + for (int i = 2295; i <= 2295; i++) + materials[i] = Material.MagentaWool; + for (int i = 2296; i <= 2296; i++) + materials[i] = Material.LightBlueWool; + for (int i = 2297; i <= 2297; i++) + materials[i] = Material.YellowWool; + for (int i = 2298; i <= 2298; i++) + materials[i] = Material.LimeWool; + for (int i = 2299; i <= 2299; i++) + materials[i] = Material.PinkWool; + for (int i = 2300; i <= 2300; i++) + materials[i] = Material.GrayWool; + for (int i = 2301; i <= 2301; i++) + materials[i] = Material.LightGrayWool; + for (int i = 2302; i <= 2302; i++) + materials[i] = Material.CyanWool; + for (int i = 2303; i <= 2303; i++) + materials[i] = Material.PurpleWool; + for (int i = 2304; i <= 2304; i++) + materials[i] = Material.BlueWool; + for (int i = 2305; i <= 2305; i++) + materials[i] = Material.BrownWool; + for (int i = 2306; i <= 2306; i++) + materials[i] = Material.GreenWool; + for (int i = 2307; i <= 2307; i++) + materials[i] = Material.RedWool; + for (int i = 2308; i <= 2308; i++) + materials[i] = Material.BlackWool; + for (int i = 2309; i <= 2320; i++) + materials[i] = Material.MovingPiston; + for (int i = 2321; i <= 2321; i++) + materials[i] = Material.Dandelion; + for (int i = 2322; i <= 2322; i++) + materials[i] = Material.GoldenDandelion; + for (int i = 2323; i <= 2323; i++) + materials[i] = Material.Torchflower; + for (int i = 2324; i <= 2324; i++) + materials[i] = Material.Poppy; + for (int i = 2325; i <= 2325; i++) + materials[i] = Material.BlueOrchid; + for (int i = 2326; i <= 2326; i++) + materials[i] = Material.Allium; + for (int i = 2327; i <= 2327; i++) + materials[i] = Material.AzureBluet; + for (int i = 2328; i <= 2328; i++) + materials[i] = Material.RedTulip; + for (int i = 2329; i <= 2329; i++) + materials[i] = Material.OrangeTulip; + for (int i = 2330; i <= 2330; i++) + materials[i] = Material.WhiteTulip; + for (int i = 2331; i <= 2331; i++) + materials[i] = Material.PinkTulip; + for (int i = 2332; i <= 2332; i++) + materials[i] = Material.OxeyeDaisy; + for (int i = 2333; i <= 2333; i++) + materials[i] = Material.Cornflower; + for (int i = 2334; i <= 2334; i++) + materials[i] = Material.WitherRose; + for (int i = 2335; i <= 2335; i++) + materials[i] = Material.LilyOfTheValley; + for (int i = 2336; i <= 2336; i++) + materials[i] = Material.BrownMushroom; + for (int i = 2337; i <= 2337; i++) + materials[i] = Material.RedMushroom; + for (int i = 2338; i <= 2338; i++) + materials[i] = Material.GoldBlock; + for (int i = 2339; i <= 2339; i++) + materials[i] = Material.IronBlock; + for (int i = 2340; i <= 2340; i++) + materials[i] = Material.Bricks; + for (int i = 2341; i <= 2342; i++) + materials[i] = Material.Tnt; + for (int i = 2343; i <= 2343; i++) + materials[i] = Material.Bookshelf; + for (int i = 2344; i <= 2599; i++) + materials[i] = Material.ChiseledBookshelf; + for (int i = 2600; i <= 2663; i++) + materials[i] = Material.AcaciaShelf; + for (int i = 2664; i <= 2727; i++) + materials[i] = Material.BambooShelf; + for (int i = 2728; i <= 2791; i++) + materials[i] = Material.BirchShelf; + for (int i = 2792; i <= 2855; i++) + materials[i] = Material.CherryShelf; + for (int i = 2856; i <= 2919; i++) + materials[i] = Material.CrimsonShelf; + for (int i = 2920; i <= 2983; i++) + materials[i] = Material.DarkOakShelf; + for (int i = 2984; i <= 3047; i++) + materials[i] = Material.JungleShelf; + for (int i = 3048; i <= 3111; i++) + materials[i] = Material.MangroveShelf; + for (int i = 3112; i <= 3175; i++) + materials[i] = Material.OakShelf; + for (int i = 3176; i <= 3239; i++) + materials[i] = Material.PaleOakShelf; + for (int i = 3240; i <= 3303; i++) + materials[i] = Material.SpruceShelf; + for (int i = 3304; i <= 3367; i++) + materials[i] = Material.WarpedShelf; + for (int i = 3368; i <= 3368; i++) + materials[i] = Material.MossyCobblestone; + for (int i = 3369; i <= 3369; i++) + materials[i] = Material.Obsidian; + for (int i = 3370; i <= 3370; i++) + materials[i] = Material.Torch; + for (int i = 3371; i <= 3374; i++) + materials[i] = Material.WallTorch; + for (int i = 3375; i <= 3886; i++) + materials[i] = Material.Fire; + for (int i = 3887; i <= 3887; i++) + materials[i] = Material.SoulFire; + for (int i = 3888; i <= 3888; i++) + materials[i] = Material.Spawner; + for (int i = 3889; i <= 3906; i++) + materials[i] = Material.CreakingHeart; + for (int i = 3907; i <= 3986; i++) + materials[i] = Material.OakStairs; + for (int i = 3987; i <= 4010; i++) + materials[i] = Material.Chest; + for (int i = 4011; i <= 5306; i++) + materials[i] = Material.RedstoneWire; + for (int i = 5307; i <= 5307; i++) + materials[i] = Material.DiamondOre; + for (int i = 5308; i <= 5308; i++) + materials[i] = Material.DeepslateDiamondOre; + for (int i = 5309; i <= 5309; i++) + materials[i] = Material.DiamondBlock; + for (int i = 5310; i <= 5310; i++) + materials[i] = Material.CraftingTable; + for (int i = 5311; i <= 5318; i++) + materials[i] = Material.Wheat; + for (int i = 5319; i <= 5326; i++) + materials[i] = Material.Farmland; + for (int i = 5327; i <= 5334; i++) + materials[i] = Material.Furnace; + for (int i = 5335; i <= 5366; i++) + materials[i] = Material.OakSign; + for (int i = 5367; i <= 5398; i++) + materials[i] = Material.SpruceSign; + for (int i = 5399; i <= 5430; i++) + materials[i] = Material.BirchSign; + for (int i = 5431; i <= 5462; i++) + materials[i] = Material.AcaciaSign; + for (int i = 5463; i <= 5494; i++) + materials[i] = Material.CherrySign; + for (int i = 5495; i <= 5526; i++) + materials[i] = Material.JungleSign; + for (int i = 5527; i <= 5558; i++) + materials[i] = Material.DarkOakSign; + for (int i = 5559; i <= 5590; i++) + materials[i] = Material.PaleOakSign; + for (int i = 5591; i <= 5622; i++) + materials[i] = Material.MangroveSign; + for (int i = 5623; i <= 5654; i++) + materials[i] = Material.BambooSign; + for (int i = 5655; i <= 5718; i++) + materials[i] = Material.OakDoor; + for (int i = 5719; i <= 5726; i++) + materials[i] = Material.Ladder; + for (int i = 5727; i <= 5746; i++) + materials[i] = Material.Rail; + for (int i = 5747; i <= 5826; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 5827; i <= 5834; i++) + materials[i] = Material.OakWallSign; + for (int i = 5835; i <= 5842; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 5843; i <= 5850; i++) + materials[i] = Material.BirchWallSign; + for (int i = 5851; i <= 5858; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 5859; i <= 5866; i++) + materials[i] = Material.CherryWallSign; + for (int i = 5867; i <= 5874; i++) + materials[i] = Material.JungleWallSign; + for (int i = 5875; i <= 5882; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 5883; i <= 5890; i++) + materials[i] = Material.PaleOakWallSign; + for (int i = 5891; i <= 5898; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 5899; i <= 5906; i++) + materials[i] = Material.BambooWallSign; + for (int i = 5907; i <= 5970; i++) + materials[i] = Material.OakHangingSign; + for (int i = 5971; i <= 6034; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 6035; i <= 6098; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 6099; i <= 6162; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 6163; i <= 6226; i++) + materials[i] = Material.CherryHangingSign; + for (int i = 6227; i <= 6290; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 6291; i <= 6354; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 6355; i <= 6418; i++) + materials[i] = Material.PaleOakHangingSign; + for (int i = 6419; i <= 6482; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 6483; i <= 6546; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 6547; i <= 6610; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 6611; i <= 6674; i++) + materials[i] = Material.BambooHangingSign; + for (int i = 6675; i <= 6682; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 6683; i <= 6690; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 6691; i <= 6698; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 6699; i <= 6706; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 6707; i <= 6714; i++) + materials[i] = Material.CherryWallHangingSign; + for (int i = 6715; i <= 6722; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 6723; i <= 6730; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 6731; i <= 6738; i++) + materials[i] = Material.PaleOakWallHangingSign; + for (int i = 6739; i <= 6746; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 6747; i <= 6754; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 6755; i <= 6762; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 6763; i <= 6770; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 6771; i <= 6794; i++) + materials[i] = Material.Lever; + for (int i = 6795; i <= 6796; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 6797; i <= 6860; i++) + materials[i] = Material.IronDoor; + for (int i = 6861; i <= 6862; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 6863; i <= 6864; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 6865; i <= 6866; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 6867; i <= 6868; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 6869; i <= 6870; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 6871; i <= 6872; i++) + materials[i] = Material.CherryPressurePlate; + for (int i = 6873; i <= 6874; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 6875; i <= 6876; i++) + materials[i] = Material.PaleOakPressurePlate; + for (int i = 6877; i <= 6878; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 6879; i <= 6880; i++) + materials[i] = Material.BambooPressurePlate; + for (int i = 6881; i <= 6882; i++) + materials[i] = Material.RedstoneOre; + for (int i = 6883; i <= 6884; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 6885; i <= 6886; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 6887; i <= 6894; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 6895; i <= 6918; i++) + materials[i] = Material.StoneButton; + for (int i = 6919; i <= 6926; i++) + materials[i] = Material.Snow; + for (int i = 6927; i <= 6927; i++) + materials[i] = Material.Ice; + for (int i = 6928; i <= 6928; i++) + materials[i] = Material.SnowBlock; + for (int i = 6929; i <= 6944; i++) + materials[i] = Material.Cactus; + for (int i = 6945; i <= 6945; i++) + materials[i] = Material.CactusFlower; + for (int i = 6946; i <= 6946; i++) + materials[i] = Material.Clay; + for (int i = 6947; i <= 6962; i++) + materials[i] = Material.SugarCane; + for (int i = 6963; i <= 6964; i++) + materials[i] = Material.Jukebox; + for (int i = 6965; i <= 6996; i++) + materials[i] = Material.OakFence; + for (int i = 6997; i <= 6997; i++) + materials[i] = Material.Netherrack; + for (int i = 6998; i <= 6998; i++) + materials[i] = Material.SoulSand; + for (int i = 6999; i <= 6999; i++) + materials[i] = Material.SoulSoil; + for (int i = 7000; i <= 7002; i++) + materials[i] = Material.Basalt; + for (int i = 7003; i <= 7005; i++) + materials[i] = Material.PolishedBasalt; + for (int i = 7006; i <= 7006; i++) + materials[i] = Material.SoulTorch; + for (int i = 7007; i <= 7010; i++) + materials[i] = Material.SoulWallTorch; + for (int i = 7011; i <= 7011; i++) + materials[i] = Material.CopperTorch; + for (int i = 7012; i <= 7015; i++) + materials[i] = Material.CopperWallTorch; + for (int i = 7016; i <= 7016; i++) + materials[i] = Material.Glowstone; + for (int i = 7017; i <= 7018; i++) + materials[i] = Material.NetherPortal; + for (int i = 7019; i <= 7022; i++) + materials[i] = Material.CarvedPumpkin; + for (int i = 7023; i <= 7026; i++) + materials[i] = Material.JackOLantern; + for (int i = 7027; i <= 7033; i++) + materials[i] = Material.Cake; + for (int i = 7034; i <= 7097; i++) + materials[i] = Material.Repeater; + for (int i = 7098; i <= 7098; i++) + materials[i] = Material.WhiteStainedGlass; + for (int i = 7099; i <= 7099; i++) + materials[i] = Material.OrangeStainedGlass; + for (int i = 7100; i <= 7100; i++) + materials[i] = Material.MagentaStainedGlass; + for (int i = 7101; i <= 7101; i++) + materials[i] = Material.LightBlueStainedGlass; + for (int i = 7102; i <= 7102; i++) + materials[i] = Material.YellowStainedGlass; + for (int i = 7103; i <= 7103; i++) + materials[i] = Material.LimeStainedGlass; + for (int i = 7104; i <= 7104; i++) + materials[i] = Material.PinkStainedGlass; + for (int i = 7105; i <= 7105; i++) + materials[i] = Material.GrayStainedGlass; + for (int i = 7106; i <= 7106; i++) + materials[i] = Material.LightGrayStainedGlass; + for (int i = 7107; i <= 7107; i++) + materials[i] = Material.CyanStainedGlass; + for (int i = 7108; i <= 7108; i++) + materials[i] = Material.PurpleStainedGlass; + for (int i = 7109; i <= 7109; i++) + materials[i] = Material.BlueStainedGlass; + for (int i = 7110; i <= 7110; i++) + materials[i] = Material.BrownStainedGlass; + for (int i = 7111; i <= 7111; i++) + materials[i] = Material.GreenStainedGlass; + for (int i = 7112; i <= 7112; i++) + materials[i] = Material.RedStainedGlass; + for (int i = 7113; i <= 7113; i++) + materials[i] = Material.BlackStainedGlass; + for (int i = 7114; i <= 7177; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 7178; i <= 7241; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 7242; i <= 7305; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 7306; i <= 7369; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 7370; i <= 7433; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 7434; i <= 7497; i++) + materials[i] = Material.CherryTrapdoor; + for (int i = 7498; i <= 7561; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 7562; i <= 7625; i++) + materials[i] = Material.PaleOakTrapdoor; + for (int i = 7626; i <= 7689; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 7690; i <= 7753; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 7754; i <= 7754; i++) + materials[i] = Material.StoneBricks; + for (int i = 7755; i <= 7755; i++) + materials[i] = Material.MossyStoneBricks; + for (int i = 7756; i <= 7756; i++) + materials[i] = Material.CrackedStoneBricks; + for (int i = 7757; i <= 7757; i++) + materials[i] = Material.ChiseledStoneBricks; + for (int i = 7758; i <= 7758; i++) + materials[i] = Material.PackedMud; + for (int i = 7759; i <= 7759; i++) + materials[i] = Material.MudBricks; + for (int i = 7760; i <= 7760; i++) + materials[i] = Material.InfestedStone; + for (int i = 7761; i <= 7761; i++) + materials[i] = Material.InfestedCobblestone; + for (int i = 7762; i <= 7762; i++) + materials[i] = Material.InfestedStoneBricks; + for (int i = 7763; i <= 7763; i++) + materials[i] = Material.InfestedMossyStoneBricks; + for (int i = 7764; i <= 7764; i++) + materials[i] = Material.InfestedCrackedStoneBricks; + for (int i = 7765; i <= 7765; i++) + materials[i] = Material.InfestedChiseledStoneBricks; + for (int i = 7766; i <= 7829; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 7830; i <= 7893; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 7894; i <= 7957; i++) + materials[i] = Material.MushroomStem; + for (int i = 7958; i <= 7989; i++) + materials[i] = Material.IronBars; + for (int i = 7990; i <= 8021; i++) + materials[i] = Material.CopperBars; + for (int i = 8022; i <= 8053; i++) + materials[i] = Material.ExposedCopperBars; + for (int i = 8054; i <= 8085; i++) + materials[i] = Material.WeatheredCopperBars; + for (int i = 8086; i <= 8117; i++) + materials[i] = Material.OxidizedCopperBars; + for (int i = 8118; i <= 8149; i++) + materials[i] = Material.WaxedCopperBars; + for (int i = 8150; i <= 8181; i++) + materials[i] = Material.WaxedExposedCopperBars; + for (int i = 8182; i <= 8213; i++) + materials[i] = Material.WaxedWeatheredCopperBars; + for (int i = 8214; i <= 8245; i++) + materials[i] = Material.WaxedOxidizedCopperBars; + for (int i = 8246; i <= 8251; i++) + materials[i] = Material.IronChain; + for (int i = 8252; i <= 8257; i++) + materials[i] = Material.CopperChain; + for (int i = 8258; i <= 8263; i++) + materials[i] = Material.ExposedCopperChain; + for (int i = 8264; i <= 8269; i++) + materials[i] = Material.WeatheredCopperChain; + for (int i = 8270; i <= 8275; i++) + materials[i] = Material.OxidizedCopperChain; + for (int i = 8276; i <= 8281; i++) + materials[i] = Material.WaxedCopperChain; + for (int i = 8282; i <= 8287; i++) + materials[i] = Material.WaxedExposedCopperChain; + for (int i = 8288; i <= 8293; i++) + materials[i] = Material.WaxedWeatheredCopperChain; + for (int i = 8294; i <= 8299; i++) + materials[i] = Material.WaxedOxidizedCopperChain; + for (int i = 8300; i <= 8331; i++) + materials[i] = Material.GlassPane; + for (int i = 8332; i <= 8332; i++) + materials[i] = Material.Pumpkin; + for (int i = 8333; i <= 8333; i++) + materials[i] = Material.Melon; + for (int i = 8334; i <= 8337; i++) + materials[i] = Material.AttachedPumpkinStem; + for (int i = 8338; i <= 8341; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 8342; i <= 8349; i++) + materials[i] = Material.PumpkinStem; + for (int i = 8350; i <= 8357; i++) + materials[i] = Material.MelonStem; + for (int i = 8358; i <= 8389; i++) + materials[i] = Material.Vine; + for (int i = 8390; i <= 8517; i++) + materials[i] = Material.GlowLichen; + for (int i = 8518; i <= 8645; i++) + materials[i] = Material.ResinClump; + for (int i = 8646; i <= 8677; i++) + materials[i] = Material.OakFenceGate; + for (int i = 8678; i <= 8757; i++) + materials[i] = Material.BrickStairs; + for (int i = 8758; i <= 8837; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 8838; i <= 8917; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 8918; i <= 8919; i++) + materials[i] = Material.Mycelium; + for (int i = 8920; i <= 8920; i++) + materials[i] = Material.LilyPad; + for (int i = 8921; i <= 8921; i++) + materials[i] = Material.ResinBlock; + for (int i = 8922; i <= 8922; i++) + materials[i] = Material.ResinBricks; + for (int i = 8923; i <= 9002; i++) + materials[i] = Material.ResinBrickStairs; + for (int i = 9003; i <= 9008; i++) + materials[i] = Material.ResinBrickSlab; + for (int i = 9009; i <= 9332; i++) + materials[i] = Material.ResinBrickWall; + for (int i = 9333; i <= 9333; i++) + materials[i] = Material.ChiseledResinBricks; + for (int i = 9334; i <= 9334; i++) + materials[i] = Material.NetherBricks; + for (int i = 9335; i <= 9366; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 9367; i <= 9446; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 9447; i <= 9450; i++) + materials[i] = Material.NetherWart; + for (int i = 9451; i <= 9451; i++) + materials[i] = Material.EnchantingTable; + for (int i = 9452; i <= 9459; i++) + materials[i] = Material.BrewingStand; + for (int i = 9460; i <= 9460; i++) + materials[i] = Material.Cauldron; + for (int i = 9461; i <= 9463; i++) + materials[i] = Material.WaterCauldron; + for (int i = 9464; i <= 9464; i++) + materials[i] = Material.LavaCauldron; + for (int i = 9465; i <= 9467; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 9468; i <= 9468; i++) + materials[i] = Material.EndPortal; + for (int i = 9469; i <= 9476; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 9477; i <= 9477; i++) + materials[i] = Material.EndStone; + for (int i = 9478; i <= 9478; i++) + materials[i] = Material.DragonEgg; + for (int i = 9479; i <= 9480; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 9481; i <= 9492; i++) + materials[i] = Material.Cocoa; + for (int i = 9493; i <= 9572; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 9573; i <= 9573; i++) + materials[i] = Material.EmeraldOre; + for (int i = 9574; i <= 9574; i++) + materials[i] = Material.DeepslateEmeraldOre; + for (int i = 9575; i <= 9582; i++) + materials[i] = Material.EnderChest; + for (int i = 9583; i <= 9598; i++) + materials[i] = Material.TripwireHook; + for (int i = 9599; i <= 9726; i++) + materials[i] = Material.Tripwire; + for (int i = 9727; i <= 9727; i++) + materials[i] = Material.EmeraldBlock; + for (int i = 9728; i <= 9807; i++) + materials[i] = Material.SpruceStairs; + for (int i = 9808; i <= 9887; i++) + materials[i] = Material.BirchStairs; + for (int i = 9888; i <= 9967; i++) + materials[i] = Material.JungleStairs; + for (int i = 9968; i <= 9979; i++) + materials[i] = Material.CommandBlock; + for (int i = 9980; i <= 9980; i++) + materials[i] = Material.Beacon; + for (int i = 9981; i <= 10304; i++) + materials[i] = Material.CobblestoneWall; + for (int i = 10305; i <= 10628; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 10629; i <= 10629; i++) + materials[i] = Material.FlowerPot; + for (int i = 10630; i <= 10630; i++) + materials[i] = Material.PottedTorchflower; + for (int i = 10631; i <= 10631; i++) + materials[i] = Material.PottedOakSapling; + for (int i = 10632; i <= 10632; i++) + materials[i] = Material.PottedSpruceSapling; + for (int i = 10633; i <= 10633; i++) + materials[i] = Material.PottedBirchSapling; + for (int i = 10634; i <= 10634; i++) + materials[i] = Material.PottedJungleSapling; + for (int i = 10635; i <= 10635; i++) + materials[i] = Material.PottedAcaciaSapling; + for (int i = 10636; i <= 10636; i++) + materials[i] = Material.PottedCherrySapling; + for (int i = 10637; i <= 10637; i++) + materials[i] = Material.PottedDarkOakSapling; + for (int i = 10638; i <= 10638; i++) + materials[i] = Material.PottedPaleOakSapling; + for (int i = 10639; i <= 10639; i++) + materials[i] = Material.PottedMangrovePropagule; + for (int i = 10640; i <= 10640; i++) + materials[i] = Material.PottedFern; + for (int i = 10641; i <= 10641; i++) + materials[i] = Material.PottedDandelion; + for (int i = 10642; i <= 10642; i++) + materials[i] = Material.PottedGoldenDandelion; + for (int i = 10643; i <= 10643; i++) + materials[i] = Material.PottedPoppy; + for (int i = 10644; i <= 10644; i++) + materials[i] = Material.PottedBlueOrchid; + for (int i = 10645; i <= 10645; i++) + materials[i] = Material.PottedAllium; + for (int i = 10646; i <= 10646; i++) + materials[i] = Material.PottedAzureBluet; + for (int i = 10647; i <= 10647; i++) + materials[i] = Material.PottedRedTulip; + for (int i = 10648; i <= 10648; i++) + materials[i] = Material.PottedOrangeTulip; + for (int i = 10649; i <= 10649; i++) + materials[i] = Material.PottedWhiteTulip; + for (int i = 10650; i <= 10650; i++) + materials[i] = Material.PottedPinkTulip; + for (int i = 10651; i <= 10651; i++) + materials[i] = Material.PottedOxeyeDaisy; + for (int i = 10652; i <= 10652; i++) + materials[i] = Material.PottedCornflower; + for (int i = 10653; i <= 10653; i++) + materials[i] = Material.PottedLilyOfTheValley; + for (int i = 10654; i <= 10654; i++) + materials[i] = Material.PottedWitherRose; + for (int i = 10655; i <= 10655; i++) + materials[i] = Material.PottedRedMushroom; + for (int i = 10656; i <= 10656; i++) + materials[i] = Material.PottedBrownMushroom; + for (int i = 10657; i <= 10657; i++) + materials[i] = Material.PottedDeadBush; + for (int i = 10658; i <= 10658; i++) + materials[i] = Material.PottedCactus; + for (int i = 10659; i <= 10666; i++) + materials[i] = Material.Carrots; + for (int i = 10667; i <= 10674; i++) + materials[i] = Material.Potatoes; + for (int i = 10675; i <= 10698; i++) + materials[i] = Material.OakButton; + for (int i = 10699; i <= 10722; i++) + materials[i] = Material.SpruceButton; + for (int i = 10723; i <= 10746; i++) + materials[i] = Material.BirchButton; + for (int i = 10747; i <= 10770; i++) + materials[i] = Material.JungleButton; + for (int i = 10771; i <= 10794; i++) + materials[i] = Material.AcaciaButton; + for (int i = 10795; i <= 10818; i++) + materials[i] = Material.CherryButton; + for (int i = 10819; i <= 10842; i++) + materials[i] = Material.DarkOakButton; + for (int i = 10843; i <= 10866; i++) + materials[i] = Material.PaleOakButton; + for (int i = 10867; i <= 10890; i++) + materials[i] = Material.MangroveButton; + for (int i = 10891; i <= 10914; i++) + materials[i] = Material.BambooButton; + for (int i = 10915; i <= 10946; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 10947; i <= 10954; i++) + materials[i] = Material.SkeletonWallSkull; + for (int i = 10955; i <= 10986; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 10987; i <= 10994; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 10995; i <= 11026; i++) + materials[i] = Material.ZombieHead; + for (int i = 11027; i <= 11034; i++) + materials[i] = Material.ZombieWallHead; + for (int i = 11035; i <= 11066; i++) + materials[i] = Material.PlayerHead; + for (int i = 11067; i <= 11074; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 11075; i <= 11106; i++) + materials[i] = Material.CreeperHead; + for (int i = 11107; i <= 11114; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 11115; i <= 11146; i++) + materials[i] = Material.DragonHead; + for (int i = 11147; i <= 11154; i++) + materials[i] = Material.DragonWallHead; + for (int i = 11155; i <= 11186; i++) + materials[i] = Material.PiglinHead; + for (int i = 11187; i <= 11194; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 11195; i <= 11198; i++) + materials[i] = Material.Anvil; + for (int i = 11199; i <= 11202; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 11203; i <= 11206; i++) + materials[i] = Material.DamagedAnvil; + for (int i = 11207; i <= 11230; i++) + materials[i] = Material.TrappedChest; + for (int i = 11231; i <= 11246; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 11247; i <= 11262; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + for (int i = 11263; i <= 11278; i++) + materials[i] = Material.Comparator; + for (int i = 11279; i <= 11310; i++) + materials[i] = Material.DaylightDetector; + for (int i = 11311; i <= 11311; i++) + materials[i] = Material.RedstoneBlock; + for (int i = 11312; i <= 11312; i++) + materials[i] = Material.NetherQuartzOre; + for (int i = 11313; i <= 11322; i++) + materials[i] = Material.Hopper; + for (int i = 11323; i <= 11323; i++) + materials[i] = Material.QuartzBlock; + for (int i = 11324; i <= 11324; i++) + materials[i] = Material.ChiseledQuartzBlock; + for (int i = 11325; i <= 11327; i++) + materials[i] = Material.QuartzPillar; + for (int i = 11328; i <= 11407; i++) + materials[i] = Material.QuartzStairs; + for (int i = 11408; i <= 11431; i++) + materials[i] = Material.ActivatorRail; + for (int i = 11432; i <= 11443; i++) + materials[i] = Material.Dropper; + for (int i = 11444; i <= 11444; i++) + materials[i] = Material.WhiteTerracotta; + for (int i = 11445; i <= 11445; i++) + materials[i] = Material.OrangeTerracotta; + for (int i = 11446; i <= 11446; i++) + materials[i] = Material.MagentaTerracotta; + for (int i = 11447; i <= 11447; i++) + materials[i] = Material.LightBlueTerracotta; + for (int i = 11448; i <= 11448; i++) + materials[i] = Material.YellowTerracotta; + for (int i = 11449; i <= 11449; i++) + materials[i] = Material.LimeTerracotta; + for (int i = 11450; i <= 11450; i++) + materials[i] = Material.PinkTerracotta; + for (int i = 11451; i <= 11451; i++) + materials[i] = Material.GrayTerracotta; + for (int i = 11452; i <= 11452; i++) + materials[i] = Material.LightGrayTerracotta; + for (int i = 11453; i <= 11453; i++) + materials[i] = Material.CyanTerracotta; + for (int i = 11454; i <= 11454; i++) + materials[i] = Material.PurpleTerracotta; + for (int i = 11455; i <= 11455; i++) + materials[i] = Material.BlueTerracotta; + for (int i = 11456; i <= 11456; i++) + materials[i] = Material.BrownTerracotta; + for (int i = 11457; i <= 11457; i++) + materials[i] = Material.GreenTerracotta; + for (int i = 11458; i <= 11458; i++) + materials[i] = Material.RedTerracotta; + for (int i = 11459; i <= 11459; i++) + materials[i] = Material.BlackTerracotta; + for (int i = 11460; i <= 11491; i++) + materials[i] = Material.WhiteStainedGlassPane; + for (int i = 11492; i <= 11523; i++) + materials[i] = Material.OrangeStainedGlassPane; + for (int i = 11524; i <= 11555; i++) + materials[i] = Material.MagentaStainedGlassPane; + for (int i = 11556; i <= 11587; i++) + materials[i] = Material.LightBlueStainedGlassPane; + for (int i = 11588; i <= 11619; i++) + materials[i] = Material.YellowStainedGlassPane; + for (int i = 11620; i <= 11651; i++) + materials[i] = Material.LimeStainedGlassPane; + for (int i = 11652; i <= 11683; i++) + materials[i] = Material.PinkStainedGlassPane; + for (int i = 11684; i <= 11715; i++) + materials[i] = Material.GrayStainedGlassPane; + for (int i = 11716; i <= 11747; i++) + materials[i] = Material.LightGrayStainedGlassPane; + for (int i = 11748; i <= 11779; i++) + materials[i] = Material.CyanStainedGlassPane; + for (int i = 11780; i <= 11811; i++) + materials[i] = Material.PurpleStainedGlassPane; + for (int i = 11812; i <= 11843; i++) + materials[i] = Material.BlueStainedGlassPane; + for (int i = 11844; i <= 11875; i++) + materials[i] = Material.BrownStainedGlassPane; + for (int i = 11876; i <= 11907; i++) + materials[i] = Material.GreenStainedGlassPane; + for (int i = 11908; i <= 11939; i++) + materials[i] = Material.RedStainedGlassPane; + for (int i = 11940; i <= 11971; i++) + materials[i] = Material.BlackStainedGlassPane; + for (int i = 11972; i <= 12051; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 12052; i <= 12131; i++) + materials[i] = Material.CherryStairs; + for (int i = 12132; i <= 12211; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 12212; i <= 12291; i++) + materials[i] = Material.PaleOakStairs; + for (int i = 12292; i <= 12371; i++) + materials[i] = Material.MangroveStairs; + for (int i = 12372; i <= 12451; i++) + materials[i] = Material.BambooStairs; + for (int i = 12452; i <= 12531; i++) + materials[i] = Material.BambooMosaicStairs; + for (int i = 12532; i <= 12532; i++) + materials[i] = Material.SlimeBlock; + for (int i = 12533; i <= 12534; i++) + materials[i] = Material.Barrier; + for (int i = 12535; i <= 12566; i++) + materials[i] = Material.Light; + for (int i = 12567; i <= 12630; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 12631; i <= 12631; i++) + materials[i] = Material.Prismarine; + for (int i = 12632; i <= 12632; i++) + materials[i] = Material.PrismarineBricks; + for (int i = 12633; i <= 12633; i++) + materials[i] = Material.DarkPrismarine; + for (int i = 12634; i <= 12713; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 12714; i <= 12793; i++) + materials[i] = Material.PrismarineBrickStairs; + for (int i = 12794; i <= 12873; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 12874; i <= 12879; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 12880; i <= 12885; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 12886; i <= 12891; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 12892; i <= 12892; i++) + materials[i] = Material.SeaLantern; + for (int i = 12893; i <= 12895; i++) + materials[i] = Material.HayBlock; + for (int i = 12896; i <= 12896; i++) + materials[i] = Material.WhiteCarpet; + for (int i = 12897; i <= 12897; i++) + materials[i] = Material.OrangeCarpet; + for (int i = 12898; i <= 12898; i++) + materials[i] = Material.MagentaCarpet; + for (int i = 12899; i <= 12899; i++) + materials[i] = Material.LightBlueCarpet; + for (int i = 12900; i <= 12900; i++) + materials[i] = Material.YellowCarpet; + for (int i = 12901; i <= 12901; i++) + materials[i] = Material.LimeCarpet; + for (int i = 12902; i <= 12902; i++) + materials[i] = Material.PinkCarpet; + for (int i = 12903; i <= 12903; i++) + materials[i] = Material.GrayCarpet; + for (int i = 12904; i <= 12904; i++) + materials[i] = Material.LightGrayCarpet; + for (int i = 12905; i <= 12905; i++) + materials[i] = Material.CyanCarpet; + for (int i = 12906; i <= 12906; i++) + materials[i] = Material.PurpleCarpet; + for (int i = 12907; i <= 12907; i++) + materials[i] = Material.BlueCarpet; + for (int i = 12908; i <= 12908; i++) + materials[i] = Material.BrownCarpet; + for (int i = 12909; i <= 12909; i++) + materials[i] = Material.GreenCarpet; + for (int i = 12910; i <= 12910; i++) + materials[i] = Material.RedCarpet; + for (int i = 12911; i <= 12911; i++) + materials[i] = Material.BlackCarpet; + for (int i = 12912; i <= 12912; i++) + materials[i] = Material.Terracotta; + for (int i = 12913; i <= 12913; i++) + materials[i] = Material.CoalBlock; + for (int i = 12914; i <= 12914; i++) + materials[i] = Material.PackedIce; + for (int i = 12915; i <= 12916; i++) + materials[i] = Material.Sunflower; + for (int i = 12917; i <= 12918; i++) + materials[i] = Material.Lilac; + for (int i = 12919; i <= 12920; i++) + materials[i] = Material.RoseBush; + for (int i = 12921; i <= 12922; i++) + materials[i] = Material.Peony; + for (int i = 12923; i <= 12924; i++) + materials[i] = Material.TallGrass; + for (int i = 12925; i <= 12926; i++) + materials[i] = Material.LargeFern; + for (int i = 12927; i <= 12942; i++) + materials[i] = Material.WhiteBanner; + for (int i = 12943; i <= 12958; i++) + materials[i] = Material.OrangeBanner; + for (int i = 12959; i <= 12974; i++) + materials[i] = Material.MagentaBanner; + for (int i = 12975; i <= 12990; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 12991; i <= 13006; i++) + materials[i] = Material.YellowBanner; + for (int i = 13007; i <= 13022; i++) + materials[i] = Material.LimeBanner; + for (int i = 13023; i <= 13038; i++) + materials[i] = Material.PinkBanner; + for (int i = 13039; i <= 13054; i++) + materials[i] = Material.GrayBanner; + for (int i = 13055; i <= 13070; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 13071; i <= 13086; i++) + materials[i] = Material.CyanBanner; + for (int i = 13087; i <= 13102; i++) + materials[i] = Material.PurpleBanner; + for (int i = 13103; i <= 13118; i++) + materials[i] = Material.BlueBanner; + for (int i = 13119; i <= 13134; i++) + materials[i] = Material.BrownBanner; + for (int i = 13135; i <= 13150; i++) + materials[i] = Material.GreenBanner; + for (int i = 13151; i <= 13166; i++) + materials[i] = Material.RedBanner; + for (int i = 13167; i <= 13182; i++) + materials[i] = Material.BlackBanner; + for (int i = 13183; i <= 13186; i++) + materials[i] = Material.WhiteWallBanner; + for (int i = 13187; i <= 13190; i++) + materials[i] = Material.OrangeWallBanner; + for (int i = 13191; i <= 13194; i++) + materials[i] = Material.MagentaWallBanner; + for (int i = 13195; i <= 13198; i++) + materials[i] = Material.LightBlueWallBanner; + for (int i = 13199; i <= 13202; i++) + materials[i] = Material.YellowWallBanner; + for (int i = 13203; i <= 13206; i++) + materials[i] = Material.LimeWallBanner; + for (int i = 13207; i <= 13210; i++) + materials[i] = Material.PinkWallBanner; + for (int i = 13211; i <= 13214; i++) + materials[i] = Material.GrayWallBanner; + for (int i = 13215; i <= 13218; i++) + materials[i] = Material.LightGrayWallBanner; + for (int i = 13219; i <= 13222; i++) + materials[i] = Material.CyanWallBanner; + for (int i = 13223; i <= 13226; i++) + materials[i] = Material.PurpleWallBanner; + for (int i = 13227; i <= 13230; i++) + materials[i] = Material.BlueWallBanner; + for (int i = 13231; i <= 13234; i++) + materials[i] = Material.BrownWallBanner; + for (int i = 13235; i <= 13238; i++) + materials[i] = Material.GreenWallBanner; + for (int i = 13239; i <= 13242; i++) + materials[i] = Material.RedWallBanner; + for (int i = 13243; i <= 13246; i++) + materials[i] = Material.BlackWallBanner; + for (int i = 13247; i <= 13247; i++) + materials[i] = Material.RedSandstone; + for (int i = 13248; i <= 13248; i++) + materials[i] = Material.ChiseledRedSandstone; + for (int i = 13249; i <= 13249; i++) + materials[i] = Material.CutRedSandstone; + for (int i = 13250; i <= 13329; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 13330; i <= 13335; i++) + materials[i] = Material.OakSlab; + for (int i = 13336; i <= 13341; i++) + materials[i] = Material.SpruceSlab; + for (int i = 13342; i <= 13347; i++) + materials[i] = Material.BirchSlab; + for (int i = 13348; i <= 13353; i++) + materials[i] = Material.JungleSlab; + for (int i = 13354; i <= 13359; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 13360; i <= 13365; i++) + materials[i] = Material.CherrySlab; + for (int i = 13366; i <= 13371; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 13372; i <= 13377; i++) + materials[i] = Material.PaleOakSlab; + for (int i = 13378; i <= 13383; i++) + materials[i] = Material.MangroveSlab; + for (int i = 13384; i <= 13389; i++) + materials[i] = Material.BambooSlab; + for (int i = 13390; i <= 13395; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 13396; i <= 13401; i++) + materials[i] = Material.StoneSlab; + for (int i = 13402; i <= 13407; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 13408; i <= 13413; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 13414; i <= 13419; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 13420; i <= 13425; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 13426; i <= 13431; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 13432; i <= 13437; i++) + materials[i] = Material.BrickSlab; + for (int i = 13438; i <= 13443; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 13444; i <= 13449; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 13450; i <= 13455; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 13456; i <= 13461; i++) + materials[i] = Material.QuartzSlab; + for (int i = 13462; i <= 13467; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 13468; i <= 13473; i++) + materials[i] = Material.CutRedSandstoneSlab; + for (int i = 13474; i <= 13479; i++) + materials[i] = Material.PurpurSlab; + for (int i = 13480; i <= 13480; i++) + materials[i] = Material.SmoothStone; + for (int i = 13481; i <= 13481; i++) + materials[i] = Material.SmoothSandstone; + for (int i = 13482; i <= 13482; i++) + materials[i] = Material.SmoothQuartz; + for (int i = 13483; i <= 13483; i++) + materials[i] = Material.SmoothRedSandstone; + for (int i = 13484; i <= 13515; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 13516; i <= 13547; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 13548; i <= 13579; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 13580; i <= 13611; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 13612; i <= 13643; i++) + materials[i] = Material.CherryFenceGate; + for (int i = 13644; i <= 13675; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 13676; i <= 13707; i++) + materials[i] = Material.PaleOakFenceGate; + for (int i = 13708; i <= 13739; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 13740; i <= 13771; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 13772; i <= 13803; i++) + materials[i] = Material.SpruceFence; + for (int i = 13804; i <= 13835; i++) + materials[i] = Material.BirchFence; + for (int i = 13836; i <= 13867; i++) + materials[i] = Material.JungleFence; + for (int i = 13868; i <= 13899; i++) + materials[i] = Material.AcaciaFence; + for (int i = 13900; i <= 13931; i++) + materials[i] = Material.CherryFence; + for (int i = 13932; i <= 13963; i++) + materials[i] = Material.DarkOakFence; + for (int i = 13964; i <= 13995; i++) + materials[i] = Material.PaleOakFence; + for (int i = 13996; i <= 14027; i++) + materials[i] = Material.MangroveFence; + for (int i = 14028; i <= 14059; i++) + materials[i] = Material.BambooFence; + for (int i = 14060; i <= 14123; i++) + materials[i] = Material.SpruceDoor; + for (int i = 14124; i <= 14187; i++) + materials[i] = Material.BirchDoor; + for (int i = 14188; i <= 14251; i++) + materials[i] = Material.JungleDoor; + for (int i = 14252; i <= 14315; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 14316; i <= 14379; i++) + materials[i] = Material.CherryDoor; + for (int i = 14380; i <= 14443; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 14444; i <= 14507; i++) + materials[i] = Material.PaleOakDoor; + for (int i = 14508; i <= 14571; i++) + materials[i] = Material.MangroveDoor; + for (int i = 14572; i <= 14635; i++) + materials[i] = Material.BambooDoor; + for (int i = 14636; i <= 14641; i++) + materials[i] = Material.EndRod; + for (int i = 14642; i <= 14705; i++) + materials[i] = Material.ChorusPlant; + for (int i = 14706; i <= 14711; i++) + materials[i] = Material.ChorusFlower; + for (int i = 14712; i <= 14712; i++) + materials[i] = Material.PurpurBlock; + for (int i = 14713; i <= 14715; i++) + materials[i] = Material.PurpurPillar; + for (int i = 14716; i <= 14795; i++) + materials[i] = Material.PurpurStairs; + for (int i = 14796; i <= 14796; i++) + materials[i] = Material.EndStoneBricks; + for (int i = 14797; i <= 14798; i++) + materials[i] = Material.TorchflowerCrop; + for (int i = 14799; i <= 14808; i++) + materials[i] = Material.PitcherCrop; + for (int i = 14809; i <= 14810; i++) + materials[i] = Material.PitcherPlant; + for (int i = 14811; i <= 14814; i++) + materials[i] = Material.Beetroots; + for (int i = 14815; i <= 14815; i++) + materials[i] = Material.DirtPath; + for (int i = 14816; i <= 14816; i++) + materials[i] = Material.EndGateway; + for (int i = 14817; i <= 14828; i++) + materials[i] = Material.RepeatingCommandBlock; + for (int i = 14829; i <= 14840; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 14841; i <= 14844; i++) + materials[i] = Material.FrostedIce; + for (int i = 14845; i <= 14845; i++) + materials[i] = Material.MagmaBlock; + for (int i = 14846; i <= 14846; i++) + materials[i] = Material.NetherWartBlock; + for (int i = 14847; i <= 14847; i++) + materials[i] = Material.RedNetherBricks; + for (int i = 14848; i <= 14850; i++) + materials[i] = Material.BoneBlock; + for (int i = 14851; i <= 14851; i++) + materials[i] = Material.StructureVoid; + for (int i = 14852; i <= 14863; i++) + materials[i] = Material.Observer; + for (int i = 14864; i <= 14869; i++) + materials[i] = Material.ShulkerBox; + for (int i = 14870; i <= 14875; i++) + materials[i] = Material.WhiteShulkerBox; + for (int i = 14876; i <= 14881; i++) + materials[i] = Material.OrangeShulkerBox; + for (int i = 14882; i <= 14887; i++) + materials[i] = Material.MagentaShulkerBox; + for (int i = 14888; i <= 14893; i++) + materials[i] = Material.LightBlueShulkerBox; + for (int i = 14894; i <= 14899; i++) + materials[i] = Material.YellowShulkerBox; + for (int i = 14900; i <= 14905; i++) + materials[i] = Material.LimeShulkerBox; + for (int i = 14906; i <= 14911; i++) + materials[i] = Material.PinkShulkerBox; + for (int i = 14912; i <= 14917; i++) + materials[i] = Material.GrayShulkerBox; + for (int i = 14918; i <= 14923; i++) + materials[i] = Material.LightGrayShulkerBox; + for (int i = 14924; i <= 14929; i++) + materials[i] = Material.CyanShulkerBox; + for (int i = 14930; i <= 14935; i++) + materials[i] = Material.PurpleShulkerBox; + for (int i = 14936; i <= 14941; i++) + materials[i] = Material.BlueShulkerBox; + for (int i = 14942; i <= 14947; i++) + materials[i] = Material.BrownShulkerBox; + for (int i = 14948; i <= 14953; i++) + materials[i] = Material.GreenShulkerBox; + for (int i = 14954; i <= 14959; i++) + materials[i] = Material.RedShulkerBox; + for (int i = 14960; i <= 14965; i++) + materials[i] = Material.BlackShulkerBox; + for (int i = 14966; i <= 14969; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 14970; i <= 14973; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 14974; i <= 14977; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 14978; i <= 14981; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 14982; i <= 14985; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 14986; i <= 14989; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 14990; i <= 14993; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 14994; i <= 14997; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 14998; i <= 15001; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 15002; i <= 15005; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 15006; i <= 15009; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 15010; i <= 15013; i++) + materials[i] = Material.BlueGlazedTerracotta; + for (int i = 15014; i <= 15017; i++) + materials[i] = Material.BrownGlazedTerracotta; + for (int i = 15018; i <= 15021; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 15022; i <= 15025; i++) + materials[i] = Material.RedGlazedTerracotta; + for (int i = 15026; i <= 15029; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 15030; i <= 15030; i++) + materials[i] = Material.WhiteConcrete; + for (int i = 15031; i <= 15031; i++) + materials[i] = Material.OrangeConcrete; + for (int i = 15032; i <= 15032; i++) + materials[i] = Material.MagentaConcrete; + for (int i = 15033; i <= 15033; i++) + materials[i] = Material.LightBlueConcrete; + for (int i = 15034; i <= 15034; i++) + materials[i] = Material.YellowConcrete; + for (int i = 15035; i <= 15035; i++) + materials[i] = Material.LimeConcrete; + for (int i = 15036; i <= 15036; i++) + materials[i] = Material.PinkConcrete; + for (int i = 15037; i <= 15037; i++) + materials[i] = Material.GrayConcrete; + for (int i = 15038; i <= 15038; i++) + materials[i] = Material.LightGrayConcrete; + for (int i = 15039; i <= 15039; i++) + materials[i] = Material.CyanConcrete; + for (int i = 15040; i <= 15040; i++) + materials[i] = Material.PurpleConcrete; + for (int i = 15041; i <= 15041; i++) + materials[i] = Material.BlueConcrete; + for (int i = 15042; i <= 15042; i++) + materials[i] = Material.BrownConcrete; + for (int i = 15043; i <= 15043; i++) + materials[i] = Material.GreenConcrete; + for (int i = 15044; i <= 15044; i++) + materials[i] = Material.RedConcrete; + for (int i = 15045; i <= 15045; i++) + materials[i] = Material.BlackConcrete; + for (int i = 15046; i <= 15046; i++) + materials[i] = Material.WhiteConcretePowder; + for (int i = 15047; i <= 15047; i++) + materials[i] = Material.OrangeConcretePowder; + for (int i = 15048; i <= 15048; i++) + materials[i] = Material.MagentaConcretePowder; + for (int i = 15049; i <= 15049; i++) + materials[i] = Material.LightBlueConcretePowder; + for (int i = 15050; i <= 15050; i++) + materials[i] = Material.YellowConcretePowder; + for (int i = 15051; i <= 15051; i++) + materials[i] = Material.LimeConcretePowder; + for (int i = 15052; i <= 15052; i++) + materials[i] = Material.PinkConcretePowder; + for (int i = 15053; i <= 15053; i++) + materials[i] = Material.GrayConcretePowder; + for (int i = 15054; i <= 15054; i++) + materials[i] = Material.LightGrayConcretePowder; + for (int i = 15055; i <= 15055; i++) + materials[i] = Material.CyanConcretePowder; + for (int i = 15056; i <= 15056; i++) + materials[i] = Material.PurpleConcretePowder; + for (int i = 15057; i <= 15057; i++) + materials[i] = Material.BlueConcretePowder; + for (int i = 15058; i <= 15058; i++) + materials[i] = Material.BrownConcretePowder; + for (int i = 15059; i <= 15059; i++) + materials[i] = Material.GreenConcretePowder; + for (int i = 15060; i <= 15060; i++) + materials[i] = Material.RedConcretePowder; + for (int i = 15061; i <= 15061; i++) + materials[i] = Material.BlackConcretePowder; + for (int i = 15062; i <= 15087; i++) + materials[i] = Material.Kelp; + for (int i = 15088; i <= 15088; i++) + materials[i] = Material.KelpPlant; + for (int i = 15089; i <= 15089; i++) + materials[i] = Material.DriedKelpBlock; + for (int i = 15090; i <= 15101; i++) + materials[i] = Material.TurtleEgg; + for (int i = 15102; i <= 15104; i++) + materials[i] = Material.SnifferEgg; + for (int i = 15105; i <= 15136; i++) + materials[i] = Material.DriedGhast; + for (int i = 15137; i <= 15137; i++) + materials[i] = Material.DeadTubeCoralBlock; + for (int i = 15138; i <= 15138; i++) + materials[i] = Material.DeadBrainCoralBlock; + for (int i = 15139; i <= 15139; i++) + materials[i] = Material.DeadBubbleCoralBlock; + for (int i = 15140; i <= 15140; i++) + materials[i] = Material.DeadFireCoralBlock; + for (int i = 15141; i <= 15141; i++) + materials[i] = Material.DeadHornCoralBlock; + for (int i = 15142; i <= 15142; i++) + materials[i] = Material.TubeCoralBlock; + for (int i = 15143; i <= 15143; i++) + materials[i] = Material.BrainCoralBlock; + for (int i = 15144; i <= 15144; i++) + materials[i] = Material.BubbleCoralBlock; + for (int i = 15145; i <= 15145; i++) + materials[i] = Material.FireCoralBlock; + for (int i = 15146; i <= 15146; i++) + materials[i] = Material.HornCoralBlock; + for (int i = 15147; i <= 15148; i++) + materials[i] = Material.DeadTubeCoral; + for (int i = 15149; i <= 15150; i++) + materials[i] = Material.DeadBrainCoral; + for (int i = 15151; i <= 15152; i++) + materials[i] = Material.DeadBubbleCoral; + for (int i = 15153; i <= 15154; i++) + materials[i] = Material.DeadFireCoral; + for (int i = 15155; i <= 15156; i++) + materials[i] = Material.DeadHornCoral; + for (int i = 15157; i <= 15158; i++) + materials[i] = Material.TubeCoral; + for (int i = 15159; i <= 15160; i++) + materials[i] = Material.BrainCoral; + for (int i = 15161; i <= 15162; i++) + materials[i] = Material.BubbleCoral; + for (int i = 15163; i <= 15164; i++) + materials[i] = Material.FireCoral; + for (int i = 15165; i <= 15166; i++) + materials[i] = Material.HornCoral; + for (int i = 15167; i <= 15168; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 15169; i <= 15170; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 15171; i <= 15172; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 15173; i <= 15174; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 15175; i <= 15176; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 15177; i <= 15178; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 15179; i <= 15180; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 15181; i <= 15182; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 15183; i <= 15184; i++) + materials[i] = Material.FireCoralFan; + for (int i = 15185; i <= 15186; i++) + materials[i] = Material.HornCoralFan; + for (int i = 15187; i <= 15194; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 15195; i <= 15202; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 15203; i <= 15210; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + for (int i = 15211; i <= 15218; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 15219; i <= 15226; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 15227; i <= 15234; i++) + materials[i] = Material.TubeCoralWallFan; + for (int i = 15235; i <= 15242; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 15243; i <= 15250; i++) + materials[i] = Material.BubbleCoralWallFan; + for (int i = 15251; i <= 15258; i++) + materials[i] = Material.FireCoralWallFan; + for (int i = 15259; i <= 15266; i++) + materials[i] = Material.HornCoralWallFan; + for (int i = 15267; i <= 15274; i++) + materials[i] = Material.SeaPickle; + for (int i = 15275; i <= 15275; i++) + materials[i] = Material.BlueIce; + for (int i = 15276; i <= 15277; i++) + materials[i] = Material.Conduit; + for (int i = 15278; i <= 15278; i++) + materials[i] = Material.BambooSapling; + for (int i = 15279; i <= 15290; i++) + materials[i] = Material.Bamboo; + for (int i = 15291; i <= 15291; i++) + materials[i] = Material.PottedBamboo; + for (int i = 15292; i <= 15292; i++) + materials[i] = Material.VoidAir; + for (int i = 15293; i <= 15293; i++) + materials[i] = Material.CaveAir; + for (int i = 15294; i <= 15295; i++) + materials[i] = Material.BubbleColumn; + for (int i = 15296; i <= 15375; i++) + materials[i] = Material.PolishedGraniteStairs; + for (int i = 15376; i <= 15455; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + for (int i = 15456; i <= 15535; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 15536; i <= 15615; i++) + materials[i] = Material.PolishedDioriteStairs; + for (int i = 15616; i <= 15695; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 15696; i <= 15775; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 15776; i <= 15855; i++) + materials[i] = Material.StoneStairs; + for (int i = 15856; i <= 15935; i++) + materials[i] = Material.SmoothSandstoneStairs; + for (int i = 15936; i <= 16015; i++) + materials[i] = Material.SmoothQuartzStairs; + for (int i = 16016; i <= 16095; i++) + materials[i] = Material.GraniteStairs; + for (int i = 16096; i <= 16175; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 16176; i <= 16255; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 16256; i <= 16335; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 16336; i <= 16415; i++) + materials[i] = Material.DioriteStairs; + for (int i = 16416; i <= 16421; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 16422; i <= 16427; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 16428; i <= 16433; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 16434; i <= 16439; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 16440; i <= 16445; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 16446; i <= 16451; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 16452; i <= 16457; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 16458; i <= 16463; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 16464; i <= 16469; i++) + materials[i] = Material.GraniteSlab; + for (int i = 16470; i <= 16475; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 16476; i <= 16481; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 16482; i <= 16487; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 16488; i <= 16493; i++) + materials[i] = Material.DioriteSlab; + for (int i = 16494; i <= 16817; i++) + materials[i] = Material.BrickWall; + for (int i = 16818; i <= 17141; i++) + materials[i] = Material.PrismarineWall; + for (int i = 17142; i <= 17465; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 17466; i <= 17789; i++) + materials[i] = Material.MossyStoneBrickWall; + for (int i = 17790; i <= 18113; i++) + materials[i] = Material.GraniteWall; + for (int i = 18114; i <= 18437; i++) + materials[i] = Material.StoneBrickWall; + for (int i = 18438; i <= 18761; i++) + materials[i] = Material.MudBrickWall; + for (int i = 18762; i <= 19085; i++) + materials[i] = Material.NetherBrickWall; + for (int i = 19086; i <= 19409; i++) + materials[i] = Material.AndesiteWall; + for (int i = 19410; i <= 19733; i++) + materials[i] = Material.RedNetherBrickWall; + for (int i = 19734; i <= 20057; i++) + materials[i] = Material.SandstoneWall; + for (int i = 20058; i <= 20381; i++) + materials[i] = Material.EndStoneBrickWall; + for (int i = 20382; i <= 20705; i++) + materials[i] = Material.DioriteWall; + for (int i = 20706; i <= 20737; i++) + materials[i] = Material.Scaffolding; + for (int i = 20738; i <= 20741; i++) + materials[i] = Material.Loom; + for (int i = 20742; i <= 20753; i++) + materials[i] = Material.Barrel; + for (int i = 20754; i <= 20761; i++) + materials[i] = Material.Smoker; + for (int i = 20762; i <= 20769; i++) + materials[i] = Material.BlastFurnace; + for (int i = 20770; i <= 20770; i++) + materials[i] = Material.CartographyTable; + for (int i = 20771; i <= 20771; i++) + materials[i] = Material.FletchingTable; + for (int i = 20772; i <= 20783; i++) + materials[i] = Material.Grindstone; + for (int i = 20784; i <= 20799; i++) + materials[i] = Material.Lectern; + for (int i = 20800; i <= 20800; i++) + materials[i] = Material.SmithingTable; + for (int i = 20801; i <= 20804; i++) + materials[i] = Material.Stonecutter; + for (int i = 20805; i <= 20836; i++) + materials[i] = Material.Bell; + for (int i = 20837; i <= 20840; i++) + materials[i] = Material.Lantern; + for (int i = 20841; i <= 20844; i++) + materials[i] = Material.SoulLantern; + for (int i = 20845; i <= 20848; i++) + materials[i] = Material.CopperLantern; + for (int i = 20849; i <= 20852; i++) + materials[i] = Material.ExposedCopperLantern; + for (int i = 20853; i <= 20856; i++) + materials[i] = Material.WeatheredCopperLantern; + for (int i = 20857; i <= 20860; i++) + materials[i] = Material.OxidizedCopperLantern; + for (int i = 20861; i <= 20864; i++) + materials[i] = Material.WaxedCopperLantern; + for (int i = 20865; i <= 20868; i++) + materials[i] = Material.WaxedExposedCopperLantern; + for (int i = 20869; i <= 20872; i++) + materials[i] = Material.WaxedWeatheredCopperLantern; + for (int i = 20873; i <= 20876; i++) + materials[i] = Material.WaxedOxidizedCopperLantern; + for (int i = 20877; i <= 20908; i++) + materials[i] = Material.Campfire; + for (int i = 20909; i <= 20940; i++) + materials[i] = Material.SoulCampfire; + for (int i = 20941; i <= 20944; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 20945; i <= 20947; i++) + materials[i] = Material.WarpedStem; + for (int i = 20948; i <= 20950; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 20951; i <= 20953; i++) + materials[i] = Material.WarpedHyphae; + for (int i = 20954; i <= 20956; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 20957; i <= 20957; i++) + materials[i] = Material.WarpedNylium; + for (int i = 20958; i <= 20958; i++) + materials[i] = Material.WarpedFungus; + for (int i = 20959; i <= 20959; i++) + materials[i] = Material.WarpedWartBlock; + for (int i = 20960; i <= 20960; i++) + materials[i] = Material.WarpedRoots; + for (int i = 20961; i <= 20961; i++) + materials[i] = Material.NetherSprouts; + for (int i = 20962; i <= 20964; i++) + materials[i] = Material.CrimsonStem; + for (int i = 20965; i <= 20967; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 20968; i <= 20970; i++) + materials[i] = Material.CrimsonHyphae; + for (int i = 20971; i <= 20973; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 20974; i <= 20974; i++) + materials[i] = Material.CrimsonNylium; + for (int i = 20975; i <= 20975; i++) + materials[i] = Material.CrimsonFungus; + for (int i = 20976; i <= 20976; i++) + materials[i] = Material.Shroomlight; + for (int i = 20977; i <= 21002; i++) + materials[i] = Material.WeepingVines; + for (int i = 21003; i <= 21003; i++) + materials[i] = Material.WeepingVinesPlant; + for (int i = 21004; i <= 21029; i++) + materials[i] = Material.TwistingVines; + for (int i = 21030; i <= 21030; i++) + materials[i] = Material.TwistingVinesPlant; + for (int i = 21031; i <= 21031; i++) + materials[i] = Material.CrimsonRoots; + for (int i = 21032; i <= 21032; i++) + materials[i] = Material.CrimsonPlanks; + for (int i = 21033; i <= 21033; i++) + materials[i] = Material.WarpedPlanks; + for (int i = 21034; i <= 21039; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 21040; i <= 21045; i++) + materials[i] = Material.WarpedSlab; + for (int i = 21046; i <= 21047; i++) + materials[i] = Material.CrimsonPressurePlate; + for (int i = 21048; i <= 21049; i++) + materials[i] = Material.WarpedPressurePlate; + for (int i = 21050; i <= 21081; i++) + materials[i] = Material.CrimsonFence; + for (int i = 21082; i <= 21113; i++) + materials[i] = Material.WarpedFence; + for (int i = 21114; i <= 21177; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 21178; i <= 21241; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 21242; i <= 21273; i++) + materials[i] = Material.CrimsonFenceGate; + for (int i = 21274; i <= 21305; i++) + materials[i] = Material.WarpedFenceGate; + for (int i = 21306; i <= 21385; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 21386; i <= 21465; i++) + materials[i] = Material.WarpedStairs; + for (int i = 21466; i <= 21489; i++) + materials[i] = Material.CrimsonButton; + for (int i = 21490; i <= 21513; i++) + materials[i] = Material.WarpedButton; + for (int i = 21514; i <= 21577; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 21578; i <= 21641; i++) + materials[i] = Material.WarpedDoor; + for (int i = 21642; i <= 21673; i++) + materials[i] = Material.CrimsonSign; + for (int i = 21674; i <= 21705; i++) + materials[i] = Material.WarpedSign; + for (int i = 21706; i <= 21713; i++) + materials[i] = Material.CrimsonWallSign; + for (int i = 21714; i <= 21721; i++) + materials[i] = Material.WarpedWallSign; + for (int i = 21722; i <= 21725; i++) + materials[i] = Material.StructureBlock; + for (int i = 21726; i <= 21737; i++) + materials[i] = Material.Jigsaw; + for (int i = 21738; i <= 21741; i++) + materials[i] = Material.TestBlock; + for (int i = 21742; i <= 21742; i++) + materials[i] = Material.TestInstanceBlock; + for (int i = 21743; i <= 21751; i++) + materials[i] = Material.Composter; + for (int i = 21752; i <= 21767; i++) + materials[i] = Material.Target; + for (int i = 21768; i <= 21791; i++) + materials[i] = Material.BeeNest; + for (int i = 21792; i <= 21815; i++) + materials[i] = Material.Beehive; + for (int i = 21816; i <= 21816; i++) + materials[i] = Material.HoneyBlock; + for (int i = 21817; i <= 21817; i++) + materials[i] = Material.HoneycombBlock; + for (int i = 21818; i <= 21818; i++) + materials[i] = Material.NetheriteBlock; + for (int i = 21819; i <= 21819; i++) + materials[i] = Material.AncientDebris; + for (int i = 21820; i <= 21820; i++) + materials[i] = Material.CryingObsidian; + for (int i = 21821; i <= 21825; i++) + materials[i] = Material.RespawnAnchor; + for (int i = 21826; i <= 21826; i++) + materials[i] = Material.PottedCrimsonFungus; + for (int i = 21827; i <= 21827; i++) + materials[i] = Material.PottedWarpedFungus; + for (int i = 21828; i <= 21828; i++) + materials[i] = Material.PottedCrimsonRoots; + for (int i = 21829; i <= 21829; i++) + materials[i] = Material.PottedWarpedRoots; + for (int i = 21830; i <= 21830; i++) + materials[i] = Material.Lodestone; + for (int i = 21831; i <= 21831; i++) + materials[i] = Material.Blackstone; + for (int i = 21832; i <= 21911; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 21912; i <= 22235; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 22236; i <= 22241; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 22242; i <= 22242; i++) + materials[i] = Material.PolishedBlackstone; + for (int i = 22243; i <= 22243; i++) + materials[i] = Material.PolishedBlackstoneBricks; + for (int i = 22244; i <= 22244; i++) + materials[i] = Material.CrackedPolishedBlackstoneBricks; + for (int i = 22245; i <= 22245; i++) + materials[i] = Material.ChiseledPolishedBlackstone; + for (int i = 22246; i <= 22251; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 22252; i <= 22331; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 22332; i <= 22655; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + for (int i = 22656; i <= 22656; i++) + materials[i] = Material.GildedBlackstone; + for (int i = 22657; i <= 22736; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 22737; i <= 22742; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 22743; i <= 22744; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 22745; i <= 22768; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 22769; i <= 23092; i++) + materials[i] = Material.PolishedBlackstoneWall; + for (int i = 23093; i <= 23093; i++) + materials[i] = Material.ChiseledNetherBricks; + for (int i = 23094; i <= 23094; i++) + materials[i] = Material.CrackedNetherBricks; + for (int i = 23095; i <= 23095; i++) + materials[i] = Material.QuartzBricks; + for (int i = 23096; i <= 23111; i++) + materials[i] = Material.Candle; + for (int i = 23112; i <= 23127; i++) + materials[i] = Material.WhiteCandle; + for (int i = 23128; i <= 23143; i++) + materials[i] = Material.OrangeCandle; + for (int i = 23144; i <= 23159; i++) + materials[i] = Material.MagentaCandle; + for (int i = 23160; i <= 23175; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 23176; i <= 23191; i++) + materials[i] = Material.YellowCandle; + for (int i = 23192; i <= 23207; i++) + materials[i] = Material.LimeCandle; + for (int i = 23208; i <= 23223; i++) + materials[i] = Material.PinkCandle; + for (int i = 23224; i <= 23239; i++) + materials[i] = Material.GrayCandle; + for (int i = 23240; i <= 23255; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 23256; i <= 23271; i++) + materials[i] = Material.CyanCandle; + for (int i = 23272; i <= 23287; i++) + materials[i] = Material.PurpleCandle; + for (int i = 23288; i <= 23303; i++) + materials[i] = Material.BlueCandle; + for (int i = 23304; i <= 23319; i++) + materials[i] = Material.BrownCandle; + for (int i = 23320; i <= 23335; i++) + materials[i] = Material.GreenCandle; + for (int i = 23336; i <= 23351; i++) + materials[i] = Material.RedCandle; + for (int i = 23352; i <= 23367; i++) + materials[i] = Material.BlackCandle; + for (int i = 23368; i <= 23369; i++) + materials[i] = Material.CandleCake; + for (int i = 23370; i <= 23371; i++) + materials[i] = Material.WhiteCandleCake; + for (int i = 23372; i <= 23373; i++) + materials[i] = Material.OrangeCandleCake; + for (int i = 23374; i <= 23375; i++) + materials[i] = Material.MagentaCandleCake; + for (int i = 23376; i <= 23377; i++) + materials[i] = Material.LightBlueCandleCake; + for (int i = 23378; i <= 23379; i++) + materials[i] = Material.YellowCandleCake; + for (int i = 23380; i <= 23381; i++) + materials[i] = Material.LimeCandleCake; + for (int i = 23382; i <= 23383; i++) + materials[i] = Material.PinkCandleCake; + for (int i = 23384; i <= 23385; i++) + materials[i] = Material.GrayCandleCake; + for (int i = 23386; i <= 23387; i++) + materials[i] = Material.LightGrayCandleCake; + for (int i = 23388; i <= 23389; i++) + materials[i] = Material.CyanCandleCake; + for (int i = 23390; i <= 23391; i++) + materials[i] = Material.PurpleCandleCake; + for (int i = 23392; i <= 23393; i++) + materials[i] = Material.BlueCandleCake; + for (int i = 23394; i <= 23395; i++) + materials[i] = Material.BrownCandleCake; + for (int i = 23396; i <= 23397; i++) + materials[i] = Material.GreenCandleCake; + for (int i = 23398; i <= 23399; i++) + materials[i] = Material.RedCandleCake; + for (int i = 23400; i <= 23401; i++) + materials[i] = Material.BlackCandleCake; + for (int i = 23402; i <= 23402; i++) + materials[i] = Material.AmethystBlock; + for (int i = 23403; i <= 23403; i++) + materials[i] = Material.BuddingAmethyst; + for (int i = 23404; i <= 23415; i++) + materials[i] = Material.AmethystCluster; + for (int i = 23416; i <= 23427; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 23428; i <= 23439; i++) + materials[i] = Material.MediumAmethystBud; + for (int i = 23440; i <= 23451; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 23452; i <= 23452; i++) + materials[i] = Material.Tuff; + for (int i = 23453; i <= 23458; i++) + materials[i] = Material.TuffSlab; + for (int i = 23459; i <= 23538; i++) + materials[i] = Material.TuffStairs; + for (int i = 23539; i <= 23862; i++) + materials[i] = Material.TuffWall; + for (int i = 23863; i <= 23863; i++) + materials[i] = Material.PolishedTuff; + for (int i = 23864; i <= 23869; i++) + materials[i] = Material.PolishedTuffSlab; + for (int i = 23870; i <= 23949; i++) + materials[i] = Material.PolishedTuffStairs; + for (int i = 23950; i <= 24273; i++) + materials[i] = Material.PolishedTuffWall; + for (int i = 24274; i <= 24274; i++) + materials[i] = Material.ChiseledTuff; + for (int i = 24275; i <= 24275; i++) + materials[i] = Material.TuffBricks; + for (int i = 24276; i <= 24281; i++) + materials[i] = Material.TuffBrickSlab; + for (int i = 24282; i <= 24361; i++) + materials[i] = Material.TuffBrickStairs; + for (int i = 24362; i <= 24685; i++) + materials[i] = Material.TuffBrickWall; + for (int i = 24686; i <= 24686; i++) + materials[i] = Material.ChiseledTuffBricks; + for (int i = 24687; i <= 24687; i++) + materials[i] = Material.Calcite; + for (int i = 24688; i <= 24688; i++) + materials[i] = Material.TintedGlass; + for (int i = 24689; i <= 24689; i++) + materials[i] = Material.PowderSnow; + for (int i = 24690; i <= 24785; i++) + materials[i] = Material.SculkSensor; + for (int i = 24786; i <= 25169; i++) + materials[i] = Material.CalibratedSculkSensor; + for (int i = 25170; i <= 25170; i++) + materials[i] = Material.Sculk; + for (int i = 25171; i <= 25298; i++) + materials[i] = Material.SculkVein; + for (int i = 25299; i <= 25300; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 25301; i <= 25308; i++) + materials[i] = Material.SculkShrieker; + for (int i = 25309; i <= 25309; i++) + materials[i] = Material.CopperBlock; + for (int i = 25310; i <= 25310; i++) + materials[i] = Material.ExposedCopper; + for (int i = 25311; i <= 25311; i++) + materials[i] = Material.WeatheredCopper; + for (int i = 25312; i <= 25312; i++) + materials[i] = Material.OxidizedCopper; + for (int i = 25313; i <= 25313; i++) + materials[i] = Material.CopperOre; + for (int i = 25314; i <= 25314; i++) + materials[i] = Material.DeepslateCopperOre; + for (int i = 25315; i <= 25315; i++) + materials[i] = Material.OxidizedCutCopper; + for (int i = 25316; i <= 25316; i++) + materials[i] = Material.WeatheredCutCopper; + for (int i = 25317; i <= 25317; i++) + materials[i] = Material.ExposedCutCopper; + for (int i = 25318; i <= 25318; i++) + materials[i] = Material.CutCopper; + for (int i = 25319; i <= 25319; i++) + materials[i] = Material.OxidizedChiseledCopper; + for (int i = 25320; i <= 25320; i++) + materials[i] = Material.WeatheredChiseledCopper; + for (int i = 25321; i <= 25321; i++) + materials[i] = Material.ExposedChiseledCopper; + for (int i = 25322; i <= 25322; i++) + materials[i] = Material.ChiseledCopper; + for (int i = 25323; i <= 25323; i++) + materials[i] = Material.WaxedOxidizedChiseledCopper; + for (int i = 25324; i <= 25324; i++) + materials[i] = Material.WaxedWeatheredChiseledCopper; + for (int i = 25325; i <= 25325; i++) + materials[i] = Material.WaxedExposedChiseledCopper; + for (int i = 25326; i <= 25326; i++) + materials[i] = Material.WaxedChiseledCopper; + for (int i = 25327; i <= 25406; i++) + materials[i] = Material.OxidizedCutCopperStairs; + for (int i = 25407; i <= 25486; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 25487; i <= 25566; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 25567; i <= 25646; i++) + materials[i] = Material.CutCopperStairs; + for (int i = 25647; i <= 25652; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 25653; i <= 25658; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 25659; i <= 25664; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 25665; i <= 25670; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 25671; i <= 25671; i++) + materials[i] = Material.WaxedCopperBlock; + for (int i = 25672; i <= 25672; i++) + materials[i] = Material.WaxedWeatheredCopper; + for (int i = 25673; i <= 25673; i++) + materials[i] = Material.WaxedExposedCopper; + for (int i = 25674; i <= 25674; i++) + materials[i] = Material.WaxedOxidizedCopper; + for (int i = 25675; i <= 25675; i++) + materials[i] = Material.WaxedOxidizedCutCopper; + for (int i = 25676; i <= 25676; i++) + materials[i] = Material.WaxedWeatheredCutCopper; + for (int i = 25677; i <= 25677; i++) + materials[i] = Material.WaxedExposedCutCopper; + for (int i = 25678; i <= 25678; i++) + materials[i] = Material.WaxedCutCopper; + for (int i = 25679; i <= 25758; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + for (int i = 25759; i <= 25838; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + for (int i = 25839; i <= 25918; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + for (int i = 25919; i <= 25998; i++) + materials[i] = Material.WaxedCutCopperStairs; + for (int i = 25999; i <= 26004; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 26005; i <= 26010; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 26011; i <= 26016; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 26017; i <= 26022; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 26023; i <= 26086; i++) + materials[i] = Material.CopperDoor; + for (int i = 26087; i <= 26150; i++) + materials[i] = Material.ExposedCopperDoor; + for (int i = 26151; i <= 26214; i++) + materials[i] = Material.OxidizedCopperDoor; + for (int i = 26215; i <= 26278; i++) + materials[i] = Material.WeatheredCopperDoor; + for (int i = 26279; i <= 26342; i++) + materials[i] = Material.WaxedCopperDoor; + for (int i = 26343; i <= 26406; i++) + materials[i] = Material.WaxedExposedCopperDoor; + for (int i = 26407; i <= 26470; i++) + materials[i] = Material.WaxedOxidizedCopperDoor; + for (int i = 26471; i <= 26534; i++) + materials[i] = Material.WaxedWeatheredCopperDoor; + for (int i = 26535; i <= 26598; i++) + materials[i] = Material.CopperTrapdoor; + for (int i = 26599; i <= 26662; i++) + materials[i] = Material.ExposedCopperTrapdoor; + for (int i = 26663; i <= 26726; i++) + materials[i] = Material.OxidizedCopperTrapdoor; + for (int i = 26727; i <= 26790; i++) + materials[i] = Material.WeatheredCopperTrapdoor; + for (int i = 26791; i <= 26854; i++) + materials[i] = Material.WaxedCopperTrapdoor; + for (int i = 26855; i <= 26918; i++) + materials[i] = Material.WaxedExposedCopperTrapdoor; + for (int i = 26919; i <= 26982; i++) + materials[i] = Material.WaxedOxidizedCopperTrapdoor; + for (int i = 26983; i <= 27046; i++) + materials[i] = Material.WaxedWeatheredCopperTrapdoor; + for (int i = 27047; i <= 27048; i++) + materials[i] = Material.CopperGrate; + for (int i = 27049; i <= 27050; i++) + materials[i] = Material.ExposedCopperGrate; + for (int i = 27051; i <= 27052; i++) + materials[i] = Material.WeatheredCopperGrate; + for (int i = 27053; i <= 27054; i++) + materials[i] = Material.OxidizedCopperGrate; + for (int i = 27055; i <= 27056; i++) + materials[i] = Material.WaxedCopperGrate; + for (int i = 27057; i <= 27058; i++) + materials[i] = Material.WaxedExposedCopperGrate; + for (int i = 27059; i <= 27060; i++) + materials[i] = Material.WaxedWeatheredCopperGrate; + for (int i = 27061; i <= 27062; i++) + materials[i] = Material.WaxedOxidizedCopperGrate; + for (int i = 27063; i <= 27066; i++) + materials[i] = Material.CopperBulb; + for (int i = 27067; i <= 27070; i++) + materials[i] = Material.ExposedCopperBulb; + for (int i = 27071; i <= 27074; i++) + materials[i] = Material.WeatheredCopperBulb; + for (int i = 27075; i <= 27078; i++) + materials[i] = Material.OxidizedCopperBulb; + for (int i = 27079; i <= 27082; i++) + materials[i] = Material.WaxedCopperBulb; + for (int i = 27083; i <= 27086; i++) + materials[i] = Material.WaxedExposedCopperBulb; + for (int i = 27087; i <= 27090; i++) + materials[i] = Material.WaxedWeatheredCopperBulb; + for (int i = 27091; i <= 27094; i++) + materials[i] = Material.WaxedOxidizedCopperBulb; + for (int i = 27095; i <= 27118; i++) + materials[i] = Material.CopperChest; + for (int i = 27119; i <= 27142; i++) + materials[i] = Material.ExposedCopperChest; + for (int i = 27143; i <= 27166; i++) + materials[i] = Material.WeatheredCopperChest; + for (int i = 27167; i <= 27190; i++) + materials[i] = Material.OxidizedCopperChest; + for (int i = 27191; i <= 27214; i++) + materials[i] = Material.WaxedCopperChest; + for (int i = 27215; i <= 27238; i++) + materials[i] = Material.WaxedExposedCopperChest; + for (int i = 27239; i <= 27262; i++) + materials[i] = Material.WaxedWeatheredCopperChest; + for (int i = 27263; i <= 27286; i++) + materials[i] = Material.WaxedOxidizedCopperChest; + for (int i = 27287; i <= 27318; i++) + materials[i] = Material.CopperGolemStatue; + for (int i = 27319; i <= 27350; i++) + materials[i] = Material.ExposedCopperGolemStatue; + for (int i = 27351; i <= 27382; i++) + materials[i] = Material.WeatheredCopperGolemStatue; + for (int i = 27383; i <= 27414; i++) + materials[i] = Material.OxidizedCopperGolemStatue; + for (int i = 27415; i <= 27446; i++) + materials[i] = Material.WaxedCopperGolemStatue; + for (int i = 27447; i <= 27478; i++) + materials[i] = Material.WaxedExposedCopperGolemStatue; + for (int i = 27479; i <= 27510; i++) + materials[i] = Material.WaxedWeatheredCopperGolemStatue; + for (int i = 27511; i <= 27542; i++) + materials[i] = Material.WaxedOxidizedCopperGolemStatue; + for (int i = 27543; i <= 27566; i++) + materials[i] = Material.LightningRod; + for (int i = 27567; i <= 27590; i++) + materials[i] = Material.ExposedLightningRod; + for (int i = 27591; i <= 27614; i++) + materials[i] = Material.WeatheredLightningRod; + for (int i = 27615; i <= 27638; i++) + materials[i] = Material.OxidizedLightningRod; + for (int i = 27639; i <= 27662; i++) + materials[i] = Material.WaxedLightningRod; + for (int i = 27663; i <= 27686; i++) + materials[i] = Material.WaxedExposedLightningRod; + for (int i = 27687; i <= 27710; i++) + materials[i] = Material.WaxedWeatheredLightningRod; + for (int i = 27711; i <= 27734; i++) + materials[i] = Material.WaxedOxidizedLightningRod; + for (int i = 27735; i <= 27754; i++) + materials[i] = Material.PointedDripstone; + for (int i = 27755; i <= 27755; i++) + materials[i] = Material.DripstoneBlock; + for (int i = 27756; i <= 27807; i++) + materials[i] = Material.CaveVines; + for (int i = 27808; i <= 27809; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 27810; i <= 27810; i++) + materials[i] = Material.SporeBlossom; + for (int i = 27811; i <= 27811; i++) + materials[i] = Material.Azalea; + for (int i = 27812; i <= 27812; i++) + materials[i] = Material.FloweringAzalea; + for (int i = 27813; i <= 27813; i++) + materials[i] = Material.MossCarpet; + for (int i = 27814; i <= 27829; i++) + materials[i] = Material.PinkPetals; + for (int i = 27830; i <= 27845; i++) + materials[i] = Material.Wildflowers; + for (int i = 27846; i <= 27861; i++) + materials[i] = Material.LeafLitter; + for (int i = 27862; i <= 27862; i++) + materials[i] = Material.MossBlock; + for (int i = 27863; i <= 27894; i++) + materials[i] = Material.BigDripleaf; + for (int i = 27895; i <= 27902; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 27903; i <= 27918; i++) + materials[i] = Material.SmallDripleaf; + for (int i = 27919; i <= 27920; i++) + materials[i] = Material.HangingRoots; + for (int i = 27921; i <= 27921; i++) + materials[i] = Material.RootedDirt; + for (int i = 27922; i <= 27922; i++) + materials[i] = Material.Mud; + for (int i = 27923; i <= 27925; i++) + materials[i] = Material.Deepslate; + for (int i = 27926; i <= 27926; i++) + materials[i] = Material.CobbledDeepslate; + for (int i = 27927; i <= 28006; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 28007; i <= 28012; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 28013; i <= 28336; i++) + materials[i] = Material.CobbledDeepslateWall; + for (int i = 28337; i <= 28337; i++) + materials[i] = Material.PolishedDeepslate; + for (int i = 28338; i <= 28417; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 28418; i <= 28423; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 28424; i <= 28747; i++) + materials[i] = Material.PolishedDeepslateWall; + for (int i = 28748; i <= 28748; i++) + materials[i] = Material.DeepslateTiles; + for (int i = 28749; i <= 28828; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 28829; i <= 28834; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 28835; i <= 29158; i++) + materials[i] = Material.DeepslateTileWall; + for (int i = 29159; i <= 29159; i++) + materials[i] = Material.DeepslateBricks; + for (int i = 29160; i <= 29239; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 29240; i <= 29245; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 29246; i <= 29569; i++) + materials[i] = Material.DeepslateBrickWall; + for (int i = 29570; i <= 29570; i++) + materials[i] = Material.ChiseledDeepslate; + for (int i = 29571; i <= 29571; i++) + materials[i] = Material.CrackedDeepslateBricks; + for (int i = 29572; i <= 29572; i++) + materials[i] = Material.CrackedDeepslateTiles; + for (int i = 29573; i <= 29575; i++) + materials[i] = Material.InfestedDeepslate; + for (int i = 29576; i <= 29576; i++) + materials[i] = Material.SmoothBasalt; + for (int i = 29577; i <= 29577; i++) + materials[i] = Material.RawIronBlock; + for (int i = 29578; i <= 29578; i++) + materials[i] = Material.RawCopperBlock; + for (int i = 29579; i <= 29579; i++) + materials[i] = Material.RawGoldBlock; + for (int i = 29580; i <= 29580; i++) + materials[i] = Material.PottedAzaleaBush; + for (int i = 29581; i <= 29581; i++) + materials[i] = Material.PottedFloweringAzaleaBush; + for (int i = 29582; i <= 29584; i++) + materials[i] = Material.OchreFroglight; + for (int i = 29585; i <= 29587; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 29588; i <= 29590; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 29591; i <= 29591; i++) + materials[i] = Material.Frogspawn; + for (int i = 29592; i <= 29592; i++) + materials[i] = Material.ReinforcedDeepslate; + for (int i = 29593; i <= 29608; i++) + materials[i] = Material.DecoratedPot; + for (int i = 29609; i <= 29656; i++) + materials[i] = Material.Crafter; + for (int i = 29657; i <= 29668; i++) + materials[i] = Material.TrialSpawner; + for (int i = 29669; i <= 29700; i++) + materials[i] = Material.Vault; + for (int i = 29701; i <= 29702; i++) + materials[i] = Material.HeavyCore; + for (int i = 29703; i <= 29703; i++) + materials[i] = Material.PaleMossBlock; + for (int i = 29704; i <= 29865; i++) + materials[i] = Material.PaleMossCarpet; + for (int i = 29866; i <= 29867; i++) + materials[i] = Material.PaleHangingMoss; + for (int i = 29868; i <= 29868; i++) + materials[i] = Material.OpenEyeblossom; + for (int i = 29869; i <= 29869; i++) + materials[i] = Material.ClosedEyeblossom; + for (int i = 29870; i <= 29870; i++) + materials[i] = Material.PottedOpenEyeblossom; + for (int i = 29871; i <= 29871; i++) + materials[i] = Material.PottedClosedEyeblossom; + for (int i = 29872; i <= 29872; i++) + materials[i] = Material.FireflyBush; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/Dimension.cs b/MinecraftClient/Mapping/Dimension.cs index fe52b0e0..f9e8380f 100644 --- a/MinecraftClient/Mapping/Dimension.cs +++ b/MinecraftClient/Mapping/Dimension.cs @@ -129,7 +129,7 @@ namespace MinecraftClient.Mapping { Name = name ?? throw new ArgumentNullException(nameof(name)); - if (nbt == null) + if (nbt is null) throw new ArgumentNullException(nameof(nbt)); if (nbt.ContainsKey("piglin_safe")) diff --git a/MinecraftClient/Mapping/DirectionExtensions.cs b/MinecraftClient/Mapping/DirectionExtensions.cs new file mode 100644 index 00000000..4f965cde --- /dev/null +++ b/MinecraftClient/Mapping/DirectionExtensions.cs @@ -0,0 +1,42 @@ +using System; + +namespace MinecraftClient.Mapping; + +public static class DirectionExtensions +{ + public static Direction GetOpposite(this Direction direction) => direction switch + { + Direction.SouthEast => Direction.NorthEast, + Direction.SouthWest => Direction.NorthWest, + Direction.NorthEast => Direction.SouthEast, + Direction.NorthWest => Direction.SouthWest, + Direction.West => Direction.East, + Direction.East => Direction.West, + Direction.North => Direction.South, + Direction.South => Direction.North, + Direction.Down => Direction.Up, + Direction.Up => Direction.Down, + _ => Direction.Up, + }; + + public static Direction[] HORIZONTAL = + [ + Direction.South, + Direction.West, + Direction.North, + Direction.East, + ]; + + public static Direction FromRotation(double rotation) + { + double floor = Math.Floor((rotation / 90.0) + 0.5); + int value = (int)floor & 3; + + return FromHorizontal(value); + } + + public static Direction FromHorizontal(int value) + { + return HORIZONTAL[Math.Abs(value % HORIZONTAL.Length)]; + } +} diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index 3d1dd67e..33b250f0 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -99,6 +99,11 @@ namespace MinecraftClient.Mapping /// public Dictionary Equipment; + /// + /// Active status effects on this entity + /// + public Dictionary ActiveEffects { get; private set; } + /// /// Create a new entity based on Entity ID, Entity Type and location /// @@ -112,6 +117,7 @@ namespace MinecraftClient.Mapping Location = location; Health = 1.0f; Equipment = new Dictionary(); + ActiveEffects = new Dictionary(); Item = new Item(ItemType.Air, 0, null); } @@ -128,6 +134,7 @@ namespace MinecraftClient.Mapping Location = location; Health = 1.0f; Equipment = new Dictionary(); + ActiveEffects = new Dictionary(); Item = new Item(ItemType.Air, 0, null); Yaw = yaw * (1F / 256) * 360; // to angle in 360 degree Pitch = pitch * (1F / 256) * 360; @@ -151,6 +158,7 @@ namespace MinecraftClient.Mapping Name = name; Health = 1.0f; Equipment = new Dictionary(); + ActiveEffects = new Dictionary(); Item = new Item(ItemType.Air, 0, null); Yaw = yaw * (1F / 256) * 360; // to angle in 360 degree Pitch = pitch * (1F / 256) * 360; diff --git a/MinecraftClient/Mapping/EntityMetaDataType.cs b/MinecraftClient/Mapping/EntityMetaDataType.cs index db24f77b..5d89f185 100644 --- a/MinecraftClient/Mapping/EntityMetaDataType.cs +++ b/MinecraftClient/Mapping/EntityMetaDataType.cs @@ -26,6 +26,10 @@ public enum EntityMetaDataType Direction, OptionalUuid, /// + /// Boolean + UUID (1.21.5+, replaces OptionalUuid) + /// + OptionalLivingEntityReference, + /// /// VarInt /// BlockId, @@ -36,6 +40,10 @@ public enum EntityMetaDataType Nbt, Particle, /// + /// List of Particle (1.20.6+) + /// + Particles, + /// /// VarInt x3 /// VillagerData, @@ -48,8 +56,44 @@ public enum EntityMetaDataType /// VarInt /// CatVariant, + /// + /// VarInt (1.21.5+) + /// + CatSoundVariant, + /// + /// VarInt (1.20.6+) + /// + CowVariant, + /// + /// VarInt (1.21.5+) + /// + CowSoundVariant, + /// + /// VarInt (1.20.6+) + /// + WolfVariant, + /// + /// VarInt (1.21.5+) + /// + WolfSoundVariant, FrogVariant, /// + /// VarInt (1.21.5+) + /// + PigVariant, + /// + /// VarInt (1.21.5+) + /// + PigSoundVariant, + /// + /// VarInt (1.21.5+) + /// + ChickenVariant, + /// + /// VarInt (1.21.5+) + /// + ChickenSoundVariant, + /// /// String + Position /// GlobalPosition, @@ -66,11 +110,35 @@ public enum EntityMetaDataType /// SnifferState, /// + /// VarInt (1.20.6+) + /// + ArmadilloState, + /// + /// VarInt (1.21.9+) + /// + CopperGolemState, + /// + /// VarInt (1.21.9+) + /// + WeatheringCopperState, + /// /// Float x3 /// Vector3, /// /// Float x4 /// - Quaternion + Quaternion, + /// + /// Either<GameProfile, Partial> + PlayerSkin.Patch (1.21.9+) + /// + ResolvableProfile, + /// + /// VarInt (1.21.11+, holder registry ID) + /// + ZombieNautilusVariant, + /// + /// VarInt (1.21.11+, 0=LEFT, 1=RIGHT) + /// + HumanoidArm } \ No newline at end of file diff --git a/MinecraftClient/Mapping/EntityMetadataPalette.cs b/MinecraftClient/Mapping/EntityMetadataPalette.cs index 15963231..98875ffb 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalette.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalette.cs @@ -22,7 +22,12 @@ public abstract class EntityMetadataPalette <= Protocol18Handler.MC_1_12_2_Version => new EntityMetadataPalette1122(), // 1.9 - 1.12.2 <= Protocol18Handler.MC_1_19_2_Version => new EntityMetadataPalette1191(), // 1.13 - 1.19.2 <= Protocol18Handler.MC_1_19_3_Version => new EntityMetadataPalette1193(), // 1.19.3 - <= Protocol18Handler.MC_1_20_4_Version => new EntityMetadataPalette1194(), // 1.19.4 - 1.20.4 + + < Protocol18Handler.MC_1_20_6_Version => new EntityMetadataPalette1194(), // 1.19.4 - 1.20.4 + <= Protocol18Handler.MC_1_21_4_Version => new EntityMetadataPalette1206(), // 1.20.6 - 1.21.4 + <= Protocol18Handler.MC_1_21_7_Version => new EntityMetadataPalette1215(), // 1.21.5 - 1.21.8 + <= Protocol18Handler.MC_1_21_9_Version => new EntityMetadataPalette1219(), // 1.21.9 - 1.21.10 + <= Protocol18Handler.MC_1_21_11_Version => new EntityMetadataPalette12111(), // 1.21.11 + <= Protocol18Handler.MC_26_1_Version => new EntityMetadataPalette261(), // 26.1 _ => throw new NotImplementedException() }; } diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1122.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1122.cs index 1bb24765..1f9e08cf 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1122.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1122.cs @@ -22,7 +22,7 @@ public class EntityMetadataPalette1122 : EntityMetadataPalette { 12, EntityMetaDataType.OptionalBlockId }, { 13, EntityMetaDataType.Nbt }, }; - + public override Dictionary GetEntityMetadataMappingsList() { return entityMetadataMappings; diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1191.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1191.cs index bea16c9a..ef7f4117 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1191.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1191.cs @@ -33,7 +33,7 @@ public class EntityMetadataPalette1191 : EntityMetadataPalette { 21, EntityMetaDataType.OptionalGlobalPosition }, { 22, EntityMetaDataType.PaintingVariant } }; - + public override Dictionary GetEntityMetadataMappingsList() { return entityMetadataMappings; diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1193.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1193.cs index b6dffe4c..664b2cfa 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1193.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1193.cs @@ -34,7 +34,7 @@ public class EntityMetadataPalette1193 : EntityMetadataPalette { 22, EntityMetaDataType.OptionalGlobalPosition }, { 23, EntityMetaDataType.PaintingVariant } }; - + public override Dictionary GetEntityMetadataMappingsList() { return entityMetadataMappings; diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1194.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1194.cs index 2ac0e467..2b180afc 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1194.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1194.cs @@ -38,7 +38,7 @@ public class EntityMetadataPalette1194 : EntityMetadataPalette { 26, EntityMetaDataType.Vector3 }, { 27, EntityMetaDataType.Quaternion }, }; - + public override Dictionary GetEntityMetadataMappingsList() { return entityMetadataMappings; diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1206.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1206.cs new file mode 100644 index 00000000..67b61cc3 --- /dev/null +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1206.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityMetadataPalettes; + +/// +/// For 1.20.6+ +/// Added PARTICLES (id 18), WOLF_VARIANT (id 23), ARMADILLO_STATE (id 28) +/// compared to 1.19.4 palette. +/// +public class EntityMetadataPalette1206 : EntityMetadataPalette +{ + private readonly Dictionary entityMetadataMappings = new() + { + { 0, EntityMetaDataType.Byte }, + { 1, EntityMetaDataType.VarInt }, + { 2, EntityMetaDataType.VarLong }, + { 3, EntityMetaDataType.Float }, + { 4, EntityMetaDataType.String }, + { 5, EntityMetaDataType.Chat }, + { 6, EntityMetaDataType.OptionalChat }, + { 7, EntityMetaDataType.Slot }, + { 8, EntityMetaDataType.Boolean }, + { 9, EntityMetaDataType.Rotation }, + { 10, EntityMetaDataType.Position }, + { 11, EntityMetaDataType.OptionalPosition }, + { 12, EntityMetaDataType.Direction }, + { 13, EntityMetaDataType.OptionalUuid }, + { 14, EntityMetaDataType.BlockId }, + { 15, EntityMetaDataType.OptionalBlockId }, + { 16, EntityMetaDataType.Nbt }, + { 17, EntityMetaDataType.Particle }, + { 18, EntityMetaDataType.Particles }, + { 19, EntityMetaDataType.VillagerData }, + { 20, EntityMetaDataType.OptionalVarInt }, + { 21, EntityMetaDataType.Pose }, + { 22, EntityMetaDataType.CatVariant }, + { 23, EntityMetaDataType.WolfVariant }, + { 24, EntityMetaDataType.FrogVariant }, + { 25, EntityMetaDataType.OptionalGlobalPosition }, + { 26, EntityMetaDataType.PaintingVariant }, + { 27, EntityMetaDataType.SnifferState }, + { 28, EntityMetaDataType.ArmadilloState }, + { 29, EntityMetaDataType.Vector3 }, + { 30, EntityMetaDataType.Quaternion }, + }; + + public override Dictionary GetEntityMetadataMappingsList() + { + return entityMetadataMappings; + } +} diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette12111.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette12111.cs new file mode 100644 index 00000000..cff64189 --- /dev/null +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette12111.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityMetadataPalettes; + +public class EntityMetadataPalette12111 : EntityMetadataPalette +{ + private readonly Dictionary entityMetadataMappings = new() + { + { 0, EntityMetaDataType.Byte }, + { 1, EntityMetaDataType.VarInt }, + { 2, EntityMetaDataType.VarLong }, + { 3, EntityMetaDataType.Float }, + { 4, EntityMetaDataType.String }, + { 5, EntityMetaDataType.Chat }, + { 6, EntityMetaDataType.OptionalChat }, + { 7, EntityMetaDataType.Slot }, + { 8, EntityMetaDataType.Boolean }, + { 9, EntityMetaDataType.Rotation }, + { 10, EntityMetaDataType.Position }, + { 11, EntityMetaDataType.OptionalPosition }, + { 12, EntityMetaDataType.Direction }, + { 13, EntityMetaDataType.OptionalLivingEntityReference }, + { 14, EntityMetaDataType.BlockId }, + { 15, EntityMetaDataType.OptionalBlockId }, + { 16, EntityMetaDataType.Particle }, + { 17, EntityMetaDataType.Particles }, + { 18, EntityMetaDataType.VillagerData }, + { 19, EntityMetaDataType.OptionalVarInt }, + { 20, EntityMetaDataType.Pose }, + { 21, EntityMetaDataType.CatVariant }, + { 22, EntityMetaDataType.CowVariant }, + { 23, EntityMetaDataType.WolfVariant }, + { 24, EntityMetaDataType.WolfSoundVariant }, + { 25, EntityMetaDataType.FrogVariant }, + { 26, EntityMetaDataType.PigVariant }, + { 27, EntityMetaDataType.ChickenVariant }, + { 28, EntityMetaDataType.ZombieNautilusVariant }, + { 29, EntityMetaDataType.OptionalGlobalPosition }, + { 30, EntityMetaDataType.PaintingVariant }, + { 31, EntityMetaDataType.SnifferState }, + { 32, EntityMetaDataType.ArmadilloState }, + { 33, EntityMetaDataType.CopperGolemState }, + { 34, EntityMetaDataType.WeatheringCopperState }, + { 35, EntityMetaDataType.Vector3 }, + { 36, EntityMetaDataType.Quaternion }, + { 37, EntityMetaDataType.ResolvableProfile }, + { 38, EntityMetaDataType.HumanoidArm }, + }; + + public override Dictionary GetEntityMetadataMappingsList() + { + return entityMetadataMappings; + } +} diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1215.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1215.cs new file mode 100644 index 00000000..71653f03 --- /dev/null +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1215.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityMetadataPalettes; + +public class EntityMetadataPalette1215 : EntityMetadataPalette +{ + private readonly Dictionary entityMetadataMappings = new() + { + { 0, EntityMetaDataType.Byte }, + { 1, EntityMetaDataType.VarInt }, + { 2, EntityMetaDataType.VarLong }, + { 3, EntityMetaDataType.Float }, + { 4, EntityMetaDataType.String }, + { 5, EntityMetaDataType.Chat }, + { 6, EntityMetaDataType.OptionalChat }, + { 7, EntityMetaDataType.Slot }, + { 8, EntityMetaDataType.Boolean }, + { 9, EntityMetaDataType.Rotation }, + { 10, EntityMetaDataType.Position }, + { 11, EntityMetaDataType.OptionalPosition }, + { 12, EntityMetaDataType.Direction }, + { 13, EntityMetaDataType.OptionalLivingEntityReference }, + { 14, EntityMetaDataType.BlockId }, + { 15, EntityMetaDataType.OptionalBlockId }, + { 16, EntityMetaDataType.Nbt }, + { 17, EntityMetaDataType.Particle }, + { 18, EntityMetaDataType.Particles }, + { 19, EntityMetaDataType.VillagerData }, + { 20, EntityMetaDataType.OptionalVarInt }, + { 21, EntityMetaDataType.Pose }, + { 22, EntityMetaDataType.CatVariant }, + { 23, EntityMetaDataType.CowVariant }, + { 24, EntityMetaDataType.WolfVariant }, + { 25, EntityMetaDataType.WolfSoundVariant }, + { 26, EntityMetaDataType.FrogVariant }, + { 27, EntityMetaDataType.PigVariant }, + { 28, EntityMetaDataType.ChickenVariant }, + { 29, EntityMetaDataType.OptionalGlobalPosition }, + { 30, EntityMetaDataType.PaintingVariant }, + { 31, EntityMetaDataType.SnifferState }, + { 32, EntityMetaDataType.ArmadilloState }, + { 33, EntityMetaDataType.Vector3 }, + { 34, EntityMetaDataType.Quaternion }, + }; + + public override Dictionary GetEntityMetadataMappingsList() + { + return entityMetadataMappings; + } +} diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1219.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1219.cs new file mode 100644 index 00000000..2fa5d35e --- /dev/null +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1219.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityMetadataPalettes; + +public class EntityMetadataPalette1219 : EntityMetadataPalette +{ + private readonly Dictionary entityMetadataMappings = new() + { + { 0, EntityMetaDataType.Byte }, + { 1, EntityMetaDataType.VarInt }, + { 2, EntityMetaDataType.VarLong }, + { 3, EntityMetaDataType.Float }, + { 4, EntityMetaDataType.String }, + { 5, EntityMetaDataType.Chat }, + { 6, EntityMetaDataType.OptionalChat }, + { 7, EntityMetaDataType.Slot }, + { 8, EntityMetaDataType.Boolean }, + { 9, EntityMetaDataType.Rotation }, + { 10, EntityMetaDataType.Position }, + { 11, EntityMetaDataType.OptionalPosition }, + { 12, EntityMetaDataType.Direction }, + { 13, EntityMetaDataType.OptionalLivingEntityReference }, + { 14, EntityMetaDataType.BlockId }, + { 15, EntityMetaDataType.OptionalBlockId }, + { 16, EntityMetaDataType.Particle }, + { 17, EntityMetaDataType.Particles }, + { 18, EntityMetaDataType.VillagerData }, + { 19, EntityMetaDataType.OptionalVarInt }, + { 20, EntityMetaDataType.Pose }, + { 21, EntityMetaDataType.CatVariant }, + { 22, EntityMetaDataType.CowVariant }, + { 23, EntityMetaDataType.WolfVariant }, + { 24, EntityMetaDataType.WolfSoundVariant }, + { 25, EntityMetaDataType.FrogVariant }, + { 26, EntityMetaDataType.PigVariant }, + { 27, EntityMetaDataType.ChickenVariant }, + { 28, EntityMetaDataType.OptionalGlobalPosition }, + { 29, EntityMetaDataType.PaintingVariant }, + { 30, EntityMetaDataType.SnifferState }, + { 31, EntityMetaDataType.ArmadilloState }, + { 32, EntityMetaDataType.CopperGolemState }, + { 33, EntityMetaDataType.WeatheringCopperState }, + { 34, EntityMetaDataType.Vector3 }, + { 35, EntityMetaDataType.Quaternion }, + { 36, EntityMetaDataType.ResolvableProfile }, + }; + + public override Dictionary GetEntityMetadataMappingsList() + { + return entityMetadataMappings; + } +} diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette18.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette18.cs index c862092c..41da3b2f 100644 --- a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette18.cs +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette18.cs @@ -16,7 +16,7 @@ public class EntityMetadataPalette18 : EntityMetadataPalette { 6, EntityMetaDataType.Vector3Int }, { 7, EntityMetaDataType.Rotation } }; - + public override Dictionary GetEntityMetadataMappingsList() { return entityMetadataMappings; diff --git a/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette261.cs b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette261.cs new file mode 100644 index 00000000..d604b464 --- /dev/null +++ b/MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette261.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityMetadataPalettes; + +public class EntityMetadataPalette261 : EntityMetadataPalette +{ + private readonly Dictionary entityMetadataMappings = new() + { + { 0, EntityMetaDataType.Byte }, + { 1, EntityMetaDataType.VarInt }, + { 2, EntityMetaDataType.VarLong }, + { 3, EntityMetaDataType.Float }, + { 4, EntityMetaDataType.String }, + { 5, EntityMetaDataType.Chat }, + { 6, EntityMetaDataType.OptionalChat }, + { 7, EntityMetaDataType.Slot }, + { 8, EntityMetaDataType.Boolean }, + { 9, EntityMetaDataType.Rotation }, + { 10, EntityMetaDataType.Position }, + { 11, EntityMetaDataType.OptionalPosition }, + { 12, EntityMetaDataType.Direction }, + { 13, EntityMetaDataType.OptionalLivingEntityReference }, + { 14, EntityMetaDataType.BlockId }, + { 15, EntityMetaDataType.OptionalBlockId }, + { 16, EntityMetaDataType.Particle }, + { 17, EntityMetaDataType.Particles }, + { 18, EntityMetaDataType.VillagerData }, + { 19, EntityMetaDataType.OptionalVarInt }, + { 20, EntityMetaDataType.Pose }, + { 21, EntityMetaDataType.CatVariant }, + { 22, EntityMetaDataType.CatSoundVariant }, + { 23, EntityMetaDataType.CowVariant }, + { 24, EntityMetaDataType.CowSoundVariant }, + { 25, EntityMetaDataType.WolfVariant }, + { 26, EntityMetaDataType.WolfSoundVariant }, + { 27, EntityMetaDataType.FrogVariant }, + { 28, EntityMetaDataType.PigVariant }, + { 29, EntityMetaDataType.PigSoundVariant }, + { 30, EntityMetaDataType.ChickenVariant }, + { 31, EntityMetaDataType.ChickenSoundVariant }, + { 32, EntityMetaDataType.ZombieNautilusVariant }, + { 33, EntityMetaDataType.OptionalGlobalPosition }, + { 34, EntityMetaDataType.PaintingVariant }, + { 35, EntityMetaDataType.SnifferState }, + { 36, EntityMetaDataType.ArmadilloState }, + { 37, EntityMetaDataType.CopperGolemState }, + { 38, EntityMetaDataType.WeatheringCopperState }, + { 39, EntityMetaDataType.Vector3 }, + { 40, EntityMetaDataType.Quaternion }, + { 41, EntityMetaDataType.ResolvableProfile }, + { 42, EntityMetaDataType.HumanoidArm }, + }; + + public override Dictionary GetEntityMetadataMappingsList() + { + return entityMetadataMappings; + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs index d1e30c16..0873d5d4 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs @@ -29,9 +29,9 @@ namespace MinecraftClient.Mapping.EntityPalettes Dictionary entityTypes = GetDict(); Dictionary? entityTypesNonLiving = GetDictNonLiving(); - if (entityTypesNonLiving != null && !living) + if (entityTypesNonLiving is not null && !living) { - //Pre-1.14 non-living entities have a different set of IDs (entityTypesNonLiving != null) + //Pre-1.14 non-living entities have a different set of IDs (entityTypesNonLiving is not null) if (entityTypesNonLiving.ContainsKey(id)) return entityTypesNonLiving[id]; } diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs index 15d0dcd4..b5259508 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs @@ -9,7 +9,7 @@ namespace MinecraftClient.Mapping.EntityPalettes /// public class EntityPalette112 : EntityPalette { - private static Dictionary mappingsObjects = new Dictionary() + private static Dictionary mappingsObjects = new() { // https://wiki.vg/Entity_metadata#Objects { 1, EntityType.Boat }, @@ -41,7 +41,7 @@ namespace MinecraftClient.Mapping.EntityPalettes { 93, EntityType.DragonFireball }, }; - private static Dictionary mappingsMobs = new Dictionary() + private static Dictionary mappingsMobs = new() { { 1, EntityType.Item }, { 2, EntityType.ExperienceOrb }, diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs index d78a7240..ab19e391 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs @@ -9,7 +9,7 @@ namespace MinecraftClient.Mapping.EntityPalettes /// public class EntityPalette113 : EntityPalette { - private static Dictionary mappingsObjects = new Dictionary() + private static Dictionary mappingsObjects = new() { // https://wiki.vg/Entity_metadata#Objects { 1, EntityType.Boat }, @@ -42,7 +42,7 @@ namespace MinecraftClient.Mapping.EntityPalettes { 94, EntityType.Trident }, }; - private static Dictionary mappingsMobs = new Dictionary() + private static Dictionary mappingsMobs = new() { // https://wiki.vg/Entity_metadata#Mobs { 0, EntityType.AreaEffectCloud }, diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1206.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1206.cs new file mode 100644 index 00000000..734a431d --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1206.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1206 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1206() + { + mappings[0] = EntityType.Allay; + mappings[1] = EntityType.AreaEffectCloud; + mappings[2] = EntityType.Armadillo; + mappings[3] = EntityType.ArmorStand; + mappings[4] = EntityType.Arrow; + mappings[5] = EntityType.Axolotl; + mappings[6] = EntityType.Bat; + mappings[7] = EntityType.Bee; + mappings[8] = EntityType.Blaze; + mappings[9] = EntityType.BlockDisplay; + mappings[10] = EntityType.Boat; + mappings[11] = EntityType.Bogged; + mappings[12] = EntityType.Breeze; + mappings[13] = EntityType.BreezeWindCharge; + mappings[14] = EntityType.Camel; + mappings[15] = EntityType.Cat; + mappings[16] = EntityType.CaveSpider; + mappings[17] = EntityType.ChestBoat; + mappings[18] = EntityType.ChestMinecart; + mappings[19] = EntityType.Chicken; + mappings[20] = EntityType.Cod; + mappings[21] = EntityType.CommandBlockMinecart; + mappings[22] = EntityType.Cow; + mappings[23] = EntityType.Creeper; + mappings[24] = EntityType.Dolphin; + mappings[25] = EntityType.Donkey; + mappings[26] = EntityType.DragonFireball; + mappings[27] = EntityType.Drowned; + mappings[28] = EntityType.Egg; + mappings[29] = EntityType.ElderGuardian; + mappings[30] = EntityType.EndCrystal; + mappings[31] = EntityType.EnderDragon; + mappings[32] = EntityType.EnderPearl; + mappings[33] = EntityType.Enderman; + mappings[34] = EntityType.Endermite; + mappings[35] = EntityType.Evoker; + mappings[36] = EntityType.EvokerFangs; + mappings[37] = EntityType.ExperienceBottle; + mappings[38] = EntityType.ExperienceOrb; + mappings[39] = EntityType.EyeOfEnder; + mappings[40] = EntityType.FallingBlock; + mappings[62] = EntityType.Fireball; + mappings[41] = EntityType.FireworkRocket; + mappings[129] = EntityType.FishingBobber; + mappings[42] = EntityType.Fox; + mappings[43] = EntityType.Frog; + mappings[44] = EntityType.FurnaceMinecart; + mappings[45] = EntityType.Ghast; + mappings[46] = EntityType.Giant; + mappings[47] = EntityType.GlowItemFrame; + mappings[48] = EntityType.GlowSquid; + mappings[49] = EntityType.Goat; + mappings[50] = EntityType.Guardian; + mappings[51] = EntityType.Hoglin; + mappings[52] = EntityType.HopperMinecart; + mappings[53] = EntityType.Horse; + mappings[54] = EntityType.Husk; + mappings[55] = EntityType.Illusioner; + mappings[56] = EntityType.Interaction; + mappings[57] = EntityType.IronGolem; + mappings[58] = EntityType.Item; + mappings[59] = EntityType.ItemDisplay; + mappings[60] = EntityType.ItemFrame; + mappings[63] = EntityType.LeashKnot; + mappings[64] = EntityType.LightningBolt; + mappings[65] = EntityType.Llama; + mappings[66] = EntityType.LlamaSpit; + mappings[67] = EntityType.MagmaCube; + mappings[68] = EntityType.Marker; + mappings[69] = EntityType.Minecart; + mappings[70] = EntityType.Mooshroom; + mappings[71] = EntityType.Mule; + mappings[72] = EntityType.Ocelot; + mappings[61] = EntityType.OminousItemSpawner; + mappings[73] = EntityType.Painting; + mappings[74] = EntityType.Panda; + mappings[75] = EntityType.Parrot; + mappings[76] = EntityType.Phantom; + mappings[77] = EntityType.Pig; + mappings[78] = EntityType.Piglin; + mappings[79] = EntityType.PiglinBrute; + mappings[80] = EntityType.Pillager; + mappings[128] = EntityType.Player; + mappings[81] = EntityType.PolarBear; + mappings[82] = EntityType.Potion; + mappings[83] = EntityType.Pufferfish; + mappings[84] = EntityType.Rabbit; + mappings[85] = EntityType.Ravager; + mappings[86] = EntityType.Salmon; + mappings[87] = EntityType.Sheep; + mappings[88] = EntityType.Shulker; + mappings[89] = EntityType.ShulkerBullet; + mappings[90] = EntityType.Silverfish; + mappings[91] = EntityType.Skeleton; + mappings[92] = EntityType.SkeletonHorse; + mappings[93] = EntityType.Slime; + mappings[94] = EntityType.SmallFireball; + mappings[95] = EntityType.Sniffer; + mappings[96] = EntityType.SnowGolem; + mappings[97] = EntityType.Snowball; + mappings[98] = EntityType.SpawnerMinecart; + mappings[99] = EntityType.SpectralArrow; + mappings[100] = EntityType.Spider; + mappings[101] = EntityType.Squid; + mappings[102] = EntityType.Stray; + mappings[103] = EntityType.Strider; + mappings[104] = EntityType.Tadpole; + mappings[105] = EntityType.TextDisplay; + mappings[106] = EntityType.Tnt; + mappings[107] = EntityType.TntMinecart; + mappings[108] = EntityType.TraderLlama; + mappings[109] = EntityType.Trident; + mappings[110] = EntityType.TropicalFish; + mappings[111] = EntityType.Turtle; + mappings[112] = EntityType.Vex; + mappings[113] = EntityType.Villager; + mappings[114] = EntityType.Vindicator; + mappings[115] = EntityType.WanderingTrader; + mappings[116] = EntityType.Warden; + mappings[117] = EntityType.WindCharge; + mappings[118] = EntityType.Witch; + mappings[119] = EntityType.Wither; + mappings[120] = EntityType.WitherSkeleton; + mappings[121] = EntityType.WitherSkull; + mappings[122] = EntityType.Wolf; + mappings[123] = EntityType.Zoglin; + mappings[124] = EntityType.Zombie; + mappings[125] = EntityType.ZombieHorse; + mappings[126] = EntityType.ZombieVillager; + mappings[127] = EntityType.ZombifiedPiglin; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette12111.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette12111.cs new file mode 100644 index 00000000..5c3cb5ca --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette12111.cs @@ -0,0 +1,175 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette12111 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette12111() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.CamelHusk; + mappings[21] = EntityType.Cat; + mappings[22] = EntityType.CaveSpider; + mappings[23] = EntityType.CherryBoat; + mappings[24] = EntityType.CherryChestBoat; + mappings[25] = EntityType.ChestMinecart; + mappings[26] = EntityType.Chicken; + mappings[27] = EntityType.Cod; + mappings[28] = EntityType.CopperGolem; + mappings[29] = EntityType.CommandBlockMinecart; + mappings[30] = EntityType.Cow; + mappings[31] = EntityType.Creaking; + mappings[32] = EntityType.Creeper; + mappings[33] = EntityType.DarkOakBoat; + mappings[34] = EntityType.DarkOakChestBoat; + mappings[35] = EntityType.Dolphin; + mappings[36] = EntityType.Donkey; + mappings[37] = EntityType.DragonFireball; + mappings[38] = EntityType.Drowned; + mappings[39] = EntityType.Egg; + mappings[40] = EntityType.ElderGuardian; + mappings[41] = EntityType.Enderman; + mappings[42] = EntityType.Endermite; + mappings[43] = EntityType.EnderDragon; + mappings[44] = EntityType.EnderPearl; + mappings[45] = EntityType.EndCrystal; + mappings[46] = EntityType.Evoker; + mappings[47] = EntityType.EvokerFangs; + mappings[48] = EntityType.ExperienceBottle; + mappings[49] = EntityType.ExperienceOrb; + mappings[50] = EntityType.EyeOfEnder; + mappings[51] = EntityType.FallingBlock; + mappings[52] = EntityType.Fireball; + mappings[53] = EntityType.FireworkRocket; + mappings[54] = EntityType.Fox; + mappings[55] = EntityType.Frog; + mappings[56] = EntityType.FurnaceMinecart; + mappings[57] = EntityType.Ghast; + mappings[58] = EntityType.HappyGhast; + mappings[59] = EntityType.Giant; + mappings[60] = EntityType.GlowItemFrame; + mappings[61] = EntityType.GlowSquid; + mappings[62] = EntityType.Goat; + mappings[63] = EntityType.Guardian; + mappings[64] = EntityType.Hoglin; + mappings[65] = EntityType.HopperMinecart; + mappings[66] = EntityType.Horse; + mappings[67] = EntityType.Husk; + mappings[68] = EntityType.Illusioner; + mappings[69] = EntityType.Interaction; + mappings[70] = EntityType.IronGolem; + mappings[71] = EntityType.Item; + mappings[72] = EntityType.ItemDisplay; + mappings[73] = EntityType.ItemFrame; + mappings[74] = EntityType.JungleBoat; + mappings[75] = EntityType.JungleChestBoat; + mappings[76] = EntityType.LeashKnot; + mappings[77] = EntityType.LightningBolt; + mappings[78] = EntityType.Llama; + mappings[79] = EntityType.LlamaSpit; + mappings[80] = EntityType.MagmaCube; + mappings[81] = EntityType.MangroveBoat; + mappings[82] = EntityType.MangroveChestBoat; + mappings[83] = EntityType.Mannequin; + mappings[84] = EntityType.Marker; + mappings[85] = EntityType.Minecart; + mappings[86] = EntityType.Mooshroom; + mappings[87] = EntityType.Mule; + mappings[88] = EntityType.Nautilus; + mappings[89] = EntityType.OakBoat; + mappings[90] = EntityType.OakChestBoat; + mappings[91] = EntityType.Ocelot; + mappings[92] = EntityType.OminousItemSpawner; + mappings[93] = EntityType.Painting; + mappings[94] = EntityType.PaleOakBoat; + mappings[95] = EntityType.PaleOakChestBoat; + mappings[96] = EntityType.Panda; + mappings[97] = EntityType.Parched; + mappings[98] = EntityType.Parrot; + mappings[99] = EntityType.Phantom; + mappings[100] = EntityType.Pig; + mappings[101] = EntityType.Piglin; + mappings[102] = EntityType.PiglinBrute; + mappings[103] = EntityType.Pillager; + mappings[104] = EntityType.PolarBear; + mappings[105] = EntityType.SplashPotion; + mappings[106] = EntityType.LingeringPotion; + mappings[107] = EntityType.Pufferfish; + mappings[108] = EntityType.Rabbit; + mappings[109] = EntityType.Ravager; + mappings[110] = EntityType.Salmon; + mappings[111] = EntityType.Sheep; + mappings[112] = EntityType.Shulker; + mappings[113] = EntityType.ShulkerBullet; + mappings[114] = EntityType.Silverfish; + mappings[115] = EntityType.Skeleton; + mappings[116] = EntityType.SkeletonHorse; + mappings[117] = EntityType.Slime; + mappings[118] = EntityType.SmallFireball; + mappings[119] = EntityType.Sniffer; + mappings[120] = EntityType.Snowball; + mappings[121] = EntityType.SnowGolem; + mappings[122] = EntityType.SpawnerMinecart; + mappings[123] = EntityType.SpectralArrow; + mappings[124] = EntityType.Spider; + mappings[125] = EntityType.SpruceBoat; + mappings[126] = EntityType.SpruceChestBoat; + mappings[127] = EntityType.Squid; + mappings[128] = EntityType.Stray; + mappings[129] = EntityType.Strider; + mappings[130] = EntityType.Tadpole; + mappings[131] = EntityType.TextDisplay; + mappings[132] = EntityType.Tnt; + mappings[133] = EntityType.TntMinecart; + mappings[134] = EntityType.TraderLlama; + mappings[135] = EntityType.Trident; + mappings[136] = EntityType.TropicalFish; + mappings[137] = EntityType.Turtle; + mappings[138] = EntityType.Vex; + mappings[139] = EntityType.Villager; + mappings[140] = EntityType.Vindicator; + mappings[141] = EntityType.WanderingTrader; + mappings[142] = EntityType.Warden; + mappings[143] = EntityType.WindCharge; + mappings[144] = EntityType.Witch; + mappings[145] = EntityType.Wither; + mappings[146] = EntityType.WitherSkeleton; + mappings[147] = EntityType.WitherSkull; + mappings[148] = EntityType.Wolf; + mappings[149] = EntityType.Zoglin; + mappings[150] = EntityType.Zombie; + mappings[151] = EntityType.ZombieHorse; + mappings[152] = EntityType.ZombieNautilus; + mappings[153] = EntityType.ZombieVillager; + mappings[154] = EntityType.ZombifiedPiglin; + mappings[155] = EntityType.Player; + mappings[156] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1212.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1212.cs new file mode 100644 index 00000000..894738d2 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1212.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1212 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1212() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.Cat; + mappings[21] = EntityType.CaveSpider; + mappings[22] = EntityType.CherryBoat; + mappings[23] = EntityType.CherryChestBoat; + mappings[24] = EntityType.ChestMinecart; + mappings[25] = EntityType.Chicken; + mappings[26] = EntityType.Cod; + mappings[27] = EntityType.CommandBlockMinecart; + mappings[28] = EntityType.Cow; + mappings[29] = EntityType.Creaking; + mappings[30] = EntityType.CreakingTransient; + mappings[31] = EntityType.Creeper; + mappings[32] = EntityType.DarkOakBoat; + mappings[33] = EntityType.DarkOakChestBoat; + mappings[34] = EntityType.Dolphin; + mappings[35] = EntityType.Donkey; + mappings[36] = EntityType.DragonFireball; + mappings[37] = EntityType.Drowned; + mappings[38] = EntityType.Egg; + mappings[39] = EntityType.ElderGuardian; + mappings[40] = EntityType.Enderman; + mappings[41] = EntityType.Endermite; + mappings[42] = EntityType.EnderDragon; + mappings[43] = EntityType.EnderPearl; + mappings[44] = EntityType.EndCrystal; + mappings[45] = EntityType.Evoker; + mappings[46] = EntityType.EvokerFangs; + mappings[47] = EntityType.ExperienceBottle; + mappings[48] = EntityType.ExperienceOrb; + mappings[49] = EntityType.EyeOfEnder; + mappings[50] = EntityType.FallingBlock; + mappings[51] = EntityType.Fireball; + mappings[52] = EntityType.FireworkRocket; + mappings[53] = EntityType.Fox; + mappings[54] = EntityType.Frog; + mappings[55] = EntityType.FurnaceMinecart; + mappings[56] = EntityType.Ghast; + mappings[57] = EntityType.Giant; + mappings[58] = EntityType.GlowItemFrame; + mappings[59] = EntityType.GlowSquid; + mappings[60] = EntityType.Goat; + mappings[61] = EntityType.Guardian; + mappings[62] = EntityType.Hoglin; + mappings[63] = EntityType.HopperMinecart; + mappings[64] = EntityType.Horse; + mappings[65] = EntityType.Husk; + mappings[66] = EntityType.Illusioner; + mappings[67] = EntityType.Interaction; + mappings[68] = EntityType.IronGolem; + mappings[69] = EntityType.Item; + mappings[70] = EntityType.ItemDisplay; + mappings[71] = EntityType.ItemFrame; + mappings[72] = EntityType.JungleBoat; + mappings[73] = EntityType.JungleChestBoat; + mappings[74] = EntityType.LeashKnot; + mappings[75] = EntityType.LightningBolt; + mappings[76] = EntityType.Llama; + mappings[77] = EntityType.LlamaSpit; + mappings[78] = EntityType.MagmaCube; + mappings[79] = EntityType.MangroveBoat; + mappings[80] = EntityType.MangroveChestBoat; + mappings[81] = EntityType.Marker; + mappings[82] = EntityType.Minecart; + mappings[83] = EntityType.Mooshroom; + mappings[84] = EntityType.Mule; + mappings[85] = EntityType.OakBoat; + mappings[86] = EntityType.OakChestBoat; + mappings[87] = EntityType.Ocelot; + mappings[88] = EntityType.OminousItemSpawner; + mappings[89] = EntityType.Painting; + mappings[90] = EntityType.PaleOakBoat; + mappings[91] = EntityType.PaleOakChestBoat; + mappings[92] = EntityType.Panda; + mappings[93] = EntityType.Parrot; + mappings[94] = EntityType.Phantom; + mappings[95] = EntityType.Pig; + mappings[96] = EntityType.Piglin; + mappings[97] = EntityType.PiglinBrute; + mappings[98] = EntityType.Pillager; + mappings[99] = EntityType.PolarBear; + mappings[100] = EntityType.Potion; + mappings[101] = EntityType.Pufferfish; + mappings[102] = EntityType.Rabbit; + mappings[103] = EntityType.Ravager; + mappings[104] = EntityType.Salmon; + mappings[105] = EntityType.Sheep; + mappings[106] = EntityType.Shulker; + mappings[107] = EntityType.ShulkerBullet; + mappings[108] = EntityType.Silverfish; + mappings[109] = EntityType.Skeleton; + mappings[110] = EntityType.SkeletonHorse; + mappings[111] = EntityType.Slime; + mappings[112] = EntityType.SmallFireball; + mappings[113] = EntityType.Sniffer; + mappings[114] = EntityType.Snowball; + mappings[115] = EntityType.SnowGolem; + mappings[116] = EntityType.SpawnerMinecart; + mappings[117] = EntityType.SpectralArrow; + mappings[118] = EntityType.Spider; + mappings[119] = EntityType.SpruceBoat; + mappings[120] = EntityType.SpruceChestBoat; + mappings[121] = EntityType.Squid; + mappings[122] = EntityType.Stray; + mappings[123] = EntityType.Strider; + mappings[124] = EntityType.Tadpole; + mappings[125] = EntityType.TextDisplay; + mappings[126] = EntityType.Tnt; + mappings[127] = EntityType.TntMinecart; + mappings[128] = EntityType.TraderLlama; + mappings[129] = EntityType.Trident; + mappings[130] = EntityType.TropicalFish; + mappings[131] = EntityType.Turtle; + mappings[132] = EntityType.Vex; + mappings[133] = EntityType.Villager; + mappings[134] = EntityType.Vindicator; + mappings[135] = EntityType.WanderingTrader; + mappings[136] = EntityType.Warden; + mappings[137] = EntityType.WindCharge; + mappings[138] = EntityType.Witch; + mappings[139] = EntityType.Wither; + mappings[140] = EntityType.WitherSkeleton; + mappings[141] = EntityType.WitherSkull; + mappings[142] = EntityType.Wolf; + mappings[143] = EntityType.Zoglin; + mappings[144] = EntityType.Zombie; + mappings[145] = EntityType.ZombieHorse; + mappings[146] = EntityType.ZombieVillager; + mappings[147] = EntityType.ZombifiedPiglin; + mappings[148] = EntityType.Player; + mappings[149] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1214.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1214.cs new file mode 100644 index 00000000..dd047d1d --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1214.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1214 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1214() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.Cat; + mappings[21] = EntityType.CaveSpider; + mappings[22] = EntityType.CherryBoat; + mappings[23] = EntityType.CherryChestBoat; + mappings[24] = EntityType.ChestMinecart; + mappings[25] = EntityType.Chicken; + mappings[26] = EntityType.Cod; + mappings[27] = EntityType.CommandBlockMinecart; + mappings[28] = EntityType.Cow; + mappings[29] = EntityType.Creaking; + // CreakingTransient removed in 1.21.4 + mappings[30] = EntityType.Creeper; + mappings[31] = EntityType.DarkOakBoat; + mappings[32] = EntityType.DarkOakChestBoat; + mappings[33] = EntityType.Dolphin; + mappings[34] = EntityType.Donkey; + mappings[35] = EntityType.DragonFireball; + mappings[36] = EntityType.Drowned; + mappings[37] = EntityType.Egg; + mappings[38] = EntityType.ElderGuardian; + mappings[39] = EntityType.Enderman; + mappings[40] = EntityType.Endermite; + mappings[41] = EntityType.EnderDragon; + mappings[42] = EntityType.EnderPearl; + mappings[43] = EntityType.EndCrystal; + mappings[44] = EntityType.Evoker; + mappings[45] = EntityType.EvokerFangs; + mappings[46] = EntityType.ExperienceBottle; + mappings[47] = EntityType.ExperienceOrb; + mappings[48] = EntityType.EyeOfEnder; + mappings[49] = EntityType.FallingBlock; + mappings[50] = EntityType.Fireball; + mappings[51] = EntityType.FireworkRocket; + mappings[52] = EntityType.Fox; + mappings[53] = EntityType.Frog; + mappings[54] = EntityType.FurnaceMinecart; + mappings[55] = EntityType.Ghast; + mappings[56] = EntityType.Giant; + mappings[57] = EntityType.GlowItemFrame; + mappings[58] = EntityType.GlowSquid; + mappings[59] = EntityType.Goat; + mappings[60] = EntityType.Guardian; + mappings[61] = EntityType.Hoglin; + mappings[62] = EntityType.HopperMinecart; + mappings[63] = EntityType.Horse; + mappings[64] = EntityType.Husk; + mappings[65] = EntityType.Illusioner; + mappings[66] = EntityType.Interaction; + mappings[67] = EntityType.IronGolem; + mappings[68] = EntityType.Item; + mappings[69] = EntityType.ItemDisplay; + mappings[70] = EntityType.ItemFrame; + mappings[71] = EntityType.JungleBoat; + mappings[72] = EntityType.JungleChestBoat; + mappings[73] = EntityType.LeashKnot; + mappings[74] = EntityType.LightningBolt; + mappings[75] = EntityType.Llama; + mappings[76] = EntityType.LlamaSpit; + mappings[77] = EntityType.MagmaCube; + mappings[78] = EntityType.MangroveBoat; + mappings[79] = EntityType.MangroveChestBoat; + mappings[80] = EntityType.Marker; + mappings[81] = EntityType.Minecart; + mappings[82] = EntityType.Mooshroom; + mappings[83] = EntityType.Mule; + mappings[84] = EntityType.OakBoat; + mappings[85] = EntityType.OakChestBoat; + mappings[86] = EntityType.Ocelot; + mappings[87] = EntityType.OminousItemSpawner; + mappings[88] = EntityType.Painting; + mappings[89] = EntityType.PaleOakBoat; + mappings[90] = EntityType.PaleOakChestBoat; + mappings[91] = EntityType.Panda; + mappings[92] = EntityType.Parrot; + mappings[93] = EntityType.Phantom; + mappings[94] = EntityType.Pig; + mappings[95] = EntityType.Piglin; + mappings[96] = EntityType.PiglinBrute; + mappings[97] = EntityType.Pillager; + mappings[98] = EntityType.PolarBear; + mappings[99] = EntityType.Potion; + mappings[100] = EntityType.Pufferfish; + mappings[101] = EntityType.Rabbit; + mappings[102] = EntityType.Ravager; + mappings[103] = EntityType.Salmon; + mappings[104] = EntityType.Sheep; + mappings[105] = EntityType.Shulker; + mappings[106] = EntityType.ShulkerBullet; + mappings[107] = EntityType.Silverfish; + mappings[108] = EntityType.Skeleton; + mappings[109] = EntityType.SkeletonHorse; + mappings[110] = EntityType.Slime; + mappings[111] = EntityType.SmallFireball; + mappings[112] = EntityType.Sniffer; + mappings[113] = EntityType.Snowball; + mappings[114] = EntityType.SnowGolem; + mappings[115] = EntityType.SpawnerMinecart; + mappings[116] = EntityType.SpectralArrow; + mappings[117] = EntityType.Spider; + mappings[118] = EntityType.SpruceBoat; + mappings[119] = EntityType.SpruceChestBoat; + mappings[120] = EntityType.Squid; + mappings[121] = EntityType.Stray; + mappings[122] = EntityType.Strider; + mappings[123] = EntityType.Tadpole; + mappings[124] = EntityType.TextDisplay; + mappings[125] = EntityType.Tnt; + mappings[126] = EntityType.TntMinecart; + mappings[127] = EntityType.TraderLlama; + mappings[128] = EntityType.Trident; + mappings[129] = EntityType.TropicalFish; + mappings[130] = EntityType.Turtle; + mappings[131] = EntityType.Vex; + mappings[132] = EntityType.Villager; + mappings[133] = EntityType.Vindicator; + mappings[134] = EntityType.WanderingTrader; + mappings[135] = EntityType.Warden; + mappings[136] = EntityType.WindCharge; + mappings[137] = EntityType.Witch; + mappings[138] = EntityType.Wither; + mappings[139] = EntityType.WitherSkeleton; + mappings[140] = EntityType.WitherSkull; + mappings[141] = EntityType.Wolf; + mappings[142] = EntityType.Zoglin; + mappings[143] = EntityType.Zombie; + mappings[144] = EntityType.ZombieHorse; + mappings[145] = EntityType.ZombieVillager; + mappings[146] = EntityType.ZombifiedPiglin; + mappings[147] = EntityType.Player; + mappings[148] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1215.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1215.cs new file mode 100644 index 00000000..ebfe6ab3 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1215.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1215 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1215() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.Cat; + mappings[21] = EntityType.CaveSpider; + mappings[22] = EntityType.CherryBoat; + mappings[23] = EntityType.CherryChestBoat; + mappings[24] = EntityType.ChestMinecart; + mappings[25] = EntityType.Chicken; + mappings[26] = EntityType.Cod; + mappings[27] = EntityType.CommandBlockMinecart; + mappings[28] = EntityType.Cow; + mappings[29] = EntityType.Creaking; + mappings[30] = EntityType.Creeper; + mappings[31] = EntityType.DarkOakBoat; + mappings[32] = EntityType.DarkOakChestBoat; + mappings[33] = EntityType.Dolphin; + mappings[34] = EntityType.Donkey; + mappings[35] = EntityType.DragonFireball; + mappings[36] = EntityType.Drowned; + mappings[37] = EntityType.Egg; + mappings[38] = EntityType.ElderGuardian; + mappings[39] = EntityType.Enderman; + mappings[40] = EntityType.Endermite; + mappings[41] = EntityType.EnderDragon; + mappings[42] = EntityType.EnderPearl; + mappings[43] = EntityType.EndCrystal; + mappings[44] = EntityType.Evoker; + mappings[45] = EntityType.EvokerFangs; + mappings[46] = EntityType.ExperienceBottle; + mappings[47] = EntityType.ExperienceOrb; + mappings[48] = EntityType.EyeOfEnder; + mappings[49] = EntityType.FallingBlock; + mappings[50] = EntityType.Fireball; + mappings[51] = EntityType.FireworkRocket; + mappings[52] = EntityType.Fox; + mappings[53] = EntityType.Frog; + mappings[54] = EntityType.FurnaceMinecart; + mappings[55] = EntityType.Ghast; + mappings[56] = EntityType.Giant; + mappings[57] = EntityType.GlowItemFrame; + mappings[58] = EntityType.GlowSquid; + mappings[59] = EntityType.Goat; + mappings[60] = EntityType.Guardian; + mappings[61] = EntityType.Hoglin; + mappings[62] = EntityType.HopperMinecart; + mappings[63] = EntityType.Horse; + mappings[64] = EntityType.Husk; + mappings[65] = EntityType.Illusioner; + mappings[66] = EntityType.Interaction; + mappings[67] = EntityType.IronGolem; + mappings[68] = EntityType.Item; + mappings[69] = EntityType.ItemDisplay; + mappings[70] = EntityType.ItemFrame; + mappings[71] = EntityType.JungleBoat; + mappings[72] = EntityType.JungleChestBoat; + mappings[73] = EntityType.LeashKnot; + mappings[74] = EntityType.LightningBolt; + mappings[75] = EntityType.Llama; + mappings[76] = EntityType.LlamaSpit; + mappings[77] = EntityType.MagmaCube; + mappings[78] = EntityType.MangroveBoat; + mappings[79] = EntityType.MangroveChestBoat; + mappings[80] = EntityType.Marker; + mappings[81] = EntityType.Minecart; + mappings[82] = EntityType.Mooshroom; + mappings[83] = EntityType.Mule; + mappings[84] = EntityType.OakBoat; + mappings[85] = EntityType.OakChestBoat; + mappings[86] = EntityType.Ocelot; + mappings[87] = EntityType.OminousItemSpawner; + mappings[88] = EntityType.Painting; + mappings[89] = EntityType.PaleOakBoat; + mappings[90] = EntityType.PaleOakChestBoat; + mappings[91] = EntityType.Panda; + mappings[92] = EntityType.Parrot; + mappings[93] = EntityType.Phantom; + mappings[94] = EntityType.Pig; + mappings[95] = EntityType.Piglin; + mappings[96] = EntityType.PiglinBrute; + mappings[97] = EntityType.Pillager; + mappings[98] = EntityType.PolarBear; + mappings[99] = EntityType.SplashPotion; + mappings[100] = EntityType.LingeringPotion; + mappings[101] = EntityType.Pufferfish; + mappings[102] = EntityType.Rabbit; + mappings[103] = EntityType.Ravager; + mappings[104] = EntityType.Salmon; + mappings[105] = EntityType.Sheep; + mappings[106] = EntityType.Shulker; + mappings[107] = EntityType.ShulkerBullet; + mappings[108] = EntityType.Silverfish; + mappings[109] = EntityType.Skeleton; + mappings[110] = EntityType.SkeletonHorse; + mappings[111] = EntityType.Slime; + mappings[112] = EntityType.SmallFireball; + mappings[113] = EntityType.Sniffer; + mappings[114] = EntityType.Snowball; + mappings[115] = EntityType.SnowGolem; + mappings[116] = EntityType.SpawnerMinecart; + mappings[117] = EntityType.SpectralArrow; + mappings[118] = EntityType.Spider; + mappings[119] = EntityType.SpruceBoat; + mappings[120] = EntityType.SpruceChestBoat; + mappings[121] = EntityType.Squid; + mappings[122] = EntityType.Stray; + mappings[123] = EntityType.Strider; + mappings[124] = EntityType.Tadpole; + mappings[125] = EntityType.TextDisplay; + mappings[126] = EntityType.Tnt; + mappings[127] = EntityType.TntMinecart; + mappings[128] = EntityType.TraderLlama; + mappings[129] = EntityType.Trident; + mappings[130] = EntityType.TropicalFish; + mappings[131] = EntityType.Turtle; + mappings[132] = EntityType.Vex; + mappings[133] = EntityType.Villager; + mappings[134] = EntityType.Vindicator; + mappings[135] = EntityType.WanderingTrader; + mappings[136] = EntityType.Warden; + mappings[137] = EntityType.WindCharge; + mappings[138] = EntityType.Witch; + mappings[139] = EntityType.Wither; + mappings[140] = EntityType.WitherSkeleton; + mappings[141] = EntityType.WitherSkull; + mappings[142] = EntityType.Wolf; + mappings[143] = EntityType.Zoglin; + mappings[144] = EntityType.Zombie; + mappings[145] = EntityType.ZombieHorse; + mappings[146] = EntityType.ZombieVillager; + mappings[147] = EntityType.ZombifiedPiglin; + mappings[148] = EntityType.Player; + mappings[149] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1216.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1216.cs new file mode 100644 index 00000000..c0cb6b76 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1216.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1216 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1216() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.Cat; + mappings[21] = EntityType.CaveSpider; + mappings[22] = EntityType.CherryBoat; + mappings[23] = EntityType.CherryChestBoat; + mappings[24] = EntityType.ChestMinecart; + mappings[25] = EntityType.Chicken; + mappings[26] = EntityType.Cod; + mappings[27] = EntityType.CommandBlockMinecart; + mappings[28] = EntityType.Cow; + mappings[29] = EntityType.Creaking; + mappings[30] = EntityType.Creeper; + mappings[31] = EntityType.DarkOakBoat; + mappings[32] = EntityType.DarkOakChestBoat; + mappings[33] = EntityType.Dolphin; + mappings[34] = EntityType.Donkey; + mappings[35] = EntityType.DragonFireball; + mappings[36] = EntityType.Drowned; + mappings[37] = EntityType.Egg; + mappings[38] = EntityType.ElderGuardian; + mappings[39] = EntityType.Enderman; + mappings[40] = EntityType.Endermite; + mappings[41] = EntityType.EnderDragon; + mappings[42] = EntityType.EnderPearl; + mappings[43] = EntityType.EndCrystal; + mappings[44] = EntityType.Evoker; + mappings[45] = EntityType.EvokerFangs; + mappings[46] = EntityType.ExperienceBottle; + mappings[47] = EntityType.ExperienceOrb; + mappings[48] = EntityType.EyeOfEnder; + mappings[49] = EntityType.FallingBlock; + mappings[50] = EntityType.Fireball; + mappings[51] = EntityType.FireworkRocket; + mappings[52] = EntityType.Fox; + mappings[53] = EntityType.Frog; + mappings[54] = EntityType.FurnaceMinecart; + mappings[55] = EntityType.Ghast; + mappings[56] = EntityType.HappyGhast; + mappings[57] = EntityType.Giant; + mappings[58] = EntityType.GlowItemFrame; + mappings[59] = EntityType.GlowSquid; + mappings[60] = EntityType.Goat; + mappings[61] = EntityType.Guardian; + mappings[62] = EntityType.Hoglin; + mappings[63] = EntityType.HopperMinecart; + mappings[64] = EntityType.Horse; + mappings[65] = EntityType.Husk; + mappings[66] = EntityType.Illusioner; + mappings[67] = EntityType.Interaction; + mappings[68] = EntityType.IronGolem; + mappings[69] = EntityType.Item; + mappings[70] = EntityType.ItemDisplay; + mappings[71] = EntityType.ItemFrame; + mappings[72] = EntityType.JungleBoat; + mappings[73] = EntityType.JungleChestBoat; + mappings[74] = EntityType.LeashKnot; + mappings[75] = EntityType.LightningBolt; + mappings[76] = EntityType.Llama; + mappings[77] = EntityType.LlamaSpit; + mappings[78] = EntityType.MagmaCube; + mappings[79] = EntityType.MangroveBoat; + mappings[80] = EntityType.MangroveChestBoat; + mappings[81] = EntityType.Marker; + mappings[82] = EntityType.Minecart; + mappings[83] = EntityType.Mooshroom; + mappings[84] = EntityType.Mule; + mappings[85] = EntityType.OakBoat; + mappings[86] = EntityType.OakChestBoat; + mappings[87] = EntityType.Ocelot; + mappings[88] = EntityType.OminousItemSpawner; + mappings[89] = EntityType.Painting; + mappings[90] = EntityType.PaleOakBoat; + mappings[91] = EntityType.PaleOakChestBoat; + mappings[92] = EntityType.Panda; + mappings[93] = EntityType.Parrot; + mappings[94] = EntityType.Phantom; + mappings[95] = EntityType.Pig; + mappings[96] = EntityType.Piglin; + mappings[97] = EntityType.PiglinBrute; + mappings[98] = EntityType.Pillager; + mappings[99] = EntityType.PolarBear; + mappings[100] = EntityType.SplashPotion; + mappings[101] = EntityType.LingeringPotion; + mappings[102] = EntityType.Pufferfish; + mappings[103] = EntityType.Rabbit; + mappings[104] = EntityType.Ravager; + mappings[105] = EntityType.Salmon; + mappings[106] = EntityType.Sheep; + mappings[107] = EntityType.Shulker; + mappings[108] = EntityType.ShulkerBullet; + mappings[109] = EntityType.Silverfish; + mappings[110] = EntityType.Skeleton; + mappings[111] = EntityType.SkeletonHorse; + mappings[112] = EntityType.Slime; + mappings[113] = EntityType.SmallFireball; + mappings[114] = EntityType.Sniffer; + mappings[115] = EntityType.Snowball; + mappings[116] = EntityType.SnowGolem; + mappings[117] = EntityType.SpawnerMinecart; + mappings[118] = EntityType.SpectralArrow; + mappings[119] = EntityType.Spider; + mappings[120] = EntityType.SpruceBoat; + mappings[121] = EntityType.SpruceChestBoat; + mappings[122] = EntityType.Squid; + mappings[123] = EntityType.Stray; + mappings[124] = EntityType.Strider; + mappings[125] = EntityType.Tadpole; + mappings[126] = EntityType.TextDisplay; + mappings[127] = EntityType.Tnt; + mappings[128] = EntityType.TntMinecart; + mappings[129] = EntityType.TraderLlama; + mappings[130] = EntityType.Trident; + mappings[131] = EntityType.TropicalFish; + mappings[132] = EntityType.Turtle; + mappings[133] = EntityType.Vex; + mappings[134] = EntityType.Villager; + mappings[135] = EntityType.Vindicator; + mappings[136] = EntityType.WanderingTrader; + mappings[137] = EntityType.Warden; + mappings[138] = EntityType.WindCharge; + mappings[139] = EntityType.Witch; + mappings[140] = EntityType.Wither; + mappings[141] = EntityType.WitherSkeleton; + mappings[142] = EntityType.WitherSkull; + mappings[143] = EntityType.Wolf; + mappings[144] = EntityType.Zoglin; + mappings[145] = EntityType.Zombie; + mappings[146] = EntityType.ZombieHorse; + mappings[147] = EntityType.ZombieVillager; + mappings[148] = EntityType.ZombifiedPiglin; + mappings[149] = EntityType.Player; + mappings[150] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1219.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1219.cs new file mode 100644 index 00000000..448bdbcc --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1219.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1219 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1219() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.Cat; + mappings[21] = EntityType.CaveSpider; + mappings[22] = EntityType.CherryBoat; + mappings[23] = EntityType.CherryChestBoat; + mappings[24] = EntityType.ChestMinecart; + mappings[25] = EntityType.Chicken; + mappings[26] = EntityType.Cod; + mappings[27] = EntityType.CopperGolem; + mappings[28] = EntityType.CommandBlockMinecart; + mappings[29] = EntityType.Cow; + mappings[30] = EntityType.Creaking; + mappings[31] = EntityType.Creeper; + mappings[32] = EntityType.DarkOakBoat; + mappings[33] = EntityType.DarkOakChestBoat; + mappings[34] = EntityType.Dolphin; + mappings[35] = EntityType.Donkey; + mappings[36] = EntityType.DragonFireball; + mappings[37] = EntityType.Drowned; + mappings[38] = EntityType.Egg; + mappings[39] = EntityType.ElderGuardian; + mappings[40] = EntityType.Enderman; + mappings[41] = EntityType.Endermite; + mappings[42] = EntityType.EnderDragon; + mappings[43] = EntityType.EnderPearl; + mappings[44] = EntityType.EndCrystal; + mappings[45] = EntityType.Evoker; + mappings[46] = EntityType.EvokerFangs; + mappings[47] = EntityType.ExperienceBottle; + mappings[48] = EntityType.ExperienceOrb; + mappings[49] = EntityType.EyeOfEnder; + mappings[50] = EntityType.FallingBlock; + mappings[51] = EntityType.Fireball; + mappings[52] = EntityType.FireworkRocket; + mappings[53] = EntityType.Fox; + mappings[54] = EntityType.Frog; + mappings[55] = EntityType.FurnaceMinecart; + mappings[56] = EntityType.Ghast; + mappings[57] = EntityType.HappyGhast; + mappings[58] = EntityType.Giant; + mappings[59] = EntityType.GlowItemFrame; + mappings[60] = EntityType.GlowSquid; + mappings[61] = EntityType.Goat; + mappings[62] = EntityType.Guardian; + mappings[63] = EntityType.Hoglin; + mappings[64] = EntityType.HopperMinecart; + mappings[65] = EntityType.Horse; + mappings[66] = EntityType.Husk; + mappings[67] = EntityType.Illusioner; + mappings[68] = EntityType.Interaction; + mappings[69] = EntityType.IronGolem; + mappings[70] = EntityType.Item; + mappings[71] = EntityType.ItemDisplay; + mappings[72] = EntityType.ItemFrame; + mappings[73] = EntityType.JungleBoat; + mappings[74] = EntityType.JungleChestBoat; + mappings[75] = EntityType.LeashKnot; + mappings[76] = EntityType.LightningBolt; + mappings[77] = EntityType.Llama; + mappings[78] = EntityType.LlamaSpit; + mappings[79] = EntityType.MagmaCube; + mappings[80] = EntityType.MangroveBoat; + mappings[81] = EntityType.MangroveChestBoat; + mappings[82] = EntityType.Mannequin; + mappings[83] = EntityType.Marker; + mappings[84] = EntityType.Minecart; + mappings[85] = EntityType.Mooshroom; + mappings[86] = EntityType.Mule; + mappings[87] = EntityType.OakBoat; + mappings[88] = EntityType.OakChestBoat; + mappings[89] = EntityType.Ocelot; + mappings[90] = EntityType.OminousItemSpawner; + mappings[91] = EntityType.Painting; + mappings[92] = EntityType.PaleOakBoat; + mappings[93] = EntityType.PaleOakChestBoat; + mappings[94] = EntityType.Panda; + mappings[95] = EntityType.Parrot; + mappings[96] = EntityType.Phantom; + mappings[97] = EntityType.Pig; + mappings[98] = EntityType.Piglin; + mappings[99] = EntityType.PiglinBrute; + mappings[100] = EntityType.Pillager; + mappings[101] = EntityType.PolarBear; + mappings[102] = EntityType.SplashPotion; + mappings[103] = EntityType.LingeringPotion; + mappings[104] = EntityType.Pufferfish; + mappings[105] = EntityType.Rabbit; + mappings[106] = EntityType.Ravager; + mappings[107] = EntityType.Salmon; + mappings[108] = EntityType.Sheep; + mappings[109] = EntityType.Shulker; + mappings[110] = EntityType.ShulkerBullet; + mappings[111] = EntityType.Silverfish; + mappings[112] = EntityType.Skeleton; + mappings[113] = EntityType.SkeletonHorse; + mappings[114] = EntityType.Slime; + mappings[115] = EntityType.SmallFireball; + mappings[116] = EntityType.Sniffer; + mappings[117] = EntityType.Snowball; + mappings[118] = EntityType.SnowGolem; + mappings[119] = EntityType.SpawnerMinecart; + mappings[120] = EntityType.SpectralArrow; + mappings[121] = EntityType.Spider; + mappings[122] = EntityType.SpruceBoat; + mappings[123] = EntityType.SpruceChestBoat; + mappings[124] = EntityType.Squid; + mappings[125] = EntityType.Stray; + mappings[126] = EntityType.Strider; + mappings[127] = EntityType.Tadpole; + mappings[128] = EntityType.TextDisplay; + mappings[129] = EntityType.Tnt; + mappings[130] = EntityType.TntMinecart; + mappings[131] = EntityType.TraderLlama; + mappings[132] = EntityType.Trident; + mappings[133] = EntityType.TropicalFish; + mappings[134] = EntityType.Turtle; + mappings[135] = EntityType.Vex; + mappings[136] = EntityType.Villager; + mappings[137] = EntityType.Vindicator; + mappings[138] = EntityType.WanderingTrader; + mappings[139] = EntityType.Warden; + mappings[140] = EntityType.WindCharge; + mappings[141] = EntityType.Witch; + mappings[142] = EntityType.Wither; + mappings[143] = EntityType.WitherSkeleton; + mappings[144] = EntityType.WitherSkull; + mappings[145] = EntityType.Wolf; + mappings[146] = EntityType.Zoglin; + mappings[147] = EntityType.Zombie; + mappings[148] = EntityType.ZombieHorse; + mappings[149] = EntityType.ZombieVillager; + mappings[150] = EntityType.ZombifiedPiglin; + mappings[151] = EntityType.Player; + mappings[152] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette18.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette18.cs index 2ff09ace..d85ed48a 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette18.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette18.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Mapping.EntityPalettes /// public class EntityPalette18 : EntityPalette { - private static Dictionary mappingsObjects = new Dictionary() + private static Dictionary mappingsObjects = new() { // https://wiki.vg/Entity_metadata#Objects { 1, EntityType.Boat }, @@ -39,7 +39,7 @@ namespace MinecraftClient.Mapping.EntityPalettes { 93, EntityType.DragonFireball }, }; - private static Dictionary mappingsMobs = new Dictionary() { + private static Dictionary mappingsMobs = new() { { 1, EntityType.Item }, { 2, EntityType.ExperienceOrb }, { 8, EntityType.LeashKnot }, diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette261.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette261.cs new file mode 100644 index 00000000..a33d0b3b --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette261.cs @@ -0,0 +1,175 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette261 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette261() + { + mappings[0] = EntityType.AcaciaBoat; + mappings[1] = EntityType.AcaciaChestBoat; + mappings[2] = EntityType.Allay; + mappings[3] = EntityType.AreaEffectCloud; + mappings[4] = EntityType.Armadillo; + mappings[5] = EntityType.ArmorStand; + mappings[6] = EntityType.Arrow; + mappings[7] = EntityType.Axolotl; + mappings[8] = EntityType.BambooChestRaft; + mappings[9] = EntityType.BambooRaft; + mappings[10] = EntityType.Bat; + mappings[11] = EntityType.Bee; + mappings[12] = EntityType.BirchBoat; + mappings[13] = EntityType.BirchChestBoat; + mappings[14] = EntityType.Blaze; + mappings[15] = EntityType.BlockDisplay; + mappings[16] = EntityType.Bogged; + mappings[17] = EntityType.Breeze; + mappings[18] = EntityType.BreezeWindCharge; + mappings[19] = EntityType.Camel; + mappings[20] = EntityType.CamelHusk; + mappings[21] = EntityType.Cat; + mappings[22] = EntityType.CaveSpider; + mappings[23] = EntityType.CherryBoat; + mappings[24] = EntityType.CherryChestBoat; + mappings[25] = EntityType.ChestMinecart; + mappings[26] = EntityType.Chicken; + mappings[27] = EntityType.Cod; + mappings[28] = EntityType.CopperGolem; + mappings[29] = EntityType.CommandBlockMinecart; + mappings[30] = EntityType.Cow; + mappings[31] = EntityType.Creaking; + mappings[32] = EntityType.Creeper; + mappings[33] = EntityType.DarkOakBoat; + mappings[34] = EntityType.DarkOakChestBoat; + mappings[35] = EntityType.Dolphin; + mappings[36] = EntityType.Donkey; + mappings[37] = EntityType.DragonFireball; + mappings[38] = EntityType.Drowned; + mappings[39] = EntityType.Egg; + mappings[40] = EntityType.ElderGuardian; + mappings[41] = EntityType.Enderman; + mappings[42] = EntityType.Endermite; + mappings[43] = EntityType.EnderDragon; + mappings[44] = EntityType.EnderPearl; + mappings[45] = EntityType.EndCrystal; + mappings[46] = EntityType.Evoker; + mappings[47] = EntityType.EvokerFangs; + mappings[48] = EntityType.ExperienceBottle; + mappings[49] = EntityType.ExperienceOrb; + mappings[50] = EntityType.EyeOfEnder; + mappings[51] = EntityType.FallingBlock; + mappings[52] = EntityType.Fireball; + mappings[53] = EntityType.FireworkRocket; + mappings[54] = EntityType.Fox; + mappings[55] = EntityType.Frog; + mappings[56] = EntityType.FurnaceMinecart; + mappings[57] = EntityType.Ghast; + mappings[58] = EntityType.HappyGhast; + mappings[59] = EntityType.Giant; + mappings[60] = EntityType.GlowItemFrame; + mappings[61] = EntityType.GlowSquid; + mappings[62] = EntityType.Goat; + mappings[63] = EntityType.Guardian; + mappings[64] = EntityType.Hoglin; + mappings[65] = EntityType.HopperMinecart; + mappings[66] = EntityType.Horse; + mappings[67] = EntityType.Husk; + mappings[68] = EntityType.Illusioner; + mappings[69] = EntityType.Interaction; + mappings[70] = EntityType.IronGolem; + mappings[71] = EntityType.Item; + mappings[72] = EntityType.ItemDisplay; + mappings[73] = EntityType.ItemFrame; + mappings[74] = EntityType.JungleBoat; + mappings[75] = EntityType.JungleChestBoat; + mappings[76] = EntityType.LeashKnot; + mappings[77] = EntityType.LightningBolt; + mappings[78] = EntityType.Llama; + mappings[79] = EntityType.LlamaSpit; + mappings[80] = EntityType.MagmaCube; + mappings[81] = EntityType.MangroveBoat; + mappings[82] = EntityType.MangroveChestBoat; + mappings[83] = EntityType.Mannequin; + mappings[84] = EntityType.Marker; + mappings[85] = EntityType.Minecart; + mappings[86] = EntityType.Mooshroom; + mappings[87] = EntityType.Mule; + mappings[88] = EntityType.Nautilus; + mappings[89] = EntityType.OakBoat; + mappings[90] = EntityType.OakChestBoat; + mappings[91] = EntityType.Ocelot; + mappings[92] = EntityType.OminousItemSpawner; + mappings[93] = EntityType.Painting; + mappings[94] = EntityType.PaleOakBoat; + mappings[95] = EntityType.PaleOakChestBoat; + mappings[96] = EntityType.Panda; + mappings[97] = EntityType.Parched; + mappings[98] = EntityType.Parrot; + mappings[99] = EntityType.Phantom; + mappings[100] = EntityType.Pig; + mappings[101] = EntityType.Piglin; + mappings[102] = EntityType.PiglinBrute; + mappings[103] = EntityType.Pillager; + mappings[104] = EntityType.PolarBear; + mappings[105] = EntityType.SplashPotion; + mappings[106] = EntityType.LingeringPotion; + mappings[107] = EntityType.Pufferfish; + mappings[108] = EntityType.Rabbit; + mappings[109] = EntityType.Ravager; + mappings[110] = EntityType.Salmon; + mappings[111] = EntityType.Sheep; + mappings[112] = EntityType.Shulker; + mappings[113] = EntityType.ShulkerBullet; + mappings[114] = EntityType.Silverfish; + mappings[115] = EntityType.Skeleton; + mappings[116] = EntityType.SkeletonHorse; + mappings[117] = EntityType.Slime; + mappings[118] = EntityType.SmallFireball; + mappings[119] = EntityType.Sniffer; + mappings[120] = EntityType.Snowball; + mappings[121] = EntityType.SnowGolem; + mappings[122] = EntityType.SpawnerMinecart; + mappings[123] = EntityType.SpectralArrow; + mappings[124] = EntityType.Spider; + mappings[125] = EntityType.SpruceBoat; + mappings[126] = EntityType.SpruceChestBoat; + mappings[127] = EntityType.Squid; + mappings[128] = EntityType.Stray; + mappings[129] = EntityType.Strider; + mappings[130] = EntityType.Tadpole; + mappings[131] = EntityType.TextDisplay; + mappings[132] = EntityType.Tnt; + mappings[133] = EntityType.TntMinecart; + mappings[134] = EntityType.TraderLlama; + mappings[135] = EntityType.Trident; + mappings[136] = EntityType.TropicalFish; + mappings[137] = EntityType.Turtle; + mappings[138] = EntityType.Vex; + mappings[139] = EntityType.Villager; + mappings[140] = EntityType.Vindicator; + mappings[141] = EntityType.WanderingTrader; + mappings[142] = EntityType.Warden; + mappings[143] = EntityType.WindCharge; + mappings[144] = EntityType.Witch; + mappings[145] = EntityType.Wither; + mappings[146] = EntityType.WitherSkeleton; + mappings[147] = EntityType.WitherSkull; + mappings[148] = EntityType.Wolf; + mappings[149] = EntityType.Zoglin; + mappings[150] = EntityType.Zombie; + mappings[151] = EntityType.ZombieHorse; + mappings[152] = EntityType.ZombieNautilus; + mappings[153] = EntityType.ZombieVillager; + mappings[154] = EntityType.ZombifiedPiglin; + mappings[155] = EntityType.Player; + mappings[156] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityType.cs b/MinecraftClient/Mapping/EntityType.cs index 9a036bac..7c4748fd 100644 --- a/MinecraftClient/Mapping/EntityType.cs +++ b/MinecraftClient/Mapping/EntityType.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Mapping { /// /// Represents Minecraft Entity Types @@ -14,27 +14,44 @@ /// public enum EntityType { + AcaciaBoat, + AcaciaChestBoat, Allay, AreaEffectCloud, + Armadillo, ArmorStand, Arrow, Axolotl, + BambooChestRaft, + BambooRaft, Bat, Bee, + BirchBoat, + BirchChestBoat, Blaze, BlockDisplay, Boat, + Bogged, Breeze, + BreezeWindCharge, Camel, + CamelHusk, Cat, CaveSpider, + CherryBoat, + CherryChestBoat, ChestBoat, ChestMinecart, Chicken, Cod, CommandBlockMinecart, + CopperGolem, Cow, + Creaking, + CreakingTransient, Creeper, + DarkOakBoat, + DarkOakChestBoat, Dolphin, Donkey, DragonFireball, @@ -64,6 +81,7 @@ GlowSquid, Goat, Guardian, + HappyGhast, Hoglin, HopperMinecart, Horse, @@ -74,18 +92,30 @@ Item, ItemDisplay, ItemFrame, + JungleBoat, + JungleChestBoat, LeashKnot, LightningBolt, Llama, LlamaSpit, MagmaCube, + MangroveBoat, + MangroveChestBoat, + Mannequin, Marker, Minecart, Mooshroom, Mule, + Nautilus, + OakBoat, + OakChestBoat, Ocelot, + OminousItemSpawner, Painting, + PaleOakBoat, + PaleOakChestBoat, Panda, + Parched, Parrot, Phantom, Pig, @@ -94,6 +124,8 @@ Pillager, Player, PolarBear, + SplashPotion, + LingeringPotion, Potion, Pufferfish, Rabbit, @@ -113,6 +145,8 @@ SpawnerMinecart, SpectralArrow, Spider, + SpruceBoat, + SpruceChestBoat, Squid, Stray, Strider, @@ -138,6 +172,7 @@ Zoglin, Zombie, ZombieHorse, + ZombieNautilus, ZombieVillager, ZombifiedPiglin, } diff --git a/MinecraftClient/Mapping/EntityTypeExtensions.cs b/MinecraftClient/Mapping/EntityTypeExtensions.cs index e6546d22..0e7a85fb 100644 --- a/MinecraftClient/Mapping/EntityTypeExtensions.cs +++ b/MinecraftClient/Mapping/EntityTypeExtensions.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Mapping { public static class EntityTypeExtensions { @@ -110,12 +110,15 @@ case EntityType.Egg: case EntityType.EnderPearl: case EntityType.Potion: + case EntityType.SplashPotion: + case EntityType.LingeringPotion: case EntityType.Fireball: case EntityType.FireworkRocket: return true; default: return false; - }; + } + ; } } } diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs index 4a6a7320..abfda749 100644 --- a/MinecraftClient/Mapping/Location.cs +++ b/MinecraftClient/Mapping/Location.cs @@ -108,7 +108,7 @@ namespace MinecraftClient.Mapping public static Location Parse(string x, string y, string z) { Location.TryParse(x, y, z, out Location? res); - if (res == null) + if (res is null) throw new FormatException(); else return (Location)res; @@ -116,7 +116,7 @@ namespace MinecraftClient.Mapping public static bool TryParse(string x, string y, string z, out Location? location) { - string[] coord_str = new string[] { x.Trim(), y.Trim(), z.Trim() }; + string[] coord_str = [x.Trim(), y.Trim(), z.Trim()]; double[] coord_res = new double[3]; for (int i = 0; i < 3; ++i) @@ -144,7 +144,7 @@ namespace MinecraftClient.Mapping public static Location Parse(Location current, string x, string y, string z) { Location.TryParse(current, x, y, z, out Location? res); - if (res == null) + if (res is null) throw new FormatException(); else return (Location)res; @@ -152,9 +152,9 @@ namespace MinecraftClient.Mapping public static bool TryParse(Location current, string x, string y, string z, out Location? location) { - string[] coord_str = new string[] { x.Trim(), y.Trim(), z.Trim() }; + string[] coord_str = [x.Trim(), y.Trim(), z.Trim()]; double[] coord_res = new double[3]; - double[] coord_cur = new double[] { current.X, current.Y, current.Z }; + double[] coord_cur = [current.X, current.Y, current.Z]; for (int i = 0; i < 3; ++i) { @@ -308,7 +308,7 @@ namespace MinecraftClient.Mapping /// TRUE if the locations are equals public override bool Equals(object? obj) { - if (obj == null) + if (obj is null) return false; if (obj is Location location) { diff --git a/MinecraftClient/Mapping/MapIcon.cs b/MinecraftClient/Mapping/MapIcon.cs index 3862b8db..e525f1a9 100644 --- a/MinecraftClient/Mapping/MapIcon.cs +++ b/MinecraftClient/Mapping/MapIcon.cs @@ -1,11 +1,11 @@ namespace MinecraftClient.Mapping { - public class MapIcon + public record MapIcon { - public MapIconType Type { set; get; } - public byte X { set; get; } - public byte Z { set; get; } - public byte Direction { set; get; } - public string? DisplayName { set; get; } = null; + public MapIconType Type { get; set; } + public byte X { get; set; } + public byte Z { get; set; } + public byte Direction { get; set; } + public string? DisplayName { get; set; } = null; } } diff --git a/MinecraftClient/Mapping/Material.cs b/MinecraftClient/Mapping/Material.cs index f5cbc9aa..75fb26d0 100644 --- a/MinecraftClient/Mapping/Material.cs +++ b/MinecraftClient/Mapping/Material.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Mapping { /// /// Represents Minecraft Materials @@ -24,6 +24,7 @@ AcaciaPlanks, AcaciaPressurePlate, AcaciaSapling, + AcaciaShelf, AcaciaSign, AcaciaSlab, AcaciaStairs, @@ -60,6 +61,7 @@ BambooPlanks, BambooPressurePlate, BambooSapling, + BambooShelf, BambooSign, BambooSlab, BambooStairs, @@ -87,6 +89,7 @@ BirchPlanks, BirchPressurePlate, BirchSapling, + BirchShelf, BirchSign, BirchSlab, BirchStairs, @@ -162,7 +165,9 @@ BubbleCoralFan, BubbleCoralWallFan, BuddingAmethyst, + Bush, Cactus, + CactusFlower, Cake, Calcite, CalibratedSculkSensor, @@ -188,6 +193,7 @@ CherryPlanks, CherryPressurePlate, CherrySapling, + CherryShelf, CherrySign, CherrySlab, CherryStairs, @@ -204,6 +210,7 @@ ChiseledPolishedBlackstone, ChiseledQuartzBlock, ChiseledRedSandstone, + ChiseledResinBricks, ChiseledSandstone, ChiseledStoneBricks, ChiseledTuff, @@ -211,6 +218,7 @@ ChorusFlower, ChorusPlant, Clay, + ClosedEyeblossom, CoalBlock, CoalOre, CoarseDirt, @@ -228,12 +236,19 @@ Comparator, Composter, Conduit, + CopperBars, CopperBlock, CopperBulb, + CopperChain, + CopperChest, CopperDoor, + CopperGolemStatue, CopperGrate, + CopperLantern, CopperOre, + CopperTorch, CopperTrapdoor, + CopperWallTorch, Cornflower, CrackedDeepslateBricks, CrackedDeepslateTiles, @@ -242,6 +257,7 @@ CrackedStoneBricks, Crafter, CraftingTable, + CreakingHeart, CreeperHead, CreeperWallHead, CrimsonButton, @@ -255,6 +271,7 @@ CrimsonPlanks, CrimsonPressurePlate, CrimsonRoots, + CrimsonShelf, CrimsonSign, CrimsonSlab, CrimsonStairs, @@ -296,6 +313,7 @@ DarkOakPlanks, DarkOakPressurePlate, DarkOakSapling, + DarkOakShelf, DarkOakSign, DarkOakSlab, DarkOakStairs, @@ -359,6 +377,7 @@ DragonEgg, DragonHead, DragonWallHead, + DriedGhast, DriedKelpBlock, DripstoneBlock, Dropper, @@ -377,13 +396,19 @@ EnderChest, ExposedChiseledCopper, ExposedCopper, + ExposedCopperBars, ExposedCopperBulb, + ExposedCopperChain, + ExposedCopperChest, ExposedCopperDoor, + ExposedCopperGolemStatue, ExposedCopperGrate, + ExposedCopperLantern, ExposedCopperTrapdoor, ExposedCutCopper, ExposedCutCopperSlab, ExposedCutCopperStairs, + ExposedLightningRod, Farmland, Fern, Fire, @@ -391,6 +416,7 @@ FireCoralBlock, FireCoralFan, FireCoralWallFan, + FireflyBush, FletchingTable, FlowerPot, FloweringAzalea, @@ -405,11 +431,11 @@ Glowstone, GoldBlock, GoldOre, + GoldenDandelion, Granite, GraniteSlab, GraniteStairs, GraniteWall, - Grass, // 1.20.3+ renamed to ShortGrass GrassBlock, Gravel, GrayBanner, @@ -443,6 +469,7 @@ Grindstone, HangingRoots, HayBlock, + HeavyCore, HeavyWeightedPressurePlate, HoneyBlock, HoneycombBlock, @@ -461,6 +488,7 @@ InfestedStoneBricks, IronBars, IronBlock, + IronChain, IronDoor, IronOre, IronTrapdoor, @@ -477,6 +505,7 @@ JunglePlanks, JunglePressurePlate, JungleSapling, + JungleShelf, JungleSign, JungleSlab, JungleStairs, @@ -494,6 +523,7 @@ LargeFern, Lava, LavaCauldron, + LeafLitter, Lectern, Lever, Light, @@ -572,6 +602,7 @@ MangrovePressurePlate, MangrovePropagule, MangroveRoots, + MangroveShelf, MangroveSign, MangroveSlab, MangroveStairs, @@ -625,6 +656,7 @@ OakPlanks, OakPressurePlate, OakSapling, + OakShelf, OakSign, OakSlab, OakStairs, @@ -635,6 +667,7 @@ Observer, Obsidian, OchreFroglight, + OpenEyeblossom, OrangeBanner, OrangeBed, OrangeCandle, @@ -653,15 +686,42 @@ OxeyeDaisy, OxidizedChiseledCopper, OxidizedCopper, + OxidizedCopperBars, OxidizedCopperBulb, + OxidizedCopperChain, + OxidizedCopperChest, OxidizedCopperDoor, + OxidizedCopperGolemStatue, OxidizedCopperGrate, + OxidizedCopperLantern, OxidizedCopperTrapdoor, OxidizedCutCopper, OxidizedCutCopperSlab, OxidizedCutCopperStairs, + OxidizedLightningRod, PackedIce, PackedMud, + PaleHangingMoss, + PaleMossBlock, + PaleMossCarpet, + PaleOakButton, + PaleOakDoor, + PaleOakFence, + PaleOakFenceGate, + PaleOakHangingSign, + PaleOakLeaves, + PaleOakLog, + PaleOakPlanks, + PaleOakPressurePlate, + PaleOakSapling, + PaleOakShelf, + PaleOakSign, + PaleOakSlab, + PaleOakStairs, + PaleOakTrapdoor, + PaleOakWallHangingSign, + PaleOakWallSign, + PaleOakWood, PearlescentFroglight, Peony, PetrifiedOakSlab, @@ -731,6 +791,7 @@ PottedBrownMushroom, PottedCactus, PottedCherrySapling, + PottedClosedEyeblossom, PottedCornflower, PottedCrimsonFungus, PottedCrimsonRoots, @@ -739,12 +800,15 @@ PottedDeadBush, PottedFern, PottedFloweringAzaleaBush, + PottedGoldenDandelion, PottedJungleSapling, PottedLilyOfTheValley, PottedMangrovePropagule, PottedOakSapling, + PottedOpenEyeblossom, PottedOrangeTulip, PottedOxeyeDaisy, + PottedPaleOakSapling, PottedPinkTulip, PottedPoppy, PottedRedMushroom, @@ -829,6 +893,12 @@ ReinforcedDeepslate, Repeater, RepeatingCommandBlock, + ResinBlock, + ResinBrickSlab, + ResinBrickStairs, + ResinBrickWall, + ResinBricks, + ResinClump, RespawnAnchor, RootedDirt, RoseBush, @@ -846,6 +916,7 @@ SeaLantern, SeaPickle, Seagrass, + ShortDryGrass, ShortGrass, Shroomlight, ShulkerBox, @@ -891,6 +962,7 @@ SprucePlanks, SprucePressurePlate, SpruceSapling, + SpruceShelf, SpruceSign, SpruceSlab, SpruceStairs, @@ -926,6 +998,8 @@ StrippedMangroveWood, StrippedOakLog, StrippedOakWood, + StrippedPaleOakLog, + StrippedPaleOakWood, StrippedSpruceLog, StrippedSpruceWood, StrippedWarpedHyphae, @@ -937,10 +1011,13 @@ SuspiciousGravel, SuspiciousSand, SweetBerryBush, + TallDryGrass, TallGrass, TallSeagrass, Target, Terracotta, + TestBlock, + TestInstanceBlock, TintedGlass, Tnt, Torch, @@ -965,6 +1042,7 @@ TurtleEgg, TwistingVines, TwistingVinesPlant, + Vault, VerdantFroglight, Vine, VoidAir, @@ -980,6 +1058,7 @@ WarpedPlanks, WarpedPressurePlate, WarpedRoots, + WarpedShelf, WarpedSign, WarpedSlab, WarpedStairs, @@ -991,50 +1070,80 @@ Water, WaterCauldron, WaxedChiseledCopper, + WaxedCopperBars, WaxedCopperBlock, WaxedCopperBulb, + WaxedCopperChain, + WaxedCopperChest, WaxedCopperDoor, + WaxedCopperGolemStatue, WaxedCopperGrate, + WaxedCopperLantern, WaxedCopperTrapdoor, WaxedCutCopper, WaxedCutCopperSlab, WaxedCutCopperStairs, WaxedExposedChiseledCopper, WaxedExposedCopper, + WaxedExposedCopperBars, WaxedExposedCopperBulb, + WaxedExposedCopperChain, + WaxedExposedCopperChest, WaxedExposedCopperDoor, + WaxedExposedCopperGolemStatue, WaxedExposedCopperGrate, + WaxedExposedCopperLantern, WaxedExposedCopperTrapdoor, WaxedExposedCutCopper, WaxedExposedCutCopperSlab, WaxedExposedCutCopperStairs, + WaxedExposedLightningRod, + WaxedLightningRod, WaxedOxidizedChiseledCopper, WaxedOxidizedCopper, + WaxedOxidizedCopperBars, WaxedOxidizedCopperBulb, + WaxedOxidizedCopperChain, + WaxedOxidizedCopperChest, WaxedOxidizedCopperDoor, + WaxedOxidizedCopperGolemStatue, WaxedOxidizedCopperGrate, + WaxedOxidizedCopperLantern, WaxedOxidizedCopperTrapdoor, WaxedOxidizedCutCopper, WaxedOxidizedCutCopperSlab, WaxedOxidizedCutCopperStairs, + WaxedOxidizedLightningRod, WaxedWeatheredChiseledCopper, WaxedWeatheredCopper, + WaxedWeatheredCopperBars, WaxedWeatheredCopperBulb, + WaxedWeatheredCopperChain, + WaxedWeatheredCopperChest, WaxedWeatheredCopperDoor, + WaxedWeatheredCopperGolemStatue, WaxedWeatheredCopperGrate, + WaxedWeatheredCopperLantern, WaxedWeatheredCopperTrapdoor, WaxedWeatheredCutCopper, WaxedWeatheredCutCopperSlab, WaxedWeatheredCutCopperStairs, + WaxedWeatheredLightningRod, WeatheredChiseledCopper, WeatheredCopper, + WeatheredCopperBars, WeatheredCopperBulb, + WeatheredCopperChain, + WeatheredCopperChest, WeatheredCopperDoor, + WeatheredCopperGolemStatue, WeatheredCopperGrate, + WeatheredCopperLantern, WeatheredCopperTrapdoor, WeatheredCutCopper, WeatheredCutCopperSlab, WeatheredCutCopperStairs, + WeatheredLightningRod, WeepingVines, WeepingVinesPlant, WetSponge, @@ -1054,6 +1163,7 @@ WhiteTulip, WhiteWallBanner, WhiteWool, + Wildflowers, WitherRose, WitherSkeletonSkull, WitherSkeletonWallSkull, @@ -1074,4 +1184,4 @@ ZombieHead, ZombieWallHead, } -} +} \ No newline at end of file diff --git a/MinecraftClient/Mapping/Material2Tool.cs b/MinecraftClient/Mapping/Material2Tool.cs index 34c09c6c..65992480 100644 --- a/MinecraftClient/Mapping/Material2Tool.cs +++ b/MinecraftClient/Mapping/Material2Tool.cs @@ -365,7 +365,7 @@ namespace MinecraftClient.Mapping Material.CyanConcretePowder, Material.Dirt, Material.Farmland, - Material.Grass, + Material.ShortGrass, Material.GrassBlock, Material.DirtPath, Material.Gravel, @@ -374,6 +374,7 @@ namespace MinecraftClient.Mapping Material.LightBlueConcretePowder, Material.LightGrayConcretePowder, Material.LimeConcretePowder, + Material.TallGrass, Material.MagentaConcretePowder, Material.Mycelium, Material.OrangeConcretePowder, diff --git a/MinecraftClient/Mapping/MaterialExtensions.cs b/MinecraftClient/Mapping/MaterialExtensions.cs index 2e62d695..5d2fe06d 100644 --- a/MinecraftClient/Mapping/MaterialExtensions.cs +++ b/MinecraftClient/Mapping/MaterialExtensions.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Mapping { /// /// Defines extension methods for the Material enumeration @@ -122,6 +122,7 @@ case Material.ChiseledPolishedBlackstone: case Material.ChiseledQuartzBlock: case Material.ChiseledRedSandstone: + case Material.ChiseledResinBricks: case Material.ChiseledSandstone: case Material.ChiseledStoneBricks: case Material.ChorusFlower: @@ -500,6 +501,8 @@ case Material.PottedBlueOrchid: case Material.PottedBrownMushroom: case Material.PottedCactus: + case Material.PottedCherrySapling: + case Material.PottedClosedEyeblossom: case Material.PottedCornflower: case Material.PottedDandelion: case Material.PottedDarkOakSapling: @@ -509,6 +512,7 @@ case Material.PottedJungleSapling: case Material.PottedLilyOfTheValley: case Material.PottedOakSapling: + case Material.PottedOpenEyeblossom: case Material.PottedOrangeTulip: case Material.PottedOxeyeDaisy: case Material.PottedPinkTulip: @@ -576,6 +580,10 @@ case Material.RedWool: case Material.ReinforcedDeepslate: case Material.RepeatingCommandBlock: + case Material.ResinBlock: + case Material.ResinBrickStairs: + case Material.ResinBrickWall: + case Material.ResinBricks: case Material.RespawnAnchor: case Material.RootedDirt: case Material.Sand: @@ -868,6 +876,7 @@ case Material.QuartzSlab: case Material.RedNetherBrickSlab: case Material.RedSandstoneSlab: + case Material.ResinBrickSlab: case Material.SandstoneSlab: case Material.SmoothQuartzSlab: case Material.SmoothRedSandstoneSlab: diff --git a/MinecraftClient/Mapping/MiningCalculator.cs b/MinecraftClient/Mapping/MiningCalculator.cs new file mode 100644 index 00000000..458057f2 --- /dev/null +++ b/MinecraftClient/Mapping/MiningCalculator.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using MinecraftClient.Inventory; +using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +namespace MinecraftClient.Mapping +{ + /// + /// Computes dig duration in ticks for survival-style block breaking. + /// Version-aware across 1.8-1.21.11+, using tool speed, enchantments, effects, and attributes. + /// + public static class MiningCalculator + { + public sealed class MiningOptions + { + public static readonly MiningOptions Vanilla = new(); + + public bool ApplyEfficiencyEnchantments { get; init; } = true; + + public bool ApplyHasteEffects { get; init; } = true; + } + + /// + /// Compute the number of ticks required to break a block in survival mode. + /// Returns 0 for instant-break blocks, -1 for unbreakable blocks. + /// + /// The block material to break + /// The item in the player's main hand (null for empty hand) + /// The item in the player's helmet slot (null if empty, used for Aqua Affinity) + /// Currently active player effects + /// Cached player attribute values (from OnEntityProperties) + /// Whether the player's eyes are submerged in water + /// Whether the player is on the ground + /// The Minecraft protocol version + /// Ticks to break the block, 0 for instant, -1 for unbreakable + public static int ComputeDigTicks( + Material blockMaterial, + Item? heldItem, + Item? helmetItem, + Dictionary effects, + Dictionary playerAttributes, + bool isUnderwater, + bool isOnGround, + int protocolVersion, + MiningOptions? options = null) + { + options ??= MiningOptions.Vanilla; + float hardness = BlockHardness.GetHardness(blockMaterial); + + if (hardness < 0) + return -1; // Unbreakable + + if (hardness == 0) + return 0; // Instant break + + float destroySpeed = GetDestroySpeed( + blockMaterial, heldItem, helmetItem, effects, playerAttributes, + isUnderwater, isOnGround, protocolVersion, options); + + bool correctTool = HasCorrectToolForDrops(blockMaterial, heldItem, protocolVersion); + int divisor = correctTool ? 30 : 100; + + float destroyProgress = destroySpeed / hardness / divisor; + + if (destroyProgress >= 1.0f) + return 0; // Instant break + + return (int)MathF.Ceiling(1.0f / destroyProgress); + } + + /// + /// Compute the player's destroy speed for a given block, following vanilla formulas. + /// + private static float GetDestroySpeed( + Material blockMaterial, + Item? heldItem, + Item? helmetItem, + Dictionary effects, + Dictionary playerAttributes, + bool isUnderwater, + bool isOnGround, + int protocolVersion, + MiningOptions options) + { + float speed = GetToolSpeed(blockMaterial, heldItem, protocolVersion); + + if (speed > 1.0f && options.ApplyEfficiencyEnchantments) + { + speed += GetEfficiencyBonus(heldItem, playerAttributes, protocolVersion); + } + + if (options.ApplyHasteEffects) + { + int digSpeedAmplifier = GetDigSpeedAmplifier(effects); + if (digSpeedAmplifier >= 0) + speed *= 1.0f + (digSpeedAmplifier + 1) * 0.2f; + } + + // Mining Fatigue + if (effects.TryGetValue(Effects.MiningFatigue, out var fatigueData)) + { + float multiplier = fatigueData.Amplifier switch + { + 0 => 0.3f, + 1 => 0.09f, + 2 => 0.0027f, + _ => 8.1E-4f + }; + speed *= multiplier; + } + + // Attribute multipliers for modern versions + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version) + { + // BLOCK_BREAK_SPEED attribute (default 1.0) + if (playerAttributes.TryGetValue("player.block_break_speed", out double bbs)) + speed *= (float)bbs; + } + + // Underwater penalty + if (isUnderwater) + { + if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version) + { + // 1.21.11+: Uses SUBMERGED_MINING_SPEED attribute (default 0.2) + double submergedSpeed = 0.2; + if (playerAttributes.TryGetValue("player.submerged_mining_speed", out double sms)) + submergedSpeed = sms; + speed *= (float)submergedSpeed; + } + else + { + // Pre-1.21.11: /5 unless Aqua Affinity + bool hasAquaAffinity = GetEnchantmentLevel(helmetItem, Enchantments.AquaAffinity, protocolVersion) > 0; + if (!hasAquaAffinity) + speed /= 5.0f; + } + } + + // Airborne penalty + if (!isOnGround) + speed /= 5.0f; + + return speed; + } + + /// + /// Get the base tool mining speed for a block. + /// For 1.20.6+ with ToolComponent, uses structured component data. + /// For older versions, uses hardcoded tool speed tables. + /// + private static float GetToolSpeed(Material blockMaterial, Item? heldItem, int protocolVersion) + { + if (heldItem is null) + return 1.0f; + + // Modern path: use ToolComponent from structured components + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version + && TryGetToolRules(heldItem, out List? rules, out float defaultMiningSpeed)) + { + foreach (var rule in rules) + { + if (rule.HasSpeed && MatchesBlockSet(rule.Blocks, blockMaterial)) + return rule.Speed; + } + + // Structured tool data covers modern mining rules, but keep the legacy fallback for + // explicit block holder-sets that MCC cannot resolve yet (for example cobweb). + if (defaultMiningSpeed > 1.0f) + return defaultMiningSpeed; + } + + // Legacy path: hardcoded tool speed tables + return GetLegacyToolSpeed(heldItem.Type, blockMaterial); + } + + /// + /// Check whether the tool provides correct drops for a block. + /// + private static bool HasCorrectToolForDrops(Material blockMaterial, Item? heldItem, int protocolVersion) + { + if (!BlockHardness.RequiresCorrectTool(blockMaterial)) + return true; + + if (heldItem is null) + return false; + + // Modern path: check ToolComponent rules + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version + && TryGetToolRules(heldItem, out List? rules, out _)) + { + foreach (var rule in rules) + { + if (rule.HasCorrectDropForBlocks && MatchesBlockSet(rule.Blocks, blockMaterial)) + return rule.CorrectDropForBlocks; + } + } + + // Legacy path, plus a modern fallback for direct block holder-sets MCC cannot resolve yet. + return IsCorrectToolLegacy(heldItem.Type, blockMaterial); + } + + private static bool TryGetToolRules( + Item heldItem, + [NotNullWhen(true)] out List? rules, + out float defaultMiningSpeed) + { + rules = null; + defaultMiningSpeed = 1.0f; + + if (heldItem.Components is null) + return false; + + if (heldItem.Components.OfType().FirstOrDefault() is ToolComponent toolComponent) + { + rules = toolComponent.Rules; + defaultMiningSpeed = toolComponent.DefaultMiningSpeed; + return true; + } + + if (heldItem.Components.OfType().FirstOrDefault() is ToolComponent1215 toolComponent1215) + { + rules = toolComponent1215.Rules; + defaultMiningSpeed = toolComponent1215.DefaultMiningSpeed; + return true; + } + + return false; + } + + /// + /// Match a block material against a ToolComponent BlockSetSubcomponent. + /// + private static bool MatchesBlockSet( + Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6.BlockSetSubcomponent blockSet, + Material blockMaterial) + { + if (blockSet.BlockIds is not null) + { + // Check against explicit block state IDs + foreach (int blockId in blockSet.BlockIds) + { + if (Block.Palette.FromId(blockId) == blockMaterial) + return true; + } + } + + if (blockSet.TagName is not null) + { + // Match against tag name (e.g., "minecraft:mineable/pickaxe") + return MatchesBlockTag(blockSet.TagName, blockMaterial); + } + + return false; + } + + /// + /// Approximate block tag matching using Material2Tool categories. + /// Tags like "minecraft:mineable/pickaxe" map to the appropriate tool categories. + /// + private static bool MatchesBlockTag(string tagName, Material blockMaterial) + { + // Normalize tag name + string tag = tagName.Replace("minecraft:", ""); + + ItemType[] tools = Material2Tool.GetCorrectToolForBlock(blockMaterial); + return tag switch + { + "mineable/pickaxe" => tools.Length > 0 && IsPickaxe(tools[0]), + "mineable/axe" => tools.Length > 0 && IsAxe(tools[0]), + "mineable/shovel" => tools.Length > 0 && IsShovel(tools[0]), + "mineable/hoe" => tools.Length > 0 && IsHoe(tools[0]), + "leaves" => IsLeaf(blockMaterial), + "wool" => IsWool(blockMaterial), + "incorrect_for_wooden_tool" => RequiresHigherTier(blockMaterial, 0), + "incorrect_for_gold_tool" => RequiresHigherTier(blockMaterial, 0), + "incorrect_for_stone_tool" => RequiresHigherTier(blockMaterial, 1), + "incorrect_for_copper_tool" => RequiresHigherTier(blockMaterial, 1), + "incorrect_for_iron_tool" => RequiresHigherTier(blockMaterial, 2), + "incorrect_for_diamond_tool" => RequiresHigherTier(blockMaterial, 3), + "incorrect_for_netherite_tool" => RequiresHigherTier(blockMaterial, 4), + _ => false + }; + } + + private static bool RequiresHigherTier(Material blockMaterial, int tier) + { + ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (recommended.Length == 0) + return false; + + return GetRequiredTier(blockMaterial, recommended) > tier; + } + + /// + /// Get the enchantment level from an item, supporting both legacy NBT and modern structured components. + /// + public static int GetEnchantmentLevel(Item? item, Enchantments enchantment, int protocolVersion) + { + if (item is null) + return 0; + + // Modern path: structured components (1.20.6+) + var enchList = item.EnchantmentList; + if (enchList is not null) + { + var ench = enchList.FirstOrDefault(e => e.Type == enchantment); + if (ench is not null) + return ench.Level; + } + + // Legacy path: NBT data + if (item.NBT is not null && + item.NBT.TryGetValue("Enchantments", out object? enchantments)) + { + try + { + string enchNameLower = GetEnchantmentResourceName(enchantment); + foreach (Dictionary enchEntry in (object[])enchantments) + { + string id = ((string)enchEntry["id"]).ToLowerInvariant(); + if (id == enchNameLower || id == "minecraft:" + enchNameLower) + return (short)enchEntry["lvl"]; + } + } + catch + { + // NBT parsing failure - return 0 + } + } + + return 0; + } + + /// + /// Map Enchantments enum to Minecraft resource name (e.g., "efficiency"). + /// + private static string GetEnchantmentResourceName(Enchantments enchantment) + { + return enchantment switch + { + Enchantments.AquaAffinity => "aqua_affinity", + Enchantments.BaneOfArthropods => "bane_of_arthropods", + Enchantments.BlastProtection => "blast_protection", + Enchantments.Efficiency => "efficiency", + Enchantments.FeatherFalling => "feather_falling", + Enchantments.FireAspect => "fire_aspect", + Enchantments.FireProtection => "fire_protection", + Enchantments.FrostWalker => "frost_walker", + Enchantments.LuckOfTheSea => "luck_of_the_sea", + Enchantments.ProjectileProtection => "projectile_protection", + Enchantments.QuickCharge => "quick_charge", + Enchantments.SilkTouch => "silk_touch", + Enchantments.SoulSpeed => "soul_speed", + Enchantments.SwiftSneak => "swift_sneak", + Enchantments.VanishingCurse => "vanishing_curse", + Enchantments.BindingCurse => "binding_curse", + Enchantments.WindBurst => "wind_burst", + _ => enchantment.ToString().ToUnderscoreCase() + }; + } + + #region Legacy Tool Speed Tables + + /// + /// Legacy tool speed for pre-1.20.6 versions using hardcoded values. + /// + private static float GetLegacyToolSpeed(ItemType toolType, Material blockMaterial) + { + float specialToolSpeed = toolType switch + { + ItemType.Shears => GetShearsSpeed(blockMaterial), + _ when IsSword(toolType) && blockMaterial == Material.Cobweb => 15.0f, + _ => 1.0f + }; + + if (specialToolSpeed > 1.0f) + return specialToolSpeed; + + ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (recommended.Length == 0) + return 1.0f; + + // Check if the held tool matches the recommended tool category + ToolCategory heldCategory = GetToolCategory(toolType); + ToolCategory neededCategory = GetToolCategory(recommended[0]); + + if (heldCategory == ToolCategory.None || heldCategory != neededCategory) + return 1.0f; + + return GetBaseToolSpeed(toolType); + } + + private static float GetBaseToolSpeed(ItemType toolType) + { + return toolType switch + { + // Wooden tools + ItemType.WoodenPickaxe or ItemType.WoodenAxe or ItemType.WoodenShovel or + ItemType.WoodenSword or ItemType.WoodenHoe => 2.0f, + + // Stone tools + ItemType.StonePickaxe or ItemType.StoneAxe or ItemType.StoneShovel or + ItemType.StoneSword or ItemType.StoneHoe => 4.0f, + + // Iron tools + ItemType.IronPickaxe or ItemType.IronAxe or ItemType.IronShovel or + ItemType.IronSword or ItemType.IronHoe => 6.0f, + + // Diamond tools + ItemType.DiamondPickaxe or ItemType.DiamondAxe or ItemType.DiamondShovel or + ItemType.DiamondSword or ItemType.DiamondHoe => 8.0f, + + // Netherite tools + ItemType.NetheritePickaxe or ItemType.NetheriteAxe or ItemType.NetheriteShovel or + ItemType.NetheriteSword or ItemType.NetheriteHoe => 9.0f, + + // Golden tools + ItemType.GoldenPickaxe or ItemType.GoldenAxe or ItemType.GoldenShovel or + ItemType.GoldenSword or ItemType.GoldenHoe => 12.0f, + + // Shears + ItemType.Shears => 2.0f, + + _ => 1.0f + }; + } + + /// + /// Check if the held tool is the correct tool for drops in legacy versions. + /// Uses Material2Tool's recommendations to determine correctness. + /// + private static bool IsCorrectToolLegacy(ItemType toolType, Material blockMaterial) + { + ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (recommended.Length == 0) + return false; + + ToolCategory heldCategory = GetToolCategory(toolType); + ToolCategory neededCategory = GetToolCategory(recommended[0]); + + if (heldCategory == ToolCategory.None || heldCategory != neededCategory) + { + if (toolType == ItemType.Shears && blockMaterial == Material.Cobweb) + return true; + if (IsSword(toolType) && blockMaterial == Material.Cobweb) + return true; + return false; + } + + // Check tool tier requirement + int heldTier = GetToolTier(toolType); + int requiredTier = GetRequiredTier(blockMaterial, recommended); + + return heldTier >= requiredTier; + } + + /// + /// Get the minimum tool tier required for a block based on Material2Tool's recommendation ordering. + /// + private static int GetRequiredTier(Material blockMaterial, ItemType[] recommended) + { + if (recommended.Length == 0) + return 0; + + // Material2Tool lists tools from highest to lowest tier. + // The last tool in the array is the minimum required tier. + return GetToolTier(recommended[^1]); + } + + private enum ToolCategory + { + None, + Pickaxe, + Axe, + Shovel, + Hoe, + Sword, + Shears + } + + private static ToolCategory GetToolCategory(ItemType item) + { + if (IsPickaxe(item)) return ToolCategory.Pickaxe; + if (IsAxe(item)) return ToolCategory.Axe; + if (IsShovel(item)) return ToolCategory.Shovel; + if (IsHoe(item)) return ToolCategory.Hoe; + if (IsSword(item)) return ToolCategory.Sword; + if (item == ItemType.Shears) return ToolCategory.Shears; + return ToolCategory.None; + } + + private static int GetToolTier(ItemType item) + { + string name = item.ToString(); + if (name.StartsWith("Wooden")) return 0; + if (name.StartsWith("Golden")) return 0; + if (name.StartsWith("Stone")) return 1; + if (name.StartsWith("Iron")) return 2; + if (name.StartsWith("Diamond")) return 3; + if (name.StartsWith("Netherite")) return 4; + return 0; + } + + private static bool IsPickaxe(ItemType item) => + item is ItemType.WoodenPickaxe or ItemType.StonePickaxe or ItemType.IronPickaxe + or ItemType.GoldenPickaxe or ItemType.DiamondPickaxe or ItemType.NetheritePickaxe; + + private static bool IsAxe(ItemType item) => + item is ItemType.WoodenAxe or ItemType.StoneAxe or ItemType.IronAxe + or ItemType.GoldenAxe or ItemType.DiamondAxe or ItemType.NetheriteAxe; + + private static bool IsShovel(ItemType item) => + item is ItemType.WoodenShovel or ItemType.StoneShovel or ItemType.IronShovel + or ItemType.GoldenShovel or ItemType.DiamondShovel or ItemType.NetheriteShovel; + + private static bool IsHoe(ItemType item) => + item is ItemType.WoodenHoe or ItemType.StoneHoe or ItemType.IronHoe + or ItemType.GoldenHoe or ItemType.DiamondHoe or ItemType.NetheriteHoe; + + private static bool IsSword(ItemType item) => + item is ItemType.WoodenSword or ItemType.StoneSword or ItemType.IronSword + or ItemType.GoldenSword or ItemType.DiamondSword or ItemType.NetheriteSword; + + private static float GetShearsSpeed(Material block) + { + return block switch + { + Material.Cobweb => 15.0f, + Material.Vine or Material.GlowLichen => 2.0f, + _ when IsLeaf(block) => 15.0f, + _ when IsWool(block) => 5.0f, + _ => 1.0f + }; + } + + private static bool IsShearable(Material block) => + block == Material.Cobweb || IsLeaf(block) || IsWool(block) || block is Material.Vine or Material.GlowLichen; + + private static bool IsLeaf(Material block) => + block is Material.OakLeaves or Material.SpruceLeaves or Material.BirchLeaves + or Material.JungleLeaves or Material.AcaciaLeaves or Material.DarkOakLeaves + or Material.CherryLeaves or Material.MangroveLeaves or Material.AzaleaLeaves + or Material.FloweringAzaleaLeaves or Material.PaleOakLeaves; + + private static bool IsWool(Material block) => + block is Material.WhiteWool or Material.OrangeWool or Material.MagentaWool + or Material.LightBlueWool or Material.YellowWool or Material.LimeWool + or Material.PinkWool or Material.GrayWool or Material.LightGrayWool + or Material.CyanWool or Material.PurpleWool or Material.BlueWool + or Material.BrownWool or Material.GreenWool or Material.RedWool + or Material.BlackWool; + + private static float GetEfficiencyBonus(Item? heldItem, Dictionary playerAttributes, int protocolVersion) + { + if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version + && playerAttributes.TryGetValue("player.mining_efficiency", out double miningEfficiency) + && miningEfficiency > 0.0) + { + return (float)miningEfficiency; + } + + int efficiencyLevel = GetEnchantmentLevel(heldItem, Enchantments.Efficiency, protocolVersion); + return efficiencyLevel > 0 ? efficiencyLevel * efficiencyLevel + 1 : 0.0f; + } + + private static int GetDigSpeedAmplifier(Dictionary effects) + { + int amplifier = -1; + + if (effects.TryGetValue(Effects.Haste, out var hasteData)) + amplifier = Math.Max(amplifier, hasteData.Amplifier); + + if (effects.TryGetValue(Effects.ConduitPower, out var conduitData)) + amplifier = Math.Max(amplifier, conduitData.Amplifier); + + return amplifier; + } + + #endregion + } +} diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index caed454d..0e972e09 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -232,8 +232,8 @@ namespace MinecraftClient.Mapping int tentativeGScore = current.GScore + (int)current.Location.DistanceSquared(neighbor); // If the neighbor is not in the GScoreDict OR its current tentativeGScore is lower than the previously saved one: - if (!gScoreDict.ContainsKey(neighbor) || - (gScoreDict.ContainsKey(neighbor) && tentativeGScore < gScoreDict[neighbor])) + if (!gScoreDict.TryGetValue(neighbor, out int existingGScore) || + tentativeGScore < existingGScore) { // Save the new relation between the neighbored block and the current one cameFrom[neighbor] = current.Location; @@ -247,7 +247,7 @@ namespace MinecraftClient.Mapping } // Goal could not be reached. Set the path to the closest location if close enough - if (current != null && openSet.MinHScoreNode != null && + if (current is not null && openSet.MinHScoreNode is not null && (maxOffset == int.MaxValue || openSet.MinHScoreNode.HScore <= maxOffset)) return ReconstructPath(cameFrom, openSet.MinHScoreNode.Location, start, goal); @@ -306,27 +306,9 @@ namespace MinecraftClient.Mapping /// /// Represents a location and its attributes /// - public class Node + public record Node(int GScore, int HScore, Location Location) { - // Distance to start - public int GScore; - - // Distance to Goal - public int HScore; - - public int FScore - { - get { return HScore + GScore; } - } - - public Location Location; - - public Node(int gScore, int hScore, Location loc) - { - this.GScore = gScore; - this.HScore = hScore; - Location = loc; - } + public int FScore => HScore + GScore; } // List which contains all nodes in form of a Binary Heap @@ -338,8 +320,8 @@ namespace MinecraftClient.Mapping public BinaryHeap() { - heapList = new List(); - locationList = new HashSet(); + heapList = new(); + locationList = new(); MinHScoreNode = null; } @@ -362,7 +344,7 @@ namespace MinecraftClient.Mapping locationList.Add(loc); // Save node with the smallest H-Score => Distance to goal - if (MinHScoreNode == null || newNode.HScore < MinHScoreNode.HScore) + if (MinHScoreNode is null || newNode.HScore < MinHScoreNode.HScore) MinHScoreNode = newNode; if (i == 0) @@ -491,7 +473,7 @@ namespace MinecraftClient.Mapping public static bool IsOnGround(World world, Location location) { ChunkColumn? chunkColumn = world.GetChunkColumn(location); - if (chunkColumn == null || chunkColumn.FullyLoaded == false) + if (chunkColumn is null || chunkColumn.FullyLoaded == false) return true; // avoid moving downward in a not loaded chunk Location down = Move(location, Direction.Down); @@ -721,11 +703,11 @@ namespace MinecraftClient.Mapping public static bool CheckChunkLoading(World world, Location start, Location dest) { var chunkColumn = world.GetChunkColumn(dest); - if (chunkColumn == null || chunkColumn.FullyLoaded == false) + if (chunkColumn is null || chunkColumn.FullyLoaded == false) return false; chunkColumn = world.GetChunkColumn(start); - if (chunkColumn == null || chunkColumn.FullyLoaded == false) + if (chunkColumn is null || chunkColumn.FullyLoaded == false) return false; return true; diff --git a/MinecraftClient/Mapping/PlayerTeam.cs b/MinecraftClient/Mapping/PlayerTeam.cs new file mode 100644 index 00000000..4ad7ca64 --- /dev/null +++ b/MinecraftClient/Mapping/PlayerTeam.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping +{ + /// + /// Represents a Minecraft scoreboard team and its current state. + /// + public class PlayerTeam + { + /// Team internal name (up to 16 chars) + public string Name { get; set; } = string.Empty; + + /// Display name component (formatted text) + public string DisplayName { get; set; } = string.Empty; + + /// Friendly fire is allowed between team members + public bool AllowFriendlyFire { get; set; } + + /// Team members can see invisible teammates + public bool SeeFriendlyInvisibles { get; set; } + + /// + /// Nametag visibility rule. + /// Values: "always", "hideForOtherTeams", "hideForOwnTeam", "never" + /// + public string NameTagVisibility { get; set; } = string.Empty; + + /// + /// Collision rule. + /// Values: "always", "pushOtherTeams", "pushOwnTeam", "never" + /// + public string CollisionRule { get; set; } = string.Empty; + + /// + /// Team color as ChatFormatting enum ordinal (-1 = RESET/none, + /// 0–15 = BLACK … WHITE). + /// + public int Color { get; set; } = -1; + + /// Prefix displayed before member names (formatted text) + public string Prefix { get; set; } = string.Empty; + + /// Suffix displayed after member names (formatted text) + public string Suffix { get; set; } = string.Empty; + + /// Current set of player / entity names on this team + public HashSet Members { get; } = new(System.StringComparer.OrdinalIgnoreCase); + } +} diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index 0b2e02f9..832bf5d0 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -12,9 +12,9 @@ namespace MinecraftClient.Mapping { /// /// The chunks contained into the Minecraft world - /// Tuple: Tuple + /// (int ChunkX, int ChunkZ): chunkX, chunkZ /// - private ConcurrentDictionary, ChunkColumn> chunks = new(); + private ConcurrentDictionary<(int ChunkX, int ChunkZ), ChunkColumn> chunks = new(); /// /// The dimension info of the world @@ -23,6 +23,16 @@ namespace MinecraftClient.Mapping private static readonly Dictionary dimensionList = new(); + /// + /// VarInt ID → dimension name mapping, populated from RegistryData in 1.20.6+ + /// + private static Dictionary dimensionIdMap = new(); + + /// + /// VarInt ID → attribute name mapping, populated from RegistryData (minecraft:attribute) in 1.20.6+ + /// + private static Dictionary attributeIdMap = new(); + /// /// Chunk data parsing progress /// @@ -39,13 +49,13 @@ namespace MinecraftClient.Mapping { get { - chunks.TryGetValue(new(chunkX, chunkZ), out ChunkColumn? chunkColumn); + chunks.TryGetValue((chunkX, chunkZ), out ChunkColumn? chunkColumn); return chunkColumn; } set { - Tuple chunkCoord = new(chunkX, chunkZ); - if (value == null) + var chunkCoord = (chunkX, chunkZ); + if (value is null) chunks.TryRemove(chunkCoord, out _); else chunks.AddOrUpdate(chunkCoord, value, (_, _) => value); @@ -59,7 +69,16 @@ namespace MinecraftClient.Mapping /// Registry Codec nbt data public static void StoreDimensionList(Dictionary registryCodec) { - var dimensionListNbt = (object[])(((Dictionary)registryCodec["minecraft:dimension_type"])["value"]); + const string namespacedDimensionTypeKey = "minecraft:dimension_type"; + const string legacyDimensionTypeKey = "dimension_type"; + + if (!registryCodec.TryGetValue(namespacedDimensionTypeKey, out var dimensionTypeRegistry) + && !registryCodec.TryGetValue(legacyDimensionTypeKey, out dimensionTypeRegistry)) + { + return; + } + + var dimensionListNbt = (object[])(((Dictionary)dimensionTypeRegistry)["value"]); foreach (var (dimensionName, dimensionType) in from Dictionary dimensionNbt in dimensionListNbt let dimensionName = (string)dimensionNbt["name"] let dimensionType = (Dictionary)dimensionNbt["element"] @@ -69,6 +88,223 @@ namespace MinecraftClient.Mapping } } + public static void LoadDefaultDimensions1206Plus() + { + // TODO: Move this to a JSON file. + + var defaultRegistryCodec = new Dictionary + { + { "minecraft:dimension_type", new Dictionary + { + { "value", new object[] + { + new Dictionary + { + { "name", "minecraft:overworld" }, + { "id", 0 }, + { "element", new Dictionary + { + { "piglin_safe", (byte)0 }, + { "natural", 1 }, + { "ambient_light", 0.0 }, + { "monster_spawn_block_light_limit", 0 }, + { "infiniburn", "#minecraft:infiniburn_overworld" }, + { "respawn_anchor_works", 0 }, + { "has_skylight", 1 }, + { "bed_works", 1 }, + { "effects", "minecraft:overworld" }, + { "has_raids", 1 }, + { "logical_height", 384 }, + { "coordinate_scale", 1.0 }, + { "monster_spawn_light_level", new Dictionary + { + { "min_inclusive", 0 }, + { "max_inclusive", 7 }, + { "type", "minecraft:uniform" } + } + }, + { "min_y", -64 }, + { "ultrawarm", 0 }, + { "has_ceiling", 0 }, + { "height", 384 } + } + } + }, + new Dictionary + { + { "name", "minecraft:overworld_caves" }, + { "id", 1 }, + { "element", new Dictionary + { + { "piglin_safe", (byte)0 }, + { "natural", 1 }, + { "ambient_light", 0.0 }, + { "monster_spawn_block_light_limit", 0 }, + { "infiniburn", "#minecraft:infiniburn_overworld" }, + { "respawn_anchor_works", 0 }, + { "has_skylight", 1 }, + { "bed_works", 1 }, + { "effects", "minecraft:overworld" }, + { "has_raids", 1 }, + { "logical_height", 384 }, + { "coordinate_scale", 1.0 }, + { "monster_spawn_light_level", new Dictionary + { + { "min_inclusive", 0 }, + { "max_inclusive", 7 }, + { "type", "minecraft:uniform" } + } + }, + { "min_y", -64 }, + { "ultrawarm", 0 }, + { "has_ceiling", 1 }, + { "height", 384 } + } + } + }, + new Dictionary + { + { "name", "minecraft:the_end" }, + { "id", 2 }, + { "element", new Dictionary + { + { "piglin_safe", (byte)0 }, + { "natural", 0 }, + { "ambient_light", 0.0 }, + { "monster_spawn_block_light_limit", 0 }, + { "infiniburn", "#minecraft:infiniburn_end" }, + { "respawn_anchor_works", 0 }, + { "has_skylight", 0 }, + { "bed_works", 0 }, + { "effects", "minecraft:the_end" }, + { "fixed_time", 6000 }, + { "has_raids", 1 }, + { "logical_height", 256 }, + { "coordinate_scale", 1.0 }, + { "monster_spawn_light_level", new Dictionary + { + { "min_inclusive", 0 }, + { "max_inclusive", 7 }, + { "type", "minecraft:uniform" } + } + }, + { "min_y", 0 }, + { "ultrawarm", 0 }, + { "has_ceiling", 0 }, + { "height", 256 } + } + } + }, + new Dictionary + { + { "name", "minecraft:the_nether" }, + { "id", 3 }, + { "element", new Dictionary + { + { "piglin_safe", (byte)1 }, + { "natural", 0 }, + { "ambient_light", 0.1 }, + { "monster_spawn_block_light_limit", 15 }, + { "infiniburn", "#minecraft:infiniburn_nether" }, + { "respawn_anchor_works", 1 }, + { "has_skylight", 0 }, + { "bed_works", 0 }, + { "effects", "minecraft:the_nether" }, + { "fixed_time", 18000 }, + { "has_raids", 0 }, + { "logical_height", 128 }, + { "coordinate_scale", 8.0 }, + { "monster_spawn_light_level", 7 }, + { "min_y", 0 }, + { "ultrawarm", 1 }, + { "has_ceiling", 1 }, + { "height", 256 } + } + } + } + } + } + } + } + }; + + StoreDimensionList(defaultRegistryCodec); + } + + public static void SetDimensionIdMap(Dictionary idMap) + { + dimensionIdMap = idMap; + } + + public static string GetDimensionNameById(int id) + { + return dimensionIdMap.TryGetValue(id, out var name) ? name : "minecraft:overworld"; + } + + public static bool HasAnyDimension() + { + return dimensionList.Count > 0; + } + + public static void SetAttributeIdMap(Dictionary idMap) + { + attributeIdMap = idMap; + } + + /// + /// Get attribute name by its registry VarInt ID. Returns null if the ID is unknown. + /// When KnownDataPacks negotiation tells the server we already have vanilla data, + /// the server skips sending the attribute registry. In that case we fall back to + /// the built-in vanilla 1.20.6 attribute order (22 entries). + /// + public static string? GetAttributeNameById(int id) + { + if (attributeIdMap.Count == 0) + LoadDefaultAttributes(); + return attributeIdMap.TryGetValue(id, out var name) ? name : null; + } + + private static void LoadDefaultAttributes() + { + // Fallback for when the server doesn't send attribute registry via RegistryData. + // Matches 1.21.1 Attributes.java registration order. + // For 1.20.6+ servers, SetAttributeIdMap() overrides this with the actual registry. + attributeIdMap = new Dictionary + { + { 0, "generic.armor" }, + { 1, "generic.armor_toughness" }, + { 2, "generic.attack_damage" }, + { 3, "generic.attack_knockback" }, + { 4, "generic.attack_speed" }, + { 5, "player.block_break_speed" }, + { 6, "player.block_interaction_range" }, + { 7, "generic.burning_time" }, + { 8, "generic.explosion_knockback_resistance" }, + { 9, "player.entity_interaction_range" }, + { 10, "generic.fall_damage_multiplier" }, + { 11, "generic.flying_speed" }, + { 12, "generic.follow_range" }, + { 13, "generic.gravity" }, + { 14, "generic.jump_strength" }, + { 15, "generic.knockback_resistance" }, + { 16, "generic.luck" }, + { 17, "generic.max_absorption" }, + { 18, "generic.max_health" }, + { 19, "player.mining_efficiency" }, + { 20, "generic.movement_efficiency" }, + { 21, "generic.movement_speed" }, + { 22, "generic.oxygen_bonus" }, + { 23, "generic.safe_fall_distance" }, + { 24, "generic.scale" }, + { 25, "player.sneaking_speed" }, + { 26, "zombie.spawn_reinforcements" }, + { 27, "generic.step_height" }, + { 28, "player.submerged_mining_speed" }, + { 29, "player.sweeping_damage_ratio" }, + { 30, "generic.water_movement_efficiency" } + }; + } + /// /// Store one dimension - Directly used in 1.16.2 to 1.18.2 /// @@ -89,9 +325,59 @@ namespace MinecraftClient.Mapping /// The dimension type (NBT Tag Compound) public static void SetDimension(string name) { - curDimension = dimensionList[name]; // Should not fail + // Try to get the dimension using the name as is + if (dimensionList.TryGetValue(name, out Dimension? dimension)) + { + curDimension = dimension; + return; // Dimension found + } + + // If not found, check if name lacks 'minecraft:' prefix and try again + if (!name.StartsWith("minecraft:")) + { + string prefixedName = "minecraft:" + name; + if (dimensionList.TryGetValue(prefixedName, out dimension)) + { + curDimension = dimension; + return; // Dimension found with prefixed name + } + } + else + { + string unprefixedName = name["minecraft:".Length..]; + if (dimensionList.TryGetValue(unprefixedName, out dimension)) + { + curDimension = dimension; + return; + } + } + + if (TryStoreDefaultVanillaDimension(name) + && dimensionList.TryGetValue(name, out dimension)) + { + curDimension = dimension; + return; + } + + // If still not found, dimension does not exist + throw new KeyNotFoundException($"Dimension '{name}' not found in dimensions dictionary."); } + private static bool TryStoreDefaultVanillaDimension(string name) + { + var normalizedName = name.StartsWith("minecraft:") + ? name + : "minecraft:" + name; + + if (normalizedName is not ("minecraft:overworld" or "minecraft:the_nether" or "minecraft:the_end")) + return false; + + StoreOneDimension(name, new Dictionary()); + return true; + } + + + /// /// Get current dimension @@ -113,7 +399,7 @@ namespace MinecraftClient.Mapping /// Whether the ChunkColumn has been fully loaded public void StoreChunk(int chunkX, int chunkY, int chunkZ, int chunkColumnSize, Chunk? chunk, bool loadCompleted) { - ChunkColumn chunkColumn = chunks.GetOrAdd(new(chunkX, chunkZ), (_) => new(chunkColumnSize)); + ChunkColumn chunkColumn = chunks.GetOrAdd((chunkX, chunkZ), (_) => new(chunkColumnSize)); chunkColumn[chunkY] = chunk; if (loadCompleted) chunkColumn.FullyLoaded = true; @@ -137,10 +423,10 @@ namespace MinecraftClient.Mapping public Block GetBlock(Location location) { ChunkColumn? column = GetChunkColumn(location); - if (column != null) + if (column is not null) { Chunk? chunk = column.GetChunk(location); - if (chunk != null) + if (chunk is not null) return chunk.GetBlock(location); } return Block.Air; @@ -189,10 +475,10 @@ namespace MinecraftClient.Mapping public void SetBlock(Location location, Block block) { ChunkColumn? column = this[location.ChunkX, location.ChunkZ]; - if (column != null && column.ColumnSize >= location.ChunkY) + if (column is not null && location.ChunkY >= 0 && location.ChunkY < column.ColumnSize) { Chunk? chunk = column.GetChunk(location); - if (chunk == null) + if (chunk is null) column[location.ChunkY] = chunk = new Chunk(); chunk[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ] = block; } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index f92ce1b8..753de486 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; @@ -10,9 +11,11 @@ using MinecraftClient.ChatBots; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Commands; +using MinecraftClient.Dialogs; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; +using MinecraftClient.Physics; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers.Forge; @@ -37,16 +40,25 @@ namespace MinecraftClient private readonly Dictionary onlinePlayers = new(); private static bool commandsLoaded = false; + private readonly Lock tabListHeaderFooterLock = new(); + private string tabListHeader = string.Empty; + private string tabListFooter = string.Empty; private readonly Queue chatQueue = new(); private static DateTime nextMessageSendTime = DateTime.MinValue; private readonly Queue threadTasks = new(); - private readonly object threadTasksLock = new(); + private readonly Lock threadTasksLock = new(); + private readonly Lock recipeBookLock = new(); + private readonly Lock achievementsLock = new(); private readonly List bots = new(); private static readonly List botsOnHold = new(); private static readonly Dictionary inventories = new(); + private static readonly HashSet inventoriesWithFullContents = new(); + private readonly Dictionary unlockedRecipes = new(StringComparer.Ordinal); + private readonly Dictionary achievements = new(StringComparer.Ordinal); + private string? activeAdvancementTab; private readonly Dictionary> registeredBotPluginChannels = new(); private readonly List registeredServerPluginChannels = new(); @@ -57,23 +69,25 @@ namespace MinecraftClient private bool inventoryHandlingRequested = false; private bool entityHandlingEnabled; - private readonly object locationLock = new(); + private readonly Lock locationLock = new(); private bool locationReceived = false; private readonly World world = new(); - private Queue? steps; private Queue? path; private Location location; private float? _yaw; // Used for calculation ONLY!!! Doesn't reflect the client yaw private float? _pitch; // Used for calculation ONLY!!! Doesn't reflect the client pitch private float playerYaw; private float playerPitch; - private double motionY; + private readonly PlayerPhysics playerPhysics = new(); + private readonly MovementInput physicsInput = new(); + private bool physicsInitialized = false; + private Location? pathTarget; // Current waypoint for physics-driven pathfinding public enum MovementType { Sneak, Walk, Sprint } private int sequenceId; // User for player block synchronization (Aka. digging, placing blocks, etc..) private bool CanSendMessage = false; - private readonly string host; - private readonly int port; + private string host; + private int port; private readonly int protocolversion; private readonly string username; private Guid uuid; @@ -81,7 +95,7 @@ namespace MinecraftClient private readonly string sessionid; private readonly PlayerKeyPair? playerKeyPair; private DateTime lastKeepAlive; - private readonly object lastKeepAliveLock = new(); + private readonly Lock lastKeepAliveLock = new(); private int respawnTicks = 0; private int gamemode = 0; private bool isSupportPreviewsChat; @@ -89,7 +103,7 @@ namespace MinecraftClient private int playerEntityID; - private object DigLock = new(); + private readonly Lock DigLock = new(); private Tuple? LastDigPosition; private int RemainingDiggingTime = 0; @@ -99,7 +113,16 @@ namespace MinecraftClient private int playerLevel; private int playerTotalExperience; private byte CurrentSlot = 0; - + + // player effects + private readonly Dictionary playerEffects = new(); + + // player attributes (e.g., block_break_speed, mining_efficiency, submerged_mining_speed) + private readonly Dictionary playerAttributes = new(); + + // scoreboard teams (key = team name) + private readonly Dictionary teams = new(StringComparer.Ordinal); + // Sneaking public bool IsSneaking { get; set; } = false; private bool isUnderSlab = false; @@ -107,6 +130,8 @@ namespace MinecraftClient // Entity handling private readonly Dictionary entities = new(); + private readonly Lock signDataLock = new(); + private readonly Dictionary<(int x, int y, int z), (string material, string typeLabel, string[] frontText, string[] backText, bool isWaxed)> knownSigns = new(); // server TPS private long lastAge = 0; @@ -120,6 +145,9 @@ namespace MinecraftClient // ChatBot OnNetworkPacket event private bool networkPacketCaptureEnabled = false; + // Cookies + private Dictionary Cookies { get; set; } = new(); + public int GetServerPort() { return port; } public string GetServerHost() { return host; } public string GetUsername() { return username; } @@ -135,6 +163,40 @@ namespace MinecraftClient public bool GetIsSupportPreviewsChat() { return isSupportPreviewsChat; } public float GetHealth() { return playerHealth; } public int GetSaturation() { return playerFoodSaturation; } + + /// + /// Get the player's active effects + /// + /// Dictionary of active effects + public Dictionary GetPlayerEffects() + { + return new Dictionary(playerEffects); + } + + /// + /// Get a snapshot of all known scoreboard teams. + /// + /// Dictionary mapping team name to + public Dictionary GetTeams() + { + lock (teams) + return new Dictionary(teams, StringComparer.Ordinal); + } + + /// + /// Get the team that contains the given player/entity name, or null if not found. + /// + public PlayerTeam? GetPlayerTeam(string playerName) + { + lock (teams) + { + foreach (var team in teams.Values) + if (team.Members.Contains(playerName)) + return team; + return null; + } + } + public int GetLevel() { return playerLevel; } public int GetTotalExperience() { return playerTotalExperience; } public byte GetCurrentSlot() { return CurrentSlot; } @@ -144,14 +206,38 @@ namespace MinecraftClient public ILogger GetLogger() { return Log; } public int GetPlayerEntityID() { return playerEntityID; } public List GetLoadedChatBots() { return new List(bots); } + public void GetCookie(string key, out byte[]? data) => Cookies.TryGetValue(key, out data); + public void SetCookie(string key, byte[] data) => Cookies[key] = data; + public void DeleteCookie(string key) => Cookies.Remove(key, out var data); + public (Location location, string material, string typeLabel, string[] frontText, string[] backText, bool isWaxed)[] GetKnownSigns() + { + lock (signDataLock) + { + return knownSigns + .Select(pair => ( + location: new Location(pair.Key.x, pair.Key.y, pair.Key.z), + material: pair.Value.material, + typeLabel: pair.Value.typeLabel, + frontText: (string[])pair.Value.frontText.Clone(), + backText: (string[])pair.Value.backText.Clone(), + isWaxed: pair.Value.isWaxed)) + .ToArray(); + } + } - readonly TcpClient client; - readonly IMinecraftCom handler; + TcpClient client = null!; + IMinecraftCom handler = null!; + SessionToken _sessionToken; CancellationTokenSource? cmdprompt = null; Tuple? timeoutdetector = null; + private Thread? basicIOReadThread; + private int transferInProgress = 0; + private bool consoleReadThreadOwned = false; + private bool consoleHandlersAttached = false; public ILogger Log; - + public DialogManager Dialogs { get; } + private static IMinecraftComHandler? instance; public static IMinecraftComHandler? Instance => instance; @@ -168,7 +254,7 @@ namespace MinecraftClient { CmdResult.currentHandler = this; instance = this; - + terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; entityHandlingEnabled = Config.Main.Advanced.EntityHandling; @@ -182,6 +268,7 @@ namespace MinecraftClient this.port = port; this.protocolversion = protocolversion; this.playerKeyPair = playerKeyPair; + _sessionToken = session; Log = Settings.Config.Logging.LogToFile ? new FileLogLogger(Config.AppVar.ExpandVars(Settings.Config.Logging.LogFile), Settings.Config.Logging.PrependTimestamp) @@ -191,16 +278,17 @@ namespace MinecraftClient Log.ChatEnabled = Config.Logging.ChatMessages; Log.WarnEnabled = Config.Logging.WarningMessages; Log.ErrorEnabled = Config.Logging.ErrorMessages; + Dialogs = new DialogManager(this); // SENTRY: Send our client version and server version to Sentry SentrySdk.ConfigureScope(scope => { scope.SetTag("Protocol Version", protocolversion.ToString()); scope.SetTag("Minecraft Version", ProtocolHandler.ProtocolVersion2MCVer(protocolversion)); - scope.SetTag("MCC Build", Program.BuildInfo == null ? "Debug" : Program.BuildInfo); - - if (forgeInfo != null) - scope.SetTag("Forge Version", forgeInfo?.Version.ToString()); + scope.SetTag("MCC Build", Program.BuildInfo is null ? "Debug" : Program.BuildInfo); + + if (forgeInfo is not null) + scope.SetTag("Forge Version", forgeInfo.Version.ToString()); scope.Contexts["Server Information"] = new { @@ -208,17 +296,17 @@ namespace MinecraftClient MinecraftVersion = ProtocolHandler.ProtocolVersion2MCVer(protocolversion), ForgeInfo = forgeInfo?.Version }; - - scope.Contexts["Client Configuration"] = new + + scope.Contexts["Client Configuration"] = new { TerrainAndMovementsEnabled = terrainAndMovementsEnabled, InventoryHandlingEnabled = inventoryHandlingEnabled, EntityHandlingEnabled = entityHandlingEnabled }; }); - + SentrySdk.StartSession(); - + /* Load commands from Commands namespace */ LoadCommands(); @@ -231,6 +319,8 @@ namespace MinecraftClient client.ReceiveBufferSize = 1024 * 1024; client.ReceiveTimeout = Config.Main.Advanced.TcpTimeout * 1000; // Default: 30 seconds handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, forgeInfo, this); + if (forgeInfo is not null && forgeInfo.Version == FMLVersion.FML) + ChatParser.LoadForgeModTranslations(forgeInfo.Mods.Select(static mod => mod.ModID)); Log.Info(Translations.mcc_version_supported); timeoutdetector = new(new Thread(new ParameterizedThreadStart(TimeoutDetector)), new CancellationTokenSource()); @@ -247,10 +337,7 @@ namespace MinecraftClient Log.Info(string.Format(Translations.mcc_joined, Config.Main.Advanced.InternalCmdChar.ToLogString())); - cmdprompt = new CancellationTokenSource(); - ConsoleInteractive.ConsoleReader.BeginReadThread(); - ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnInputChange += ConsoleIO.AutocompleteHandler; + StartConsoleSession(); } else { @@ -274,8 +361,8 @@ namespace MinecraftClient return; - Retry: - if (timeoutdetector != null) + Retry: + if (timeoutdetector is not null) { timeoutdetector.Item2.Cancel(); timeoutdetector = null; @@ -292,30 +379,250 @@ namespace MinecraftClient } else if (InternalConfig.InteractiveMode) { - ConsoleInteractive.ConsoleReader.StopReadThread(); - ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; + StopConsoleSession(); Program.HandleFailure(); } throw new Exception("Initialization failed."); - } + } else { - // The AutoRelog ChatBot will handle reconnection at this point. - // This is important, or else we'll have multiple instances of the client running at the same time. + // AutoRelog is enabled - invoke its static handler to trigger reconnection. + // Use the same "Connection has been lost" message that OnConnectionLost uses + // for ConnectionLost, so it matches the default Kick_Messages. + if (AutoRelog.OnDisconnectStatic(ChatBot.DisconnectReason.ConnectionLost, Translations.mcc_disconnect_lost)) + return; // AutoRelog is triggering a restart - if (ReconnectionAttemptsLeft == 0) + // AutoRelog chose not to reconnect (e.g., message didn't match + // kick messages and Ignore_Kick_Message is false, or retry limit reached) + if (InternalConfig.InteractiveMode) { - if (InternalConfig.InteractiveMode) - { - ConsoleInteractive.ConsoleReader.StopReadThread(); - ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; - Program.HandleFailure(); - } + StopConsoleSession(); + Program.HandleFailure(); + } + + throw new Exception("Initialization failed."); + } + } + + public void Transfer(string newHost, int newPort) + { + // Do not block here: a new handler can start processing packets before the + // previous transfer call fully unwinds, and waiting can deadlock main-thread work. + if (Interlocked.CompareExchange(ref transferInProgress, 1, 0) != 0) + { + Log.Warn($"Ignoring overlapping transfer to {newHost}:{newPort} because another transfer is still in progress."); + return; + } + + IMinecraftCom oldHandler = handler; + TcpClient oldClient = client; + string resolvedHost = newHost; + int resolvedPort = newPort; + + try + { + ResolveTransferAddress(ref resolvedHost, ref resolvedPort); + Log.Info($"Initiating a transfer to: {resolvedHost}:{resolvedPort}"); + + // Unload bots + UnloadAllBots(); + bots.Clear(); + + ResetStateForTransfer(); + + // Retire the old handler so its updater exits without reporting a stale disconnect. + oldHandler.Dispose(); + oldClient.Close(); + + host = resolvedHost; + port = resolvedPort; + UpdateKeepAlive(); + + // Establish new connection + client = ProxyHandler.NewTcpClient(resolvedHost, resolvedPort); + client.ReceiveBufferSize = 1024 * 1024; + client.ReceiveTimeout = Config.Main.Advanced.TcpTimeout * 1000; + + // Reinitialize the protocol handler + handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, null, this); + Log.Info($"Connected to {resolvedHost}:{resolvedPort}"); + + // Retry login process + if (handler.Login(playerKeyPair, _sessionToken, isTransfer: true)) + { + foreach (var bot in botsOnHold) + BotLoad(bot, false); + botsOnHold.Clear(); + + UpdateKeepAlive(); + Log.Info($"Successfully transferred connection and logged in to {resolvedHost}:{resolvedPort}."); + + StartConsoleSession(); + } + else + { + Log.Error("Failed to login to the new host."); + throw new Exception("Login failed after transfer."); } } + catch (Exception ex) + { + Log.Error($"Transfer to {resolvedHost}:{resolvedPort} failed: {ex.Message}"); + + try + { + handler.Dispose(); + } + catch + { + } + + try + { + client.Close(); + } + catch + { + } + + // Handle reconnection attempts + if (timeoutdetector is not null) + { + timeoutdetector.Item2.Cancel(); + timeoutdetector = null; + } + + if (ReconnectionAttemptsLeft > 0) + { + Log.Info($"Reconnecting... Attempts left: {ReconnectionAttemptsLeft}"); + Thread.Sleep(5000); + ReconnectionAttemptsLeft--; + Program.Restart(); + } + else if (InternalConfig.InteractiveMode) + { + StopConsoleSession(); + Program.HandleFailure(); + } + + throw new Exception("Transfer failed and reconnection attempts exhausted.", ex); + } + finally + { + Interlocked.Exchange(ref transferInProgress, 0); + } + } + + private static void ResolveTransferAddress(ref string host, ref int port) + { + if (Config.Main.Advanced.ResolveSrvRecords == MainConfigHelper.MainConfig.AdvancedConfig.ResolveSrvRecordType.no + || port != 25565 + || IPAddress.TryParse(host, out _)) + { + return; + } + + ushort resolvedPort = (ushort)port; + ProtocolHandler.MinecraftServiceLookup(ref host, ref resolvedPort); + port = resolvedPort; + } + + private void StartConsoleSession() + { + cmdprompt = new CancellationTokenSource(); + + if (ConsoleIO.BasicIO || ConsoleIO.Backend is null) + { + if (!consoleReadThreadOwned) + { + CancellationToken token = cmdprompt.Token; + basicIOReadThread = new Thread(() => BasicIOReadLoop(token)) + { + IsBackground = true, + Name = "MCC BasicIO read thread" + }; + basicIOReadThread.Start(); + consoleReadThreadOwned = true; + } + + return; + } + + if (!consoleReadThreadOwned) + { + ConsoleIO.Backend.BeginReadThread(); + consoleReadThreadOwned = true; + } + + if (!consoleHandlersAttached) + { + ConsoleIO.Backend.MessageReceived += ConsoleReaderOnMessageReceived; + ConsoleIO.Backend.OnInputChange += ConsoleIO.AutocompleteHandler; + consoleHandlersAttached = true; + } + } + + private void StopConsoleSession() + { + if (ConsoleIO.BasicIO || ConsoleIO.Backend is null) + { + cmdprompt?.Cancel(); + basicIOReadThread = null; + consoleReadThreadOwned = false; + consoleHandlersAttached = false; + return; + } + + if (consoleHandlersAttached) + { + ConsoleIO.Backend.MessageReceived -= ConsoleReaderOnMessageReceived; + ConsoleIO.Backend.OnInputChange -= ConsoleIO.AutocompleteHandler; + consoleHandlersAttached = false; + } + + if (consoleReadThreadOwned) + { + ConsoleIO.Backend.StopReadThread(); + consoleReadThreadOwned = false; + } + } + + private void BasicIOReadLoop(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + string? input = Console.ReadLine(); + if (input is null) + return; + + if (!token.IsCancellationRequested) + ConsoleReaderOnMessageReceived(this, input); + } + } + + private void ResetStateForTransfer() + { + ClearTasks(); + ConsoleIO.CancelAutocomplete(); + SetCanSendMessage(false); + + locationReceived = false; + physicsInitialized = false; + isUnderSlab = false; + path = null; + pathTarget = null; + _yaw = null; + _pitch = null; + LastDigPosition = null; + RemainingDiggingTime = 0; + nextSneakingUpdate = DateTime.Now; + + physicsInput.Reset(); + world.Clear(); + entities.Clear(); + ClearKnownSigns(); + ClearInventories(); } /// @@ -342,13 +649,14 @@ namespace MinecraftClient if (Config.ChatBot.Map.Enabled) { BotLoad(new Map()); } if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); } if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); } - if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); } + if (Config.ChatBot.ReplayCapture.Enabled) { BotLoad(new ReplayCapture()); } if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); } if (Config.ChatBot.TelegramBridge.Enabled) { BotLoad(new TelegramBridge()); } if (Config.ChatBot.ItemsCollector.Enabled) { BotLoad(new ItemsCollector()); } - if (Config.ChatBot.WebSocketBot.Enabled) { BotLoad(new WebSocketBot()); } - //Add your ChatBot here by uncommenting and adapting - //BotLoad(new ChatBots.YourBot()); + if (Config.ChatBot.DiscordRpc.Enabled) { BotLoad(new DiscordRpc()); } + if (Config.ChatBot.McpServer.Enabled) { BotLoad(new McpServer()); } + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MCC_FILE_INPUT"))) + BotLoad(new FileInputBot()); } /// @@ -369,7 +677,7 @@ namespace MinecraftClient } /// - /// Called ~10 times per second by the protocol handler + /// Called 20 times per second by the protocol handler /// public void OnUpdate() { @@ -417,33 +725,43 @@ namespace MinecraftClient { lock (locationLock) { - for (int i = 0; i < Config.Main.Advanced.MovementSpeed; i++) //Needs to run at 20 tps; MCC runs at 10 tps + if (!physicsInitialized) { - if (_yaw == null || _pitch == null) - { - if (steps != null && steps.Count > 0) - { - location = steps.Dequeue(); - } - else if (path != null && path.Count > 0) - { - Location next = path.Dequeue(); - steps = Movement.Move2Steps(location, next, ref motionY); - - if (Config.Main.Advanced.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers - UpdateLocation(location, next + new Location(0, 1, 0)); // Update yaw and pitch to look at next step - } - else - { - location = Movement.HandleGravity(world, location, ref motionY); - } - } - playerYaw = _yaw == null ? playerYaw : _yaw.Value; - playerPitch = _pitch == null ? playerPitch : _pitch.Value; - handler.SendLocationUpdate(location, Movement.IsOnGround(world, location), _yaw, _pitch); + BlockShapes.Initialize(); + playerPhysics.SetPosition(location.X, location.Y, location.Z); + playerPhysics.Yaw = playerYaw; + playerPhysics.Pitch = playerPitch; + physicsInitialized = true; } - // First 2 updates must be player position AND look, and player must not move (to conform with vanilla) - // Once yaw and pitch have been sent, switch back to location-only updates (without yaw and pitch) + + // Navigate pathfinding: set input based on current path + UpdatePathfindingInput(); + + // Sync yaw/pitch if explicitly set (by commands/bots) + if (_yaw is not null) playerPhysics.Yaw = _yaw.Value; + if (_pitch is not null) playerPhysics.Pitch = _pitch.Value; + + // Update environment flags (water, lava, climbable) + playerPhysics.UpdateEnvironment(world); + + // Apply movement input + playerPhysics.ApplyInput(physicsInput); + + // Run one physics tick + playerPhysics.Tick(world); + + // Sync back to MCC location + location = new Location( + playerPhysics.Position.X, + playerPhysics.Position.Y, + playerPhysics.Position.Z); + + playerYaw = _yaw ?? playerYaw; + playerPitch = _pitch ?? playerPitch; + + // Send position packet + handler.SendLocationUpdate(location, playerPhysics.OnGround, playerPhysics.HorizontalCollision, _yaw, _pitch); + _yaw = null; _pitch = null; } @@ -456,6 +774,26 @@ namespace MinecraftClient SendRespawnPacket(); } + // Check for expired effects + if (playerEffects.Count > 0) + { + var expiredEffects = playerEffects + .Where(e => e.Value.IsExpired) + .Select(e => e.Key) + .ToList(); + + foreach (var effect in expiredEffects) + { + if (!playerEffects.Remove(effect, out var effectData)) + continue; + + AnnouncePlayerEffectExpired(effectData); + + if (entities.TryGetValue(playerEntityID, out var playerEntity)) + playerEntity.ActiveEffects.Remove(effect); + } + } + lock (threadTasksLock) { while (threadTasks.Count > 0) @@ -469,7 +807,7 @@ namespace MinecraftClient { if (RemainingDiggingTime > 0) { - if (--RemainingDiggingTime == 0 && LastDigPosition != null) + if (--RemainingDiggingTime == 0 && LastDigPosition is not null) { handler.SendPlayerDigging(2, LastDigPosition.Item1, LastDigPosition.Item2, sequenceId++); Log.Info(string.Format(Translations.cmd_dig_end, LastDigPosition.Item1)); @@ -528,30 +866,35 @@ namespace MinecraftClient /// public void Disconnect() { + instance = null; + DispatchBotEvent(bot => bot.OnDisconnect(ChatBot.DisconnectReason.UserLogout, "")); - botsOnHold.Clear(); - botsOnHold.AddRange(bots); + foreach (ChatBot bot in bots.Where(bot => bot.ScriptOwnerKey is not null).ToList()) + BotUnLoad(bot); - if (handler != null) + botsOnHold.Clear(); + botsOnHold.AddRange(bots.Where(bot => bot.ScriptOwnerKey is null)); + + if (handler is not null) { handler.Disconnect(); handler.Dispose(); } - if (cmdprompt != null) + if (cmdprompt is not null) { cmdprompt.Cancel(); cmdprompt = null; } - if (timeoutdetector != null) + if (timeoutdetector is not null) { timeoutdetector.Item2.Cancel(); timeoutdetector = null; } - if (client != null) + if (client is not null) client.Close(); } @@ -560,15 +903,18 @@ namespace MinecraftClient /// public void OnConnectionLost(ChatBot.DisconnectReason reason, string message) { + instance = null; + ConsoleIO.CancelAutocomplete(); handler.Dispose(); world.Clear(); + ClearKnownSigns(); - if (timeoutdetector != null) + if (timeoutdetector is not null) { - if (timeoutdetector != null && Thread.CurrentThread != timeoutdetector.Item1) + if (timeoutdetector is not null && Thread.CurrentThread != timeoutdetector.Item1) timeoutdetector.Item2.Cancel(); timeoutdetector = null; } @@ -617,12 +963,10 @@ namespace MinecraftClient } SentrySdk.EndSession(); - + if (!will_restart) { - ConsoleInteractive.ConsoleReader.StopReadThread(); - ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; + StopConsoleSession(); Program.HandleFailure(null, false, reason); } } @@ -634,7 +978,7 @@ namespace MinecraftClient private void ConsoleReaderOnMessageReceived(object? sender, string e) { - if (client.Client == null) + if (client.Client is null) return; if (client.Client.Connected) @@ -648,6 +992,14 @@ namespace MinecraftClient return; } + /// + /// Get the console message handler delegate for re-attaching after TUI mode. + /// + public EventHandler GetConsoleMessageHandler() + { + return ConsoleReaderOnMessageReceived; + } + /// /// Allows the user to send chat messages, commands, and leave the server. /// Process text from the MCC command prompt on the main thread. @@ -886,7 +1238,7 @@ namespace MinecraftClient get { int callingThreadId = Environment.CurrentManagedThreadId; - if (handler != null) + if (handler is not null) { return handler.GetNetMainThreadId() != callingThreadId; } @@ -916,9 +1268,9 @@ namespace MinecraftClient b.SetHandler(this); bots.Add(b); if (init) - DispatchBotEvent(bot => bot.Initialize(), new ChatBot[] { b }); - if (handler != null) - DispatchBotEvent(bot => bot.AfterGameJoined(), new ChatBot[] { b }); + DispatchBotEvent(bot => bot.Initialize(), [b]); + if (CanSendMessage) + DispatchBotEvent(bot => bot.AfterGameJoined(), [b]); } /// @@ -933,6 +1285,7 @@ namespace MinecraftClient } b.OnUnload(); + b.UnregisterChatBotCommands(); bots.RemoveAll(item => ReferenceEquals(item, b)); @@ -944,6 +1297,21 @@ namespace MinecraftClient } } + internal void UnloadBotsByScriptOwnerKey(string scriptOwnerKey) + { + if (InvokeRequired) + { + InvokeOnMainThread(() => UnloadBotsByScriptOwnerKey(scriptOwnerKey)); + return; + } + + foreach (ChatBot bot in GetLoadedChatBots()) + { + if (bot.ScriptOwnerKey == scriptOwnerKey) + BotUnLoad(bot); + } + } + /// /// Clear bots /// @@ -1031,6 +1399,7 @@ namespace MinecraftClient inventoryHandlingEnabled = false; inventoryHandlingRequested = false; inventories.Clear(); + ClearUnlockedRecipes(); } return true; } @@ -1087,6 +1456,15 @@ namespace MinecraftClient #region Getters: Retrieve data for use in other methods or ChatBots + /// + /// Gets the horizontal direction of the takeoff. + /// + /// Return direction of view + public Direction GetHorizontalFacing() + { + return DirectionExtensions.FromRotation(GetYaw()); + } + /// /// Get max length for chat messages /// @@ -1102,7 +1480,7 @@ namespace MinecraftClient /// public static char[] GetDisallowedChatCharacters() { - return new char[] { (char)167, (char)127 }; // Minecraft color code and ASCII code DEL + return [(char)167, (char)127]; // Minecraft color code and ASCII code DEL } /// @@ -1123,6 +1501,54 @@ namespace MinecraftClient return lastEnchantment; } + /// + /// Get all unlocked recipe book recipe identifiers. + /// + /// Unlocked recipe identifiers sorted alphabetically + public RecipeBookRecipeEntry[] GetUnlockedRecipes() + { + lock (recipeBookLock) + { + return unlockedRecipes.Values.OrderBy(static recipe => recipe.CommandId, StringComparer.Ordinal).ToArray(); + } + } + + /// + /// Get all achievements/advancements known to the client. + /// + /// Snapshot of all achievements + public Achievement[] GetAchievements() + { + lock (achievementsLock) + { + return [.. achievements.Values]; + } + } + + /// + /// Get only completed achievements/advancements. + /// + /// Snapshot of completed achievements + public Achievement[] GetUnlockedAchievements() + { + lock (achievementsLock) + { + return achievements.Values.Where(static a => a.IsCompleted).ToArray(); + } + } + + /// + /// Get only incomplete achievements/advancements. + /// + /// Snapshot of locked achievements + public Achievement[] GetLockedAchievements() + { + lock (achievementsLock) + { + return achievements.Values.Where(static a => !a.IsCompleted).ToArray(); + } + } + /// /// Get all Entities /// @@ -1169,6 +1595,22 @@ namespace MinecraftClient return GetInventory(0)!; } + /// + /// Get the currently active inventory if it supports recipe book crafting. + /// + /// Active recipe book inventory, or null if the active inventory does not support recipe book crafting + public Container? GetActiveRecipeBookInventory() + { + if (InvokeRequired) + return InvokeOnMainThread(() => GetActiveRecipeBookInventory()); + + if (inventories.Count == 0) + return null; + + Container activeInventory = inventories.MaxBy(static pair => pair.Key).Value; + return SupportsRecipeBook(activeInventory.Type) ? activeInventory : null; + } + /// /// Get a set of online player names /// @@ -1215,6 +1657,86 @@ namespace MinecraftClient return null; } + internal TabListSnapshot GetTabListSnapshot() + { + List<(Guid Uuid, string Name, string DisplayName, int Gamemode, int Ping, int TabListOrder, bool Listed)> players; + lock (onlinePlayers) + { + players = onlinePlayers + .Select(static pair => ( + pair.Key, + pair.Value.Name, + pair.Value.DisplayName ?? string.Empty, + pair.Value.Gamemode, + pair.Value.Ping, + pair.Value.TabListOrder, + pair.Value.Listed)) + .ToList(); + } + + Dictionary teamSnapshot; + lock (teams) + { + teamSnapshot = teams.ToDictionary( + static pair => pair.Key, + static pair => + { + var sourceTeam = pair.Value; + var copy = new PlayerTeam + { + Name = sourceTeam.Name, + DisplayName = sourceTeam.DisplayName, + AllowFriendlyFire = sourceTeam.AllowFriendlyFire, + SeeFriendlyInvisibles = sourceTeam.SeeFriendlyInvisibles, + NameTagVisibility = sourceTeam.NameTagVisibility, + CollisionRule = sourceTeam.CollisionRule, + Color = sourceTeam.Color, + Prefix = sourceTeam.Prefix, + Suffix = sourceTeam.Suffix + }; + + foreach (string member in sourceTeam.Members) + copy.Members.Add(member); + + return copy; + }, + StringComparer.OrdinalIgnoreCase); + } + + string header; + string footer; + lock (tabListHeaderFooterLock) + { + header = tabListHeader; + footer = tabListFooter; + } + + var entries = players + .Select(player => + { + PlayerTeam? team = teamSnapshot.Values.FirstOrDefault( + team => team.Members.Contains(player.Name)); + + string displayName = !string.IsNullOrWhiteSpace(player.DisplayName) + ? player.DisplayName + : TabListFormatter.FormatTeamMemberName(player.Name, team); + + return new TabListEntry( + player.Uuid, + player.Name, + displayName, + team?.Name ?? string.Empty, + !string.IsNullOrWhiteSpace(team?.DisplayName) ? team.DisplayName : team?.Name ?? string.Empty, + player.Gamemode, + player.Ping, + player.TabListOrder, + player.Listed); + }) + .ToList(); + + return new TabListSnapshot(header, footer, entries); + } + public PlayerKeyPair? GetPlayerKeyPair() { return playerKeyPair; @@ -1243,14 +1765,14 @@ namespace MinecraftClient { // 1-step path to the desired location without checking anything UpdateLocation(goal, goal); // Update yaw and pitch to look at next step - handler.SendLocationUpdate(goal, Movement.IsOnGround(world, goal), _yaw, _pitch); + handler.SendLocationUpdate(goal, Movement.IsOnGround(world, goal), false, _yaw, _pitch); return true; } else { - // Calculate path through pathfinding. Path contains a list of 1-block movement that will be divided into steps + pathTarget = null; path = Movement.CalculatePath(world, location, goal, allowUnsafe, maxOffset, minOffset, timeout ?? TimeSpan.FromSeconds(5)); - return path != null; + return path is not null; } } } @@ -1264,6 +1786,12 @@ namespace MinecraftClient if (String.IsNullOrEmpty(text)) return; + if (!CanSendMessage) + { + Log.Warn(Translations.mcc_send_text_not_connected); + return; + } + int maxLength = handler.GetMaxChatMessageLength(); lock (chatQueue) @@ -1294,6 +1822,14 @@ namespace MinecraftClient } } + public bool SendCustomClickAction(string id, Dictionary? payload) + { + if (InvokeRequired) + return InvokeOnMainThread(() => SendCustomClickAction(id, payload)); + + return handler.SendCustomClickAction(id, payload); + } + /// /// Allow to respawn after death /// @@ -1386,6 +1922,72 @@ namespace MinecraftClient return handler.SendPluginChannelPacket(channel, data); } + public Item? GetHeldBook(BookHand hand = BookHand.Main) + { + if (InvokeRequired) + return InvokeOnMainThread(() => GetHeldBook(hand)); + + if (!inventoryHandlingEnabled || !inventories.TryGetValue(0, out Container? inventory)) + return null; + + int slot = hand == BookHand.Off ? 45 : 36 + CurrentSlot; + return inventory.Items.TryGetValue(slot, out Item? item) ? item : null; + } + + public bool TryGetHeldBookContent(out BookContent content, BookHand hand = BookHand.Main) + { + if (InvokeRequired) + { + (bool ok, BookContent value) = InvokeOnMainThread(() => + { + bool ok = TryGetHeldBookContent(out BookContent value, hand); + return (ok, value); + }); + content = value; + return ok; + } + + return BookContentHelper.TryRead(GetHeldBook(hand), out content); + } + + public bool SendBookEdit(IReadOnlyList pages, string? title = null) + { + if (InvokeRequired) + return InvokeOnMainThread(() => SendBookEdit(pages, title)); + + Item? currentBook = GetHeldBook(BookHand.Main); + if (!BookContentHelper.IsWritableBook(currentBook)) + return false; + + IReadOnlyList normalizedPages = BookContentHelper.NormalizePages(pages); + bool sent = handler.SendEditBook(currentBook!, normalizedPages, title, username, CurrentSlot); + if (sent && GetProtocolVersion() < Protocol18Handler.MC_1_17_Version) + SetHeldBook(BookHand.Main, CreateLocalBookResult(currentBook!, normalizedPages, title)); + + return sent; + } + + private Item CreateLocalBookResult(Item currentBook, IReadOnlyList pages, string? title) + { + return title is null + ? BookContentHelper.CreateWritablePayload(currentBook, pages) + : BookContentHelper.CreateWrittenPayload( + currentBook, + pages, + title, + username, + encodePagesAsJson: GetProtocolVersion() < Protocol18Handler.MC_1_9_Version); + } + + private void SetHeldBook(BookHand hand, Item item) + { + if (!inventoryHandlingEnabled || !inventories.TryGetValue(0, out Container? inventory)) + return; + + int slot = hand == BookHand.Off ? 45 : 36 + CurrentSlot; + inventory.Items[slot] = item; + } + /// /// Send the Entity Action packet with the Specified ID /// @@ -1432,8 +2034,8 @@ namespace MinecraftClient if (item.Count <= spaceLeft) { // Can fit into the stack - item.Count = 0; curItem.Count += item.Count; + item.Count = 0; changedSlots.Add(new Tuple((short)curId, curItem)); changedSlots.Add(new Tuple((short)slotId, null)); @@ -1462,7 +2064,7 @@ namespace MinecraftClient /// Record changes private static void StoreInNewSlot(Container inventory, Item item, int slotId, int newSlotId, List> changedSlots) { - Item newItem = new(item.Type, item.Count, item.NBT); + Item newItem = item.CloneWithCount(item.Count); inventory.Items[newSlotId] = newItem; inventory.Items.Remove(slotId); @@ -1470,6 +2072,128 @@ namespace MinecraftClient changedSlots.Add(new Tuple((short)slotId, null)); } + private static bool IsServerManagedOutputSlot(Container inventory, int slotId) + { + return (inventory.Type, slotId) switch + { + (ContainerType.PlayerInventory, 0) => true, + (ContainerType.Crafting, 0) => true, + (ContainerType.Anvil, 2) => true, + (ContainerType.BlastFurnace, 2) => true, + (ContainerType.Furnace, 2) => true, + (ContainerType.Smoker, 2) => true, + (ContainerType.Grindstone, 2) => true, + (ContainerType.Cartography, 2) => true, + (ContainerType.Merchant, 2) => true, + (ContainerType.Stonecutter, 1) => true, + (ContainerType.Loom, 3) => true, + (ContainerType.SmightingTable, 3) => true, + _ => false + }; + } + + private static bool TryGetMirroredPlayerInventoryRange(Container inventory, out int firstWindowSlot, out int lastWindowSlot) + { + firstWindowSlot = -1; + lastWindowSlot = -1; + + if (inventory.Type is ContainerType.PlayerInventory or ContainerType.Unknown) + return false; + + const int mirroredPlayerInventorySlotCount = 36; + int slotCount = inventory.Type.SlotCount(); + if (slotCount <= mirroredPlayerInventorySlotCount) + return false; + + firstWindowSlot = slotCount - mirroredPlayerInventorySlotCount; + lastWindowSlot = slotCount - 1; + return true; + } + + private static bool AreSameInventorySlot(Item? left, Item? right) + { + if (left is null || left.IsEmpty) + return right is null || right.IsEmpty; + if (right is null || right.IsEmpty) + return false; + + return left.Type == right.Type + && left.Count == right.Count + && left.Data == right.Data + && ReferenceEquals(left.NBT, right.NBT) + && ReferenceEquals(left.Components, right.Components); + } + + private bool SetPlayerInventorySlot(int playerInventorySlot, Item? item) + { + if (!inventories.TryGetValue(0, out Container? playerInventory)) + return false; + + if (item is null || item.IsEmpty) + return playerInventory.Items.Remove(playerInventorySlot); + + Item itemClone = item.CloneWithCount(item.Count); + + playerInventory.Items.TryGetValue(playerInventorySlot, out Item? previousItem); + if (AreSameInventorySlot(previousItem, itemClone)) + return false; + + playerInventory.Items[playerInventorySlot] = itemClone; + + return true; + } + + private bool SyncPlayerInventorySlotsFromWindow(Container? inventory) + { + if (inventory is null) + return false; + + if (!inventoriesWithFullContents.Contains(inventory.ID)) + return false; + + if (!TryGetMirroredPlayerInventoryRange(inventory, out int firstWindowSlot, out int lastWindowSlot)) + return false; + + if (!inventories.TryGetValue(0, out Container? playerInventory)) + return false; + + const int firstPlayerInventorySlot = 9; + const int lastPlayerInventorySlot = firstPlayerInventorySlot + 36 - 1; + Dictionary mirroredItems = new(); + + for (int windowSlot = firstWindowSlot; windowSlot <= lastWindowSlot; windowSlot++) + { + if (!inventory.Items.TryGetValue(windowSlot, out Item? item) || item.IsEmpty) + continue; + + int playerInventorySlot = windowSlot - firstWindowSlot + firstPlayerInventorySlot; + mirroredItems[playerInventorySlot] = item.CloneWithCount(item.Count); + } + + bool changed = false; + for (int playerInventorySlot = firstPlayerInventorySlot; playerInventorySlot <= lastPlayerInventorySlot; playerInventorySlot++) + { + playerInventory.Items.TryGetValue(playerInventorySlot, out Item? previousItem); + mirroredItems.TryGetValue(playerInventorySlot, out Item? mirroredItem); + if (AreSameInventorySlot(previousItem, mirroredItem)) + continue; + + changed = true; + break; + } + + if (!changed) + return false; + + for (int playerInventorySlot = firstPlayerInventorySlot; playerInventorySlot <= lastPlayerInventorySlot; playerInventorySlot++) + playerInventory.Items.Remove(playerInventorySlot); + + foreach ((int playerInventorySlot, Item item) in mirroredItems) + playerInventory.Items[playerInventorySlot] = item; + + return changed; + } + /// /// Click a slot in the specified window /// @@ -1488,7 +2212,7 @@ namespace MinecraftClient // Update our inventory base on action type Container inventory = GetInventory(windowId)!; Container playerInventory = GetInventory(0)!; - if (inventory != null) + if (inventory is not null) { switch (action) { @@ -1496,8 +2220,9 @@ namespace MinecraftClient // Check if cursor have item (slot -1) if (playerInventory.Items.ContainsKey(-1)) { - // When item on cursor and clicking slot 0, nothing will happen - if (slotId == 0) break; + // Result slots are server-managed and cannot accept cursor items directly. + if (IsServerManagedOutputSlot(inventory, slotId)) + break; // Check target slot also have item? if (inventory.Items.ContainsKey(slotId)) @@ -1533,6 +2258,10 @@ namespace MinecraftClient playerInventory.Items.Remove(-1); } + // Clean up cursor item if count reached zero + if (playerInventory.Items.TryGetValue(-1, out Item? cursorAfterLeft) && cursorAfterLeft.IsEmpty) + playerInventory.Items.Remove(-1); + if (inventory.Items.ContainsKey(slotId)) changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); else @@ -1543,8 +2272,8 @@ namespace MinecraftClient // Check target slot have item? if (inventory.Items.ContainsKey(slotId)) { - // When taking item from slot 0, server will update us - if (slotId == 0) break; + if (IsServerManagedOutputSlot(inventory, slotId)) + break; // Put target slot item to cursor playerInventory.Items[-1] = inventory.Items[slotId]; @@ -1558,8 +2287,8 @@ namespace MinecraftClient // Check if cursor have item (slot -1) if (playerInventory.Items.ContainsKey(-1)) { - // When item on cursor and clicking slot 0, nothing will happen - if (slotId == 0) break; + if (IsServerManagedOutputSlot(inventory, slotId)) + break; // Check target slot have item? if (inventory.Items.ContainsKey(slotId)) @@ -1585,20 +2314,22 @@ namespace MinecraftClient { // Drop 1 item count from cursor Item itemTmp = playerInventory.Items[-1]; - Item itemClone = new(itemTmp.Type, 1, itemTmp.NBT); + Item itemClone = itemTmp.CloneWithCount(1); inventory.Items[slotId] = itemClone; playerInventory.Items[-1].Count--; } + + // Clean up cursor item if count reached zero + if (playerInventory.Items.TryGetValue(-1, out Item? cursorItem) && cursorItem.IsEmpty) + playerInventory.Items.Remove(-1); } else { // Check target slot have item? if (inventory.Items.ContainsKey(slotId)) { - if (slotId == 0) + if (IsServerManagedOutputSlot(inventory, slotId)) { - // no matter how many item in slot 0, only 1 will be taken out - // Also server will update us break; } if (inventory.Items[slotId].Count == 1) @@ -1614,14 +2345,14 @@ namespace MinecraftClient { // Can be evenly divided Item itemTmp = inventory.Items[slotId]; - playerInventory.Items[-1] = new Item(itemTmp.Type, itemTmp.Count / 2, itemTmp.NBT); + playerInventory.Items[-1] = itemTmp.CloneWithCount(itemTmp.Count / 2); inventory.Items[slotId].Count = itemTmp.Count / 2; } else { // Cannot be evenly divided. item count on cursor is always larger than item on inventory Item itemTmp = inventory.Items[slotId]; - playerInventory.Items[-1] = new Item(itemTmp.Type, (itemTmp.Count + 1) / 2, itemTmp.NBT); + playerInventory.Items[-1] = itemTmp.CloneWithCount((itemTmp.Count + 1) / 2); inventory.Items[slotId].Count = (itemTmp.Count - 1) / 2; } } @@ -1634,8 +2365,9 @@ namespace MinecraftClient break; case WindowActionType.ShiftClick: case WindowActionType.ShiftRightClick: - if (slotId == 0) break; - if (item != null) + if (IsServerManagedOutputSlot(inventory, slotId)) + break; + if (item is not null) { /* Target slot have item */ @@ -1655,7 +2387,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 9; } - else if (item != null && false /* Check if wearable */) + else if (item is not null && false /* Check if wearable */) { lower2upper = true; // upperStartSlot = ?; @@ -1791,7 +2523,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 1; } - else if (item != null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot || + else if (item is not null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot || item.Type == ItemType.Emerald || item.Type == ItemType.Diamond || item.Type == ItemType.GoldIngot || item.Type == ItemType.IronIngot) && !inventory.Items.ContainsKey(0)) { @@ -1824,7 +2556,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 3; } - else if (item != null && false /* Check if it can be burned */) + else if (item is not null && false /* Check if it can be burned */) { lower2upper = true; upperStartSlot = 0; @@ -1851,7 +2583,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 5; } - else if (item != null && item.Type == ItemType.BlazePowder) + else if (item is not null && item.Type == ItemType.BlazePowder) { lower2upper = true; if (!inventory.Items.ContainsKey(4) || inventory.Items[4].Count < 64) @@ -1859,12 +2591,12 @@ namespace MinecraftClient else upperStartSlot = upperEndSlot = 3; } - else if (item != null && false /* Check if it can be used for alchemy */) + else if (item is not null && false /* Check if it can be used for alchemy */) { lower2upper = true; upperStartSlot = upperEndSlot = 3; } - else if (item != null && (item.Type == ItemType.Potion || item.Type == ItemType.GlassBottle)) + else if (item is not null && (item.Type == ItemType.Potion || item.Type == ItemType.GlassBottle)) { lower2upper = true; upperStartSlot = 0; @@ -1906,7 +2638,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 5; } - else if (item != null && item.Type == ItemType.LapisLazuli) + else if (item is not null && item.Type == ItemType.LapisLazuli) { lower2upper = true; upperStartSlot = upperEndSlot = 1; @@ -1926,7 +2658,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 3; } - else if (item != null && false /* Check */) + else if (item is not null && false /* Check */) { lower2upper = true; upperStartSlot = 0; @@ -1963,7 +2695,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 4; } - else if (item != null && false /* Check for availability for staining */) + else if (item is not null && false /* Check for availability for staining */) { lower2upper = true; // upperStartSlot = ?; @@ -1992,7 +2724,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 3; } - else if (item != null && false /* Check if it is available for trading */) + else if (item is not null && false /* Check if it is available for trading */) { lower2upper = true; upperStartSlot = 0; @@ -2021,12 +2753,12 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 3; } - else if (item != null && item.Type == ItemType.FilledMap) + else if (item is not null && item.Type == ItemType.FilledMap) { lower2upper = true; upperStartSlot = upperEndSlot = 0; } - else if (item != null && item.Type == ItemType.Map) + else if (item is not null && item.Type == ItemType.Map) { lower2upper = true; upperStartSlot = upperEndSlot = 1; @@ -2054,7 +2786,7 @@ namespace MinecraftClient upper2backpack = true; lowerStartSlot = 2; } - else if (item != null && false /* Check if it is available for stone cutteing */) + else if (item is not null && false /* Check if it is available for stone cutteing */) { lower2upper = true; upperStartSlot = 0; @@ -2175,6 +2907,11 @@ namespace MinecraftClient changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } + if (item!.Count <= 0 && inventory.Items.ContainsKey(slotId)) + { + inventory.Items.Remove(slotId); + changedSlots.Add(new Tuple((short)slotId, null)); + } } break; case WindowActionType.DropItem: @@ -2198,6 +2935,8 @@ namespace MinecraftClient } } + SyncPlayerInventorySlotsFromWindow(inventory); + return handler.SendWindowAction(windowId, slotId, action, item, changedSlots, inventories[windowId].StateID); } @@ -2212,7 +2951,16 @@ namespace MinecraftClient /// TRUE if item given successfully public bool DoCreativeGive(int slot, ItemType itemType, int count, Dictionary? nbt = null) { - return InvokeOnMainThread(() => handler.SendCreativeInventoryAction(slot, itemType, count, nbt)); + return InvokeOnMainThread(() => + { + if (!handler.SendCreativeInventoryAction(slot, itemType, count, nbt)) + return false; + + if (slot is >= 1 and <= 45) + SetPlayerInventorySlot(slot, new Item(itemType, count, nbt)); + + return true; + }); } /// @@ -2239,7 +2987,10 @@ namespace MinecraftClient if (inventories.ContainsKey(windowId)) { if (windowId != 0) + { inventories.Remove(windowId); + inventoriesWithFullContents.Remove(windowId); + } bool result = handler.SendCloseWindow(windowId); DispatchBotEvent(bot => bot.OnInventoryClose(windowId)); return result; @@ -2260,7 +3011,9 @@ namespace MinecraftClient return InvokeOnMainThread(ClearInventories); inventories.Clear(); + inventoriesWithFullContents.Clear(); inventories[0] = new Container(0, ContainerType.PlayerInventory, "Player Inventory"); + ClearUnlockedRecipes(); return true; } @@ -2278,25 +3031,20 @@ namespace MinecraftClient if (entities.ContainsKey(entityID)) { - switch (type) + return type switch { - case InteractType.Interact: - return handler.SendInteractEntity(entityID, (int)type, (int)hand); - - case InteractType.InteractAt: - return handler.SendInteractEntity( - EntityID: entityID, - type: (int)type, - X: (float)entities[entityID].Location.X, - Y: (float)entities[entityID].Location.Y, - Z: (float)entities[entityID].Location.Z, - hand: (int)hand); - - default: - return handler.SendInteractEntity(entityID, (int)type); - } + InteractType.Interact => handler.SendInteractEntity(entityID, (int)type, (int)hand), + InteractType.InteractAt => handler.SendInteractEntity( + EntityID: entityID, + type: (int)type, + X: (float)entities[entityID].Location.X, + Y: (float)entities[entityID].Location.Y, + Z: (float)entities[entityID].Location.Z, + hand: (int)hand), + _ => handler.SendInteractEntity(entityID, (int)type), + }; } - + return false; } @@ -2305,32 +3053,43 @@ namespace MinecraftClient /// /// Location to place block to /// Block face (e.g. Direction.Down when clicking on the block below to place this block) + /// Also look at the block before interacting /// TRUE if successfully placed - public bool PlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand) + public bool PlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand, bool lookAtBlock = false) { - return InvokeOnMainThread(() => handler.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId++)); + return InvokeOnMainThread(() => + { + if (lookAtBlock) + { + UpdateLocation(GetCurrentLocation(), location.ToCenter()); + handler.SendLocationUpdate(GetCurrentLocation(), Movement.IsOnGround(world, GetCurrentLocation()), false, _yaw, _pitch); + } + return handler.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId++); + }); } + /// /// Attempt to dig a block at the specified location /// /// Location of block to dig /// Also perform the "arm swing" animation /// Also look at the block before digging - public bool DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true, double duration = 0) + public bool DigBlock(Location location, Direction blockFace, bool swingArms = true, bool lookAtBlock = true, + double duration = 0, MiningCalculator.MiningOptions? miningOptions = null) { + // TODO select best face from current player location + if (!GetTerrainEnabled()) return false; if (InvokeRequired) - return InvokeOnMainThread(() => DigBlock(location, swingArms, lookAtBlock, duration)); - - // TODO select best face from current player location - Direction blockFace = Direction.Down; + return InvokeOnMainThread(() => DigBlock(location, blockFace, swingArms, lookAtBlock, duration, + miningOptions)); lock (DigLock) { - if (RemainingDiggingTime > 0 && LastDigPosition != null) + if (RemainingDiggingTime > 0 && LastDigPosition is not null) { handler.SendPlayerDigging(1, LastDigPosition.Item1, LastDigPosition.Item2, sequenceId++); Log.Info(string.Format(Translations.cmd_dig_cancel, LastDigPosition.Item1)); @@ -2340,11 +3099,23 @@ namespace MinecraftClient if (lookAtBlock) UpdateLocation(GetCurrentLocation(), location); + // Auto-compute dig duration for survival/adventure mode when not explicitly supplied + bool autoComputedDuration = false; + if (duration <= 0 && protocolversion >= Protocol18Handler.MC_1_8_Version + && gamemode is 0 or 2) // Survival or Adventure + { + autoComputedDuration = true; + duration = ComputeAutoDigDuration(location, miningOptions); + } + // Send dig start and dig end, will need to wait for server response to know dig result // See https://wiki.vg/How_to_Write_a_Client#Digging for more details bool result = handler.SendPlayerDigging(0, location, blockFace, sequenceId++) && (!swingArms || DoAnimation((int)Hand.MainHand)); + if (autoComputedDuration && duration <= 0) + return result; + if (duration <= 0) result &= handler.SendPlayerDigging(2, location, blockFace, sequenceId++); else @@ -2357,6 +3128,85 @@ namespace MinecraftClient } } + /// + /// Compute the automatic dig duration in seconds for a block, based on held tool, + /// enchantments, effects, attributes, and player state. + /// Returns 0 for instant-break blocks. + /// + private double ComputeAutoDigDuration(Location location, MiningCalculator.MiningOptions? miningOptions = null) + { + try + { + Block block = world.GetBlock(location); + Material blockMaterial = block.Type; + + if (blockMaterial == Material.Air) + return 0; + + // Get held item from player inventory + Item? heldItem = null; + Item? helmetItem = null; + if (inventories.TryGetValue(0, out var playerInv)) + { + int hotbarSlot = 36 + CurrentSlot; // Hotbar slots are 36-44 + playerInv.Items.TryGetValue(hotbarSlot, out heldItem); + playerInv.Items.TryGetValue(5, out helmetItem); // Slot 5 = helmet + } + + int ticks = MiningCalculator.ComputeDigTicks( + blockMaterial, + heldItem, + helmetItem, + playerEffects, + playerAttributes, + playerPhysics.InWater, + playerPhysics.OnGround, + protocolversion, + miningOptions); + + if (ticks < 0) + return -1; + + if (ticks == 0) + return 0; + + return (double)ticks / Settings.ClientTicksPerSecond; + } + catch + { + return ComputeConservativeAutoDigDuration(location); + } + } + + private double ComputeConservativeAutoDigDuration(Location location) + { + try + { + Block block = world.GetBlock(location); + if (block.Type == Material.Air) + return 0; + + int ticks = MiningCalculator.ComputeDigTicks( + blockMaterial: block.Type, + heldItem: null, + helmetItem: null, + effects: new(), + playerAttributes: new(), + isUnderwater: playerPhysics.InWater, + isOnGround: playerPhysics.OnGround, + protocolVersion: protocolversion); + + if (ticks < 0) + return -1; + + return ticks == 0 ? 1.0 : (double)ticks / Settings.ClientTicksPerSecond; + } + catch + { + return 1.0; + } + } + /// /// Change active slot in the player inventory /// @@ -2374,6 +3224,41 @@ namespace MinecraftClient return handler.SendHeldItemChange(slot); } + /// + /// Drop the currently selected hotbar item like a real player pressing Q or Ctrl+Q. + /// + /// TRUE to drop the whole stack, FALSE to drop one item + /// TRUE if the packet was sent + public bool DropSelectedItem(bool dropEntireStack) + { + if (InvokeRequired) + return InvokeOnMainThread(() => DropSelectedItem(dropEntireStack)); + + Location actionLocation = GetCurrentLocation().ToFloor(); + int status = dropEntireStack ? 3 : 4; + bool sent = handler.SendPlayerDigging(status, actionLocation, Direction.Down, sequenceId++); + if (sent) + ApplySelectedItemDropPrediction(dropEntireStack); + + return sent; + } + + private void ApplySelectedItemDropPrediction(bool dropEntireStack) + { + if (!inventories.TryGetValue(0, out Container? playerInventory)) + return; + + int selectedSlotId = CurrentSlot + 36; + if (!playerInventory.Items.TryGetValue(selectedSlotId, out Item? heldItem) || heldItem.IsEmpty) + return; + + if (dropEntireStack || heldItem.Count <= 1) + playerInventory.Items.Remove(selectedSlotId); + else heldItem.Count--; + + DispatchBotEvent(bot => bot.OnInventoryUpdate(0)); + } + /// /// Update sign text /// @@ -2443,7 +3328,7 @@ namespace MinecraftClient return false; } } - + /// /// Send the server a command to type in the item name in the Anvil inventory when it's open. /// @@ -2455,9 +3340,34 @@ namespace MinecraftClient if (inventories.Values.ToList().Last().Type != ContainerType.Anvil) return false; - + return handler.SendRenameItem(itemName); } + + /// + /// Send a recipe book craft request for the currently active crafting inventory. + /// + /// Recipe identifier to craft + /// True to craft as many items as possible + /// True if the packet was sent + public bool SendPlaceRecipe(string recipeId, bool makeAll) + { + if (InvokeRequired) + return InvokeOnMainThread(() => SendPlaceRecipe(recipeId, makeAll)); + + if (protocolversion < Protocol18Handler.MC_1_13_Version) + return false; + + Container? activeInventory = GetActiveRecipeBookInventory(); + if (activeInventory is null) + return false; + + string normalizedRecipeId = NormalizeRecipeArgument(recipeId, protocolversion); + if (normalizedRecipeId.Length == 0) + return false; + + return handler.SendPlaceRecipe(activeInventory.ID, normalizedRecipeId, makeAll); + } #endregion #region Event handlers: An event occurs on the Server @@ -2475,7 +3385,7 @@ namespace MinecraftClient { ChatBot[] selectedBots; - if (botList != null) + if (botList is not null) { selectedBots = botList.ToArray(); } @@ -2522,12 +3432,42 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnNetworkPacket(packetID, packetData, isLogin, isInbound)); } + public void OnDialogRegistryData(int protocolId, string resourceId, DialogDefinition dialog) + { + Dialogs.StoreRegistryDialog(protocolId, resourceId, dialog); + } + + public void OnDialogShown(DialogDefinition dialog, DialogPhase phase) + { + var instance = Dialogs.Show(dialog, phase); + if (!Tui.DialogTuiHost.TryOpen(this, instance, force: phase == DialogPhase.Configuration)) + ConsoleIO.WriteLineFormatted(DialogFormatter.Render(instance), acceptnewlines: true); + } + + public void OnDialogRegistryReferenceShown(int protocolId, DialogPhase phase) + { + var instance = Dialogs.ShowRegistryReference(protocolId, phase); + if (!Tui.DialogTuiHost.TryOpen(this, instance, force: phase == DialogPhase.Configuration)) + ConsoleIO.WriteLineFormatted(DialogFormatter.Render(instance), acceptnewlines: true); + } + + public void OnDialogCleared() + { + Dialogs.Clear(); + Tui.DialogTuiHost.CloseCurrent(); + } + + public void OnServerLinksUpdated(IReadOnlyList links) + { + Dialogs.SetServerLinks(links); + } + /// /// Called when a server was successfully joined /// public void OnGameJoined(bool isOnlineMode) { - if (protocolversion < Protocol18Handler.MC_1_19_3_Version || playerKeyPair == null || !isOnlineMode) + if (protocolversion < Protocol18Handler.MC_1_19_3_Version || playerKeyPair is null || !isOnlineMode) SetCanSendMessage(true); else SetCanSendMessage(false); @@ -2536,7 +3476,8 @@ namespace MinecraftClient if (!String.IsNullOrWhiteSpace(bandString)) handler.SendBrandInfo(bandString.Trim()); - if (Config.MCSettings.Enabled) + // 1.20.2+ expects ClientInformation during configuration; older servers still want it here. + if (Config.MCSettings.Enabled && protocolversion < Protocol18Handler.MC_1_20_2_Version) handler.SendClientSettings( Config.MCSettings.Locale, Config.MCSettings.RenderDistance, @@ -2547,7 +3488,7 @@ namespace MinecraftClient (byte)Config.MCSettings.MainHand); if (protocolversion >= Protocol18Handler.MC_1_19_3_Version - && playerKeyPair != null && isOnlineMode) + && playerKeyPair is not null && isOnlineMode) handler.SendPlayerSession(playerKeyPair); if (inventoryHandlingRequested) @@ -2584,17 +3525,99 @@ namespace MinecraftClient } entities.Clear(); + ClearKnownSigns(); ClearInventories(); DispatchBotEvent(bot => bot.OnRespawn()); } + /// + /// Drive the physics engine input based on the current A* path. + /// Converts discrete waypoint pathfinding into continuous movement input. + /// + private void UpdatePathfindingInput() + { + physicsInput.Reset(); + + // Still heading toward a target (even if path queue is empty) + if (pathTarget is not null && ReachedWaypoint(pathTarget.Value)) + { + // Arrived at current waypoint — advance to next, or finish + if (path is not null && path.Count > 0) + { + pathTarget = path.Dequeue(); + if (Config.Main.Advanced.MoveHeadWhileWalking) + UpdateLocation(location, pathTarget.Value + new Location(0, 1, 0)); + } + else + { + pathTarget = null; + path = null; + } + } + + // Need a first target from a fresh path + if (pathTarget is null && path is not null && path.Count > 0) + { + pathTarget = path.Dequeue(); + if (Config.Main.Advanced.MoveHeadWhileWalking) + UpdateLocation(location, pathTarget.Value + new Location(0, 1, 0)); + } + + if (pathTarget is not null) + { + SetInputToward(pathTarget.Value); + } + } + + /// + /// Check if the player has approximately reached a waypoint. + /// + private bool ReachedWaypoint(Location target) + { + double dx = target.X - location.X; + double dz = target.Z - location.Z; + return dx * dx + dz * dz < 0.25; // within ~0.5 blocks horizontally + } + + /// + /// Set movement input to walk toward a target location. + /// Calculates the yaw needed and sets Forward + Sprint. + /// + private void SetInputToward(Location target) + { + double dx = target.X - location.X; + double dz = target.Z - location.Z; + double dy = target.Y - location.Y; + double distSqr = dx * dx + dz * dz; + + if (distSqr < 0.01) return; // Close enough horizontally + + // Calculate yaw to face target + float targetYaw = (float)(-Math.Atan2(dx, dz) / Math.PI * 180.0); + if (targetYaw < 0) targetYaw += 360; + playerPhysics.Yaw = targetYaw; + playerYaw = targetYaw; + + physicsInput.Forward = true; + + // Jump if target is above and we're on ground + if (dy > 0.5 && playerPhysics.OnGround) + physicsInput.Jump = true; + + // Map MovementSpeed setting: 1=sneak, 2-4=walk, 5=sprint + if (Config.Main.Advanced.MovementSpeed >= 5) + physicsInput.Sprint = true; + else if (Config.Main.Advanced.MovementSpeed <= 1) + physicsInput.Sneak = true; + } + /// /// Check if the client is currently processing a Movement. /// /// true if a movement is currently handled public bool ClientIsMoving() { - return terrainAndMovementsEnabled && locationReceived && ((steps != null && steps.Count > 0) || (path != null && path.Count > 0)); + return terrainAndMovementsEnabled && locationReceived && path is not null && path.Count > 0; } /// @@ -2603,7 +3626,7 @@ namespace MinecraftClient /// Current goal of movement. Location.Zero if not set. public Location GetCurrentMovementGoal() { - return (ClientIsMoving() || path == null) ? Location.Zero : path.Last(); + return (ClientIsMoving() || path is null) ? Location.Zero : path.Last(); } /// @@ -2656,6 +3679,12 @@ namespace MinecraftClient } else this.location = location; locationReceived = true; + + // Sync physics engine position + if (physicsInitialized) + { + playerPhysics.Teleport(this.location.X, this.location.Y, this.location.Z); + } } } @@ -2673,6 +3702,21 @@ namespace MinecraftClient UpdateLocation(location, false); } + /// + /// Send the current player position and look angles to the server. + /// + /// TRUE if the update packet was sent + public bool SendLocationUpdate() + { + if (InvokeRequired) + return InvokeOnMainThread(SendLocationUpdate); + + Location current = GetCurrentLocation(); + bool onGround = physicsInitialized ? playerPhysics.OnGround : Movement.IsOnGround(world, current); + bool horizontalCollision = physicsInitialized && playerPhysics.HorizontalCollision; + return handler.SendLocationUpdate(current, onGround, horizontalCollision, _yaw, _pitch); + } + /// /// Called when the server sends a new player location, /// or if a ChatBot whishes to update the player's location. @@ -2750,7 +3794,7 @@ namespace MinecraftClient if (!Config.Signature.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!) return; messageText = ChatParser.ParseSignedChat(message, links); - + if (message.isSystemChat) { if (Config.Signature.MarkSystemMessage) @@ -2760,7 +3804,7 @@ namespace MinecraftClient { if ((bool)message.isSignatureLegal!) { - if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) + if (Config.Signature.ShowModifiedChat && message.unsignedContent is not null) { if (Config.Signature.MarkModifiedMsg) color = "§6▌§r"; // Background Yellow @@ -2811,6 +3855,7 @@ namespace MinecraftClient /// Inventory ID public void OnInventoryOpen(int inventoryID, Container inventory) { + inventoriesWithFullContents.Remove(inventoryID); inventories[inventoryID] = inventory; if (inventoryID != 0) @@ -2818,6 +3863,13 @@ namespace MinecraftClient Log.Info(string.Format(Translations.extra_inventory_open, inventoryID, inventory.Title)); Log.Info(Translations.extra_inventory_interact); DispatchBotEvent(bot => bot.OnInventoryOpen(inventoryID)); + + if (ConsoleIO.Backend is Tui.TuiConsoleBackend + && Tui.ContainerViewBase.HasTuiSupport(inventory.Type) + && Tui.InventoryTuiHost.CanLaunch) + { + Tui.InventoryTuiHost.Launch(this, inventoryID); + } } } @@ -2830,9 +3882,15 @@ namespace MinecraftClient if (inventories.ContainsKey(inventoryID)) { if (inventoryID == 0) + { inventories[0].Items.Clear(); // Don't delete player inventory + inventoriesWithFullContents.Clear(); + } else + { inventories.Remove(inventoryID); + inventoriesWithFullContents.Remove(inventoryID); + } } if (inventoryID != 0) @@ -2840,6 +3898,8 @@ namespace MinecraftClient Log.Info(string.Format(Translations.extra_inventory_close, inventoryID)); DispatchBotEvent(bot => bot.OnInventoryClose(inventoryID)); } + + Tui.InventoryTuiHost.NotifyInventoryClosed(inventoryID); } /// @@ -2869,27 +3929,27 @@ namespace MinecraftClient // We got the last property for enchantment if (propertyId == 9 && propertyValue != -1) { - short topEnchantmentLevelRequirement = inventory.Properties[0]; - short middleEnchantmentLevelRequirement = inventory.Properties[1]; - short bottomEnchantmentLevelRequirement = inventory.Properties[2]; + var topEnchantmentLevelRequirement = inventory.Properties[0]; + var middleEnchantmentLevelRequirement = inventory.Properties[1]; + var bottomEnchantmentLevelRequirement = inventory.Properties[2]; - Enchantment topEnchantment = EnchantmentMapping.GetEnchantmentById( + var topEnchantment = EnchantmentMapping.GetEnchantmentById( GetProtocolVersion(), inventory.Properties[4]); - Enchantment middleEnchantment = EnchantmentMapping.GetEnchantmentById( + var middleEnchantment = EnchantmentMapping.GetEnchantmentById( GetProtocolVersion(), inventory.Properties[5]); - Enchantment bottomEnchantment = EnchantmentMapping.GetEnchantmentById( + var bottomEnchantment = EnchantmentMapping.GetEnchantmentById( GetProtocolVersion(), inventory.Properties[6]); - short topEnchantmentLevel = inventory.Properties[7]; - short middleEnchantmentLevel = inventory.Properties[8]; - short bottomEnchantmentLevel = inventory.Properties[9]; + var topEnchantmentLevel = inventory.Properties[7]; + var middleEnchantmentLevel = inventory.Properties[8]; + var bottomEnchantmentLevel = inventory.Properties[9]; - StringBuilder sb = new(); + var sb = new StringBuilder(); sb.AppendLine(Translations.Enchantment_enchantments_available + ":"); @@ -2956,8 +4016,16 @@ namespace MinecraftClient { if (inventories.ContainsKey(inventoryID)) { + // Filter out empty items (Count=0 or Air) that some servers may send + foreach (int key in itemList.Where(slot => slot.Value.IsEmpty).Select(slot => slot.Key).ToList()) + itemList.Remove(key); + inventories[inventoryID].Items = itemList; inventories[inventoryID].StateID = stateId; + inventoriesWithFullContents.Add(inventoryID); + bool playerInventoryChanged = SyncPlayerInventorySlotsFromWindow(inventories[inventoryID]); + if (playerInventoryChanged) + DispatchBotEvent(bot => bot.OnInventoryUpdate(0)); DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); } } @@ -2982,7 +4050,7 @@ namespace MinecraftClient inventoryID = 0; // Prevent key not found for some bots relied to this event if (inventories.ContainsKey(0)) { - if (item != null) + if (item is not null && !item.IsEmpty) inventories[0].Items[-1] = item; else inventories[0].Items.Remove(-1); @@ -2992,12 +4060,15 @@ namespace MinecraftClient { if (inventories.ContainsKey(inventoryID)) { - if (item == null || item.IsEmpty) + if (item is null || item.IsEmpty) { if (inventories[inventoryID].Items.ContainsKey(slotID)) inventories[inventoryID].Items.Remove(slotID); } else inventories[inventoryID].Items[slotID] = item; + + if (SyncPlayerInventorySlotsFromWindow(inventories[inventoryID])) + DispatchBotEvent(bot => bot.OnInventoryUpdate(0)); } } DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); @@ -3103,6 +4174,17 @@ namespace MinecraftClient } } + public void OnBookOpen(int hand) + { + if (InvokeRequired) + { + InvokeOnMainThread(() => OnBookOpen(hand)); + return; + } + + Tui.BookTuiHost.OpenFromServer(this, hand == (int)BookHand.Off ? BookHand.Off : BookHand.Main); + } + /// /// Called when an entity spawned /// @@ -3116,13 +4198,85 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnEntitySpawn(entity)); } + private static bool ShouldAnnouncePlayerEffectGain(EffectData effectData, EffectData? previousPlayerEffect) + { + if (!Config.Main.Advanced.ShowEffectMessages) + return false; + + return previousPlayerEffect is null + || previousPlayerEffect.IsExpired + || previousPlayerEffect.Amplifier != effectData.Amplifier; + } + + private static void AnnouncePlayerEffectGain(EffectData effectData) + { + if (!Config.Main.Advanced.ShowEffectMessages) + return; + + ConsoleIO.WriteLine(string.Format(Translations.bot_effect_gained, + effectData.GetDisplayNameWithArticle(), effectData.GetInitialDurationText())); + } + + private static void AnnouncePlayerEffectExpired(EffectData effectData) + { + if (!Config.Main.Advanced.ShowEffectMessages) + return; + + ConsoleIO.WriteLine(string.Format(Translations.bot_effect_expired, effectData.GetDisplayName())); + } + /// /// Called when an entity effects /// public void OnEntityEffect(int entityid, Effects effect, int amplifier, int duration, byte flags, bool hasFactorData, Dictionary? factorCodec) { - if (entities.ContainsKey(entityid)) - DispatchBotEvent(bot => bot.OnEntityEffect(entities[entityid], effect, amplifier, duration, flags)); + Entity? entity = null; + if (entities.TryGetValue(entityid, out var trackedEntity)) + { + entity = trackedEntity; + } + + var effectData = new EffectData(effect, amplifier, duration, flags); + entity?.ActiveEffects[effect] = effectData; + + if (entityid == playerEntityID) + { + playerEffects.TryGetValue(effect, out var previousPlayerEffect); + playerEffects[effect] = effectData; + + if (ShouldAnnouncePlayerEffectGain(effectData, previousPlayerEffect)) + AnnouncePlayerEffectGain(effectData); + } + + if (entity is not null) + DispatchBotEvent(bot => bot.OnEntityEffect(entity, effect, amplifier, duration, flags)); + } + + /// + /// Called when an entity has an effect removed + /// + /// Entity ID + /// Effect that was removed + public void OnRemoveEntityEffect(int entityid, Effects effect) + { + Entity? entity = null; + EffectData? removedEffectData = null; + + if (entities.TryGetValue(entityid, out var trackedEntity)) + { + entity = trackedEntity; + if (entity.ActiveEffects.Remove(effect, out var entityEffectData)) + removedEffectData = entityEffectData; + } + + if (entityid == playerEntityID && playerEffects.Remove(effect, out var playerEffectData)) + removedEffectData ??= playerEffectData; + + if (entityid == playerEntityID && removedEffectData is not null) + AnnouncePlayerEffectExpired(removedEffectData); + + if (entity is not null) + DispatchBotEvent(bot => bot.OnRemoveEntityEffect(entity, effect)); } /// @@ -3154,7 +4308,7 @@ namespace MinecraftClient Entity entity = entities[entityid]; if (entity.Equipment.ContainsKey(slot)) entity.Equipment.Remove(slot); - if (item != null) + if (item is not null) entity.Equipment[slot] = item; DispatchBotEvent(bot => bot.OnEntityEquipment(entities[entityid], slot, item)); } @@ -3278,6 +4432,44 @@ namespace MinecraftClient } } + /// + /// Called when an entity velocity update is received. + /// + /// Entity ID + /// Velocity on X axis (blocks/tick) + /// Velocity on Y axis (blocks/tick) + /// Velocity on Z axis (blocks/tick) + public void OnEntityVelocity(int entityID, double velocityX, double velocityY, double velocityZ) + { + if (entities.TryGetValue(entityID, out Entity? entity)) + DispatchBotEvent(bot => bot.OnEntityVelocity(entity, velocityX, velocityY, velocityZ)); + } + + /// + /// Called when a sound packet is received. + /// + /// Sound key when available, otherwise null + /// Sound location when available + /// Sound category id from packet + /// Sound volume + /// Sound pitch + /// Source entity id for entity sound packets, if any + public void OnSoundEffect(string? soundName, Location? location, int category, float volume, float pitch, + int? entityID) + { + Entity? sourceEntity = null; + Location? resolvedLocation = location; + + if (entityID is int id && entities.TryGetValue(id, out Entity? entity)) + { + sourceEntity = entity; + resolvedLocation ??= entity.Location; + } + + DispatchBotEvent(bot => bot.OnSoundEffect(soundName, resolvedLocation, category, volume, pitch, + sourceEntity)); + } + /// /// Called when received entity properties from server. /// @@ -3287,6 +4479,9 @@ namespace MinecraftClient { if (EntityID == playerEntityID) { + foreach (var kvp in prop) + playerAttributes[kvp.Key] = kvp.Value; + DispatchBotEvent(bot => bot.OnPlayerProperty(prop)); } } @@ -3318,11 +4513,15 @@ namespace MinecraftClient { DateTime currentTime = DateTime.Now; long tickDiff = WorldAge - lastAge; - Double tps = tickDiff / (currentTime - lastTime).TotalSeconds; + double tps = tickDiff / (currentTime - lastTime).TotalSeconds; lastAge = WorldAge; lastTime = currentTime; - if (tps <= 20 && tps > 0) + if (tps > 0) { + // A Minecraft server cannot genuinely exceed 20 TPS; values above 20 are + // caused by packet-timing jitter. Clamp instead of discarding so that a + // healthy server averages to 20 rather than being biased downward. + tps = Math.Min(tps, 20.0); // calculate average tps if (tpsSamples.Count >= maxSamples) { @@ -3359,7 +4558,7 @@ namespace MinecraftClient if (Config.Main.Advanced.AutoRespawn) { Log.Info(Translations.mcc_player_dead_respawn); - respawnTicks = 10; + respawnTicks = Settings.ClientTicksPerSecond; } else { @@ -3492,7 +4691,78 @@ namespace MinecraftClient { DispatchBotEvent(bot => bot.OnUpdateScore(entityName, action, objectiveName, objectiveDisplayName, objectiveValue, numberFormat)); } - + + /// + /// Called when a Teams packet is received. Updates the internal team state and notifies bots. + /// + public void OnTeam(string teamName, byte method, string displayName, byte friendlyFlags, + string nameTagVisibility, string collisionRule, int color, + string prefix, string suffix, List players) + { + lock (teams) + { + switch (method) + { + case 0: // create + var newTeam = new PlayerTeam + { + Name = teamName, + DisplayName = displayName, + AllowFriendlyFire = (friendlyFlags & 0x01) != 0, + SeeFriendlyInvisibles = (friendlyFlags & 0x02) != 0, + NameTagVisibility = nameTagVisibility, + CollisionRule = collisionRule, + Color = color, + Prefix = prefix, + Suffix = suffix + }; + foreach (var p in players) + newTeam.Members.Add(p); + teams[teamName] = newTeam; + break; + + case 1: // remove + teams.Remove(teamName); + break; + + case 2: // update parameters + if (!teams.TryGetValue(teamName, out var updateTeam)) + { + updateTeam = new PlayerTeam { Name = teamName }; + teams[teamName] = updateTeam; + } + updateTeam.DisplayName = displayName; + updateTeam.AllowFriendlyFire = (friendlyFlags & 0x01) != 0; + updateTeam.SeeFriendlyInvisibles = (friendlyFlags & 0x02) != 0; + updateTeam.NameTagVisibility = nameTagVisibility; + updateTeam.CollisionRule = collisionRule; + updateTeam.Color = color; + updateTeam.Prefix = prefix; + updateTeam.Suffix = suffix; + break; + + case 3: // add players + if (!teams.TryGetValue(teamName, out var addTeam)) + { + addTeam = new PlayerTeam { Name = teamName }; + teams[teamName] = addTeam; + } + foreach (var p in players) + addTeam.Members.Add(p); + break; + + case 4: // remove players + if (teams.TryGetValue(teamName, out var removeTeam)) + foreach (var p in players) + removeTeam.Members.Remove(p); + break; + } + } + DispatchBotEvent(bot => bot.OnTeam(teamName, method, displayName, friendlyFlags, + nameTagVisibility, collisionRule, color, prefix, suffix, players)); + } + + /// /// Called when the client received the Tab Header and Footer /// @@ -3500,6 +4770,11 @@ namespace MinecraftClient /// Footer public void OnTabListHeaderAndFooter(string header, string footer) { + lock (tabListHeaderFooterLock) + { + tabListHeader = header; + tabListFooter = footer; + } DispatchBotEvent(bot => bot.OnTabListHeaderAndFooter(header, footer)); } @@ -3529,25 +4804,25 @@ namespace MinecraftClient Entity entity = entities[entityID]; entity.Metadata = metadata; int itemEntityMetadataFieldIndex = protocolversion < Protocol18Handler.MC_1_17_Version ? 7 : 8; - - if (entity.Type.ContainsItem() && metadata.TryGetValue(itemEntityMetadataFieldIndex, out object? itemObj) && itemObj != null && itemObj.GetType() == typeof(Item)) + + if (entity.Type.ContainsItem() && metadata.TryGetValue(itemEntityMetadataFieldIndex, out object? itemObj) && itemObj is not null && itemObj.GetType() == typeof(Item)) { Item item = (Item)itemObj; - if (item == null) + if (item is null) entity.Item = new Item(ItemType.Air, 0, null); else entity.Item = item; } - if (metadata.TryGetValue(6, out object? poseObj) && poseObj != null && poseObj.GetType() == typeof(Int32)) + if (metadata.TryGetValue(6, out object? poseObj) && poseObj is not null && poseObj.GetType() == typeof(Int32)) { entity.Pose = (EntityPose)poseObj; } - if (metadata.TryGetValue(2, out object? nameObj) && nameObj != null && nameObj.GetType() == typeof(string)) + if (metadata.TryGetValue(2, out object? nameObj) && nameObj is not null && nameObj.GetType() == typeof(string)) { string name = nameObj.ToString() ?? string.Empty; entity.CustomNameJson = name; entity.CustomName = ChatParser.ParseText(name); } - if (metadata.TryGetValue(3, out object? nameVisableObj) && nameVisableObj != null && nameVisableObj.GetType() == typeof(bool)) + if (metadata.TryGetValue(3, out object? nameVisableObj) && nameVisableObj is not null && nameVisableObj.GetType() == typeof(bool)) { entity.IsCustomNameVisible = bool.Parse(nameVisableObj.ToString() ?? string.Empty); } @@ -3650,6 +4925,9 @@ namespace MinecraftClient { switch (reason) { + case 3: + OnGamemodeUpdate(Guid.Empty, (int)value); + break; case 7: DispatchBotEvent(bot => bot.OnRainLevelChange(value)); break; @@ -3667,9 +4945,16 @@ namespace MinecraftClient public void OnBlockChange(Location location, Block block) { world.SetBlock(location, block); + if (!IsSignMaterial(block.Type)) + RemoveKnownSign(location); DispatchBotEvent(bot => bot.OnBlockChange(location, block)); } + public void OnBlockEntityData(Location location, Dictionary? nbt) + { + UpdateKnownSign(location, nbt); + } + /// /// Called when "AutoComplete" completes. /// @@ -3686,6 +4971,95 @@ namespace MinecraftClient Log.Debug("CanSendMessage = " + canSendMessage); } + public void OnRecipeBookAdd(RecipeBookRecipeEntry[] recipes, bool replace) + { + lock (recipeBookLock) + { + if (replace) + unlockedRecipes.Clear(); + + foreach (RecipeBookRecipeEntry recipe in recipes) + { + // Guard against malformed server packets that send empty display IDs. + if (!string.IsNullOrWhiteSpace(recipe.CommandId)) + unlockedRecipes[recipe.CommandId] = recipe; + } + } + } + + public void OnRecipeBookRemove(string[] recipeIds) + { + lock (recipeBookLock) + { + foreach (string recipeId in recipeIds) + { + if (!string.IsNullOrWhiteSpace(recipeId)) + unlockedRecipes.Remove(recipeId); + } + } + } + + public void OnAchievementsUpdate(IReadOnlyList added, IReadOnlyList removedIds, bool reset) + { + lock (achievementsLock) + { + if (reset) + achievements.Clear(); + + // Remove entries + foreach (string id in removedIds) + achievements.Remove(id); + + // Add/update entries. For progress-only updates (no definition), + // merge with existing definition if available. + foreach (Achievement entry in added) + { + if (entry.Title is null && achievements.TryGetValue(entry.Id, out Achievement? existing)) + { + // Progress-only update - merge with existing definition + bool isCompleted = ComputeAchievementCompleted(existing.Requirements, entry.CriteriaProgress); + achievements[entry.Id] = existing with { IsCompleted = isCompleted, CriteriaProgress = entry.CriteriaProgress }; + } + else + { + achievements[entry.Id] = entry; + } + } + } + + DispatchBotEvent(bot => bot.OnAchievementUpdate(added, removedIds, reset)); + } + + public void OnSelectAdvancementTab(string? tabId) + { + activeAdvancementTab = tabId; + } + + /// + /// Compute whether an achievement is completed based on AND-of-ORs requirements. + /// + private static bool ComputeAchievementCompleted(IReadOnlyList> requirements, IReadOnlyDictionary criteria) + { + if (requirements.Count == 0) + return true; + + foreach (IReadOnlyList group in requirements) + { + bool groupSatisfied = false; + foreach (string criterion in group) + { + if (criteria.TryGetValue(criterion, out bool done) && done) + { + groupSatisfied = true; + break; + } + } + if (!groupSatisfied) + return false; + } + return true; + } + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom @@ -3699,6 +5073,182 @@ namespace MinecraftClient return handler.ClickContainerButton(windowId, buttonId); } + private void ClearKnownSigns() + { + lock (signDataLock) + { + knownSigns.Clear(); + } + } + + private void RemoveKnownSign(Location location) + { + var key = ToBlockKey(location); + lock (signDataLock) + { + knownSigns.Remove(key); + } + } + + private void UpdateKnownSign(Location location, Dictionary? nbt) + { + var key = ToBlockKey(location); + var block = world.GetBlock(new Location(key.x, key.y, key.z)); + if (!IsSignMaterial(block.Type) || !TryExtractSignText(nbt, out string[] frontText, out string[] backText, out bool isWaxed)) + { + lock (signDataLock) + { + knownSigns.Remove(key); + } + + return; + } + + lock (signDataLock) + { + knownSigns[key] = (block.Type.ToString(), block.GetTypeString(), frontText, backText, isWaxed); + } + } + + private static bool TryExtractSignText(Dictionary? nbt, out string[] frontText, out string[] backText, out bool isWaxed) + { + frontText = ExtractSignLines(nbt, "front_text"); + backText = ExtractSignLines(nbt, "back_text"); + if (frontText.Length == 0 && backText.Length == 0) + frontText = ExtractLegacySignLines(nbt); + + isWaxed = nbt is not null + && nbt.TryGetValue("is_waxed", out object? waxedValue) + && waxedValue is bool waxed + && waxed; + return frontText.Length > 0 || backText.Length > 0; + } + + private static string[] ExtractSignLines(Dictionary? nbt, string sideKey) + { + if (nbt is null + || !nbt.TryGetValue(sideKey, out object? sideValue) + || sideValue is not Dictionary sideData + || !sideData.TryGetValue("messages", out object? messagesValue) + || messagesValue is not object[] messages) + { + return []; + } + + return messages + .Take(4) + .Select(ConvertSignMessage) + .ToArray(); + } + + private static string[] ExtractLegacySignLines(Dictionary? nbt) + { + if (nbt is null) + return []; + + List lines = new(4); + for (int i = 1; i <= 4; i++) + { + if (nbt.TryGetValue($"Text{i}", out object? value)) + lines.Add(ConvertSignMessage(value)); + } + + return lines.ToArray(); + } + + private static string ConvertSignMessage(object? value) + { + try + { + return value switch + { + null => string.Empty, + string text => ParseMaybeJsonText(text), + Dictionary nbt => ChatParser.ParseText(nbt), + object[] items => string.Concat(items.Select(ConvertSignMessage)), + _ => value.ToString() ?? string.Empty + }; + } + catch + { + return value?.ToString() ?? string.Empty; + } + } + + private static string ParseMaybeJsonText(string text) + { + string trimmed = text.Trim(); + if ((trimmed.StartsWith("{", StringComparison.Ordinal) && trimmed.EndsWith("}", StringComparison.Ordinal)) + || (trimmed.StartsWith("[", StringComparison.Ordinal) && trimmed.EndsWith("]", StringComparison.Ordinal))) + { + try + { + return ChatParser.ParseText(trimmed); + } + catch + { + } + } + + return text; + } + + private static bool IsSignMaterial(Material material) + { + return material.ToString().Contains("Sign", StringComparison.Ordinal); + } + + private static (int x, int y, int z) ToBlockKey(Location location) + { + Location blockLocation = location.ToFloor(); + return ((int)blockLocation.X, (int)blockLocation.Y, (int)blockLocation.Z); + } + + private static bool SupportsRecipeBook(ContainerType containerType) + { + return containerType switch + { + ContainerType.PlayerInventory or + ContainerType.Crafting or + ContainerType.Furnace or + ContainerType.BlastFurnace or + ContainerType.Smoker or + ContainerType.Stonecutter => true, + _ => false, + }; + } + + private void ClearUnlockedRecipes() + { + lock (recipeBookLock) + { + unlockedRecipes.Clear(); + } + } + + /// + /// Normalize a recipe argument for the target protocol version. + /// Legacy recipe-book packets use identifiers and default to the minecraft namespace. + /// 1.21.2+ recipe-book packets use numeric recipe display ids and should be left trimmed-only. + /// + internal static string NormalizeRecipeArgument(string recipeId, int protocolVersion) + { + return protocolVersion >= Protocol18Handler.MC_1_21_2_Version + ? recipeId.Trim() + : NormalizeRecipeId(recipeId); + } + + private static string NormalizeRecipeId(string recipeId) + { + string trimmedRecipeId = recipeId.Trim(); + if (trimmedRecipeId.Length == 0) + return string.Empty; + + return trimmedRecipeId.Contains(':', StringComparison.Ordinal) + ? trimmedRecipeId + : "minecraft:" + trimmedRecipeId; + } + #endregion } } diff --git a/MinecraftClient/Mcp/IMccMcpCapabilities.cs b/MinecraftClient/Mcp/IMccMcpCapabilities.cs new file mode 100644 index 00000000..055fb561 --- /dev/null +++ b/MinecraftClient/Mcp/IMccMcpCapabilities.cs @@ -0,0 +1,66 @@ +namespace MinecraftClient.Mcp; + +public interface IMccMcpCapabilities +{ + MccMcpResult GetSessionStatus(); + MccMcpResult GetServerInfo(); + MccMcpResult GetPlayerState(); + MccMcpResult GetWorldState(); + MccMcpResult GetChunkStatus(double? x, double? y, double? z); + MccMcpResult RaycastBlock(double maxDistance, bool includeNeighbors); + MccMcpResult PreviewPath(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs, int maxWaypoints); + MccMcpResult GetPlayersList(); + MccMcpResult GetPlayersDetailed(bool includeSelf, bool includeCoordinates); + MccMcpResult GetPlayerStats(); + MccMcpResult GetStatusEffects(); + MccMcpResult GetRecentEvents(long afterId, int maxCount, string? typeFilter); + MccMcpResult GetLoadedBots(); + MccMcpResult GetChatHistory(int maxCount, bool includeJson); + MccMcpResult GetInternalCommands(); + MccMcpResult GetMaterialsList(string? filter, int maxCount); + MccMcpResult GetBlockTypesList(string? filter, int maxCount); + MccMcpResult GetEntityTypesList(string? filter, int maxCount); + MccMcpResult SendChat(string text); + MccMcpResult QuitClient(); + MccMcpResult DisconnectClient(); + MccMcpResult Respawn(); + MccMcpResult RunInternalCommand(string command); + MccMcpResult PlayAnimation(string hand); + MccMcpResult ToggleSneak(bool enabled); + MccMcpResult ToggleSprint(bool enabled); + MccMcpResult UseItemOnHand(); + MccMcpResult ChangeHotbarSlot(int slot); + MccMcpResult SelectHotbarItem(string itemType, bool preferLowestSlot); + MccMcpResult UseItemOnBlock(double x, double y, double z); + MccMcpResult DigBlock(double x, double y, double z, double durationSeconds); + MccMcpResult PlaceBlock(int x, int y, int z, string face, string hand, bool lookAtBlock); + MccMcpResult InteractEntity(int entityId, string interaction, string hand); + MccMcpResult AttackEntity(int entityId); + MccMcpResult ScanNearbyBlocks(int radius, int maxCount, string? materialFilter); + MccMcpResult FindBlocks(string? query, int radius, int maxCount, bool exactMatch); + MccMcpResult IsPlayerNearby(string? playerName, double radius, bool includeSelf); + MccMcpResult LocatePlayer(string playerName, bool includeSelf); + MccMcpResult FindNearestEntity(string? typeFilter, string? nameFilter, double radius, bool includePlayers); + MccMcpResult CanReachPosition(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs); + MccMcpResult MoveTo(double x, double y, double z, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs); + MccMcpResult MoveToPlayer(string playerName, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs); + MccMcpResult LookAt(double x, double y, double z); + MccMcpResult LookDirection(string direction); + MccMcpResult LookAngles(float yaw, float pitch); + MccMcpResult ListInventories(); + MccMcpResult GetInventorySnapshot(int inventoryId); + MccMcpResult SearchInventories(string query, int maxCount, bool exactMatch, bool includeContainers); + MccMcpResult OpenContainerAt(int x, int y, int z, int timeoutMs, bool closeCurrent); + MccMcpResult CloseContainer(int inventoryId, int timeoutMs); + MccMcpResult InventoryWindowAction(int inventoryId, int slotId, string actionType); + MccMcpResult DropInventoryItem(string itemType, int count, int inventoryId, bool preferStack); + MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack); + MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack); + MccMcpResult QueryEntities(int maxCount); + MccMcpResult ListEntities(int maxCount, string? typeFilter, double radius); + MccMcpResult GetEntityInfo(int entityId, bool includeMetadata, bool includeEquipment, bool includeEffects); + MccMcpResult FindSigns(string text, bool exactMatch, int radius, int maxCount, bool includeBackText); + MccMcpResult ListItemEntities(string? itemType, double radius, int maxCount); + MccMcpResult PickupItems(string itemType, double radius, int maxItems, bool allowUnsafe, int timeoutMs); + MccMcpResult GetWorldBlockAt(int x, int y, int z); +} diff --git a/MinecraftClient/Mcp/MccEmbeddedMcpHost.cs b/MinecraftClient/Mcp/MccEmbeddedMcpHost.cs new file mode 100644 index 00000000..a40b7183 --- /dev/null +++ b/MinecraftClient/Mcp/MccEmbeddedMcpHost.cs @@ -0,0 +1,135 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using ModelContextProtocol.Server; + +namespace MinecraftClient.Mcp; + +public sealed class MccEmbeddedMcpHost +{ + private readonly MccMcpConfig config; + private readonly IMccMcpCapabilities capabilities; + private readonly object stateLock = new(); + private WebApplication? app; + + public MccEmbeddedMcpHost(MccMcpConfig config, IMccMcpCapabilities capabilities) + { + this.config = config; + this.capabilities = capabilities; + } + + public bool IsRunning + { + get + { + lock (stateLock) + { + return app is not null; + } + } + } + + public string Endpoint => $"http://{config.Transport.BindHost}:{config.Transport.Port}{NormalizeRoute(config.Transport.Route)}"; + + public bool Start(out string? error) + { + lock (stateLock) + { + error = null; + if (app is not null) + return true; + + string route = NormalizeRoute(config.Transport.Route); + string bindHost = string.IsNullOrWhiteSpace(config.Transport.BindHost) ? "127.0.0.1" : config.Transport.BindHost.Trim(); + if (config.Transport.Port is < 1 or > 65535) + { + error = "invalid_port"; + return false; + } + + string? requiredToken = null; + if (config.Transport.RequireAuthToken) + { + requiredToken = Environment.GetEnvironmentVariable(config.Transport.AuthTokenEnvVar); + if (string.IsNullOrWhiteSpace(requiredToken)) + { + error = "missing_auth_token"; + return false; + } + } + + WebApplicationBuilder builder = WebApplication.CreateBuilder(); + builder.Logging.ClearProviders(); + builder.Logging.AddFilter(_ => false); + builder.Services.AddSingleton(capabilities); + builder.Services.AddSingleton(config); + builder.Services.AddSingleton(); + builder.Services.AddMcpServer() + .WithHttpTransport() + .WithTools() + .WithPrompts(); + + builder.WebHost.UseUrls($"http://{bindHost}:{config.Transport.Port}"); + WebApplication builtApp = builder.Build(); + + if (config.Transport.RequireAuthToken) + { + builtApp.Use(async (context, next) => + { + if (context.Request.Path.StartsWithSegments(route, StringComparison.OrdinalIgnoreCase)) + { + string auth = context.Request.Headers.Authorization.ToString(); + if (!auth.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) + || !string.Equals(auth[7..], requiredToken, StringComparison.Ordinal)) + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + await context.Response.WriteAsync("Unauthorized"); + return; + } + } + + await next(); + }); + } + + builtApp.MapMcp(route); + builtApp.StartAsync().GetAwaiter().GetResult(); + app = builtApp; + return true; + } + } + + public bool Stop(out string? error) + { + lock (stateLock) + { + error = null; + if (app is null) + return true; + + try + { + app.StopAsync().GetAwaiter().GetResult(); + app.DisposeAsync().AsTask().GetAwaiter().GetResult(); + app = null; + return true; + } + catch + { + error = "stop_failed"; + return false; + } + } + } + + private static string NormalizeRoute(string route) + { + string normalized = string.IsNullOrWhiteSpace(route) ? "/mcp" : route.Trim(); + if (!normalized.StartsWith('/')) + normalized = '/' + normalized; + return normalized; + } +} diff --git a/MinecraftClient/Mcp/MccMcpCapabilities.cs b/MinecraftClient/Mcp/MccMcpCapabilities.cs new file mode 100644 index 00000000..43b6a837 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpCapabilities.cs @@ -0,0 +1,3259 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MinecraftClient.CommandHandler; +using MinecraftClient.Inventory; +using MinecraftClient.Mapping; +using MinecraftClient.Protocol; +using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpCapabilities : IMccMcpCapabilities +{ + private static readonly StringComparer NameComparer = StringComparer.OrdinalIgnoreCase; + private static readonly double[] s_defaultDigAttemptDurations = [1.5, 3.0, 5.0]; + private const int CoordinateRoundingPrecision = 2; + private const double SelfEntityDistanceThreshold = 0.2; + private const int MaxBlockScanRadius = 12; + private const int MaxBlockFindRadius = 32; + private const double MaxRaycastDistance = 128.0; + private const double DigReachDistance = 5.0; + private const double DigReachDistanceSquared = DigReachDistance * DigReachDistance; + private const int DefaultPathQueryTimeoutMs = 5000; + private const int MinPathQueryTimeoutMs = 250; + private const int MaxPathQueryTimeoutMs = 15000; + private const int DefaultArrivalWaitMs = 3500; + private const int MinArrivalWaitMs = 250; + private const int MaxArrivalWaitMs = 15000; + private const double DefaultArrivalTolerance = 1.5; + private const int ArrivalPollIntervalMs = 125; + private const int MaxBlockVerifyWaitMs = 12000; + private const int DefaultContainerWaitMs = 5000; + private const int MinContainerWaitMs = 250; + private const int MaxContainerWaitMs = 20000; + private const int DefaultInventoryActionWaitMs = 3500; + private const int MaxPathPreviewWaypoints = 1000; + + private sealed class InternalCommandInfo + { + public required string Name { get; init; } + public required string Usage { get; init; } + public required string Description { get; init; } + } + + private sealed class NearbyPlayerSnapshot + { + public required int EntityId { get; init; } + public required Guid Uuid { get; init; } + public string? Name { get; set; } + public string? CustomName { get; init; } + public required double X { get; init; } + public required double Y { get; init; } + public required double Z { get; init; } + public required double Distance { get; init; } + public required int Latency { get; init; } + } + + private sealed class NearbyItemSnapshot + { + public required int EntityId { get; init; } + public required ItemType ItemType { get; init; } + public required string TypeLabel { get; init; } + public required int Count { get; init; } + public required double X { get; init; } + public required double Y { get; init; } + public required double Z { get; init; } + public required double Distance { get; init; } + } + + private enum InventoryTransferDirection + { + Deposit, + Withdraw + } + + private readonly Func togglesProvider; + private readonly MccGameApi game; + + public MccMcpCapabilities(Func togglesProvider) + { + this.togglesProvider = togglesProvider; + game = new MccGameApi(GetClient); + } + + private static McClient? GetClient() + { + return McClient.Instance as McClient; + } + + private static MccMcpResult NotConnected() + { + return MccMcpResult.Fail("disconnected"); + } + + private static MccMcpResult ToMcpResult(MccGameResult result) + { + return result.Success + ? MccMcpResult.Ok(message: result.Message) + : MccMcpResult.Fail(result.ErrorCode ?? "unknown", result.Message); + } + + private static MccMcpResult ToMcpResult(MccGameResult result) + { + return result.Success + ? MccMcpResult.Ok(result.Data, result.Message) + : MccMcpResult.Fail(result.ErrorCode ?? "unknown", result.Message, result.Data); + } + + private bool IsCategoryEnabled(Func selector) + { + return selector(togglesProvider()); + } + + public MccMcpResult GetSessionStatus() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + Location location = client.GetCurrentLocation(); + return MccMcpResult.Ok(new + { + host = client.GetServerHost(), + port = client.GetServerPort(), + username = client.GetUsername(), + protocolVersion = client.GetProtocolVersion(), + terrainEnabled = client.GetTerrainEnabled(), + inventoryEnabled = client.GetInventoryEnabled(), + entityEnabled = client.GetEntityHandlingEnabled(), + location = ToCoordinate(location) + }); + }); + } + + public MccMcpResult GetServerInfo() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => MccMcpResult.Ok(new + { + host = client.GetServerHost(), + port = client.GetServerPort(), + tps = client.GetServerTPS() + })); + } + + public MccMcpResult GetPlayerState() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + Location location = client.GetCurrentLocation(); + Dictionary effects = client.GetPlayerEffects(); + return MccMcpResult.Ok(new + { + nickname = client.GetUsername(), + username = client.GetUsername(), + health = client.GetHealth(), + saturation = client.GetSaturation(), + gamemode = client.GetGamemode(), + currentSlot = client.GetCurrentSlot() + 1, + yaw = client.GetYaw(), + pitch = client.GetPitch(), + location = ToCoordinate(location), + effects = effects.Values.Select(effect => new + { + id = effect.Effect.ToString(), + amplifier = effect.Amplifier, + remainingSeconds = effect.RemainingSeconds, + isInfinite = effect.IsInfinite + }).ToArray() + }); + }); + } + + public MccMcpResult GetWorldState() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + Location location = client.GetCurrentLocation(); + World world = client.GetWorld(); + Dimension dimension = World.GetDimension(); + MccRuntimeStateSnapshot runtimeState = game.GetRuntimeState(); + int totalChunkCount = world.chunkCnt; + int pendingChunkCount = Math.Max(0, world.chunkLoadNotCompleted); + int loadedChunkCount = GetLoadedChunkCount(world); + + return MccMcpResult.Ok(new + { + host = client.GetServerHost(), + port = client.GetServerPort(), + username = client.GetUsername(), + protocol = client.GetProtocolVersion(), + protocolVersion = client.GetProtocolVersion(), + terrainEnabled = client.GetTerrainEnabled(), + inventoryEnabled = client.GetInventoryEnabled(), + entityEnabled = client.GetEntityHandlingEnabled(), + entityHandlingEnabled = client.GetEntityHandlingEnabled(), + location = ToCoordinate(location), + tps = client.GetServerTPS(), + dimension = dimension.Name, + dimensionDetails = new + { + name = dimension.Name, + minY = dimension.minY, + maxY = dimension.maxY, + height = dimension.height, + logicalHeight = dimension.logicalHeight, + coordinateScale = dimension.coordinateScale, + hasSkylight = dimension.hasSkylight, + hasCeiling = dimension.hasCeiling, + fixedTime = dimension.fixedTime >= 0 ? dimension.fixedTime : (long?)null + }, + loadedChunkCount, + pendingChunkCount, + totalChunkCount, + loadRatio = GetChunkLoadRatio(world), + worldAge = runtimeState.WorldAge, + timeOfDay = runtimeState.TimeOfDay, + rainLevel = runtimeState.RainLevel, + thunderLevel = runtimeState.ThunderLevel + }); + }); + } + + public MccMcpResult GetChunkStatus(double? x, double? y, double? z) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + if (!HasCompleteCoordinateTriple(x, y, z)) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location queryLocation = x.HasValue && y.HasValue && z.HasValue + ? new Location(x.Value, y.Value, z.Value) + : client.GetCurrentLocation(); + + World world = client.GetWorld(); + ChunkColumn? chunkColumn = world.GetChunkColumn(queryLocation); + return MccMcpResult.Ok(new + { + location = ToCoordinate(queryLocation), + chunk = new + { + x = queryLocation.ChunkX, + z = queryLocation.ChunkZ + }, + chunkX = queryLocation.ChunkX, + chunkZ = queryLocation.ChunkZ, + loaded = chunkColumn is not null, + fullyLoaded = chunkColumn?.FullyLoaded ?? false, + loadedChunkCount = GetLoadedChunkCount(world), + pendingChunkCount = Math.Max(0, world.chunkLoadNotCompleted), + totalChunkCount = world.chunkCnt, + loadRatio = GetChunkLoadRatio(world) + }); + }); + } + + public MccMcpResult RaycastBlock(double maxDistance, bool includeNeighbors) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + if (maxDistance <= 0 || maxDistance > MaxRaycastDistance) + { + return MccMcpResult.Fail("invalid_args", data: new + { + parameter = "maxDistance", + minExclusive = 0, + max = MaxRaycastDistance + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location playerLocation = client.GetCurrentLocation(); + Location eyeLocation = playerLocation.EyesLocation(); + Tuple raycast = RaycastHelper.RaycastBlock(client, maxDistance, includeFluids: false); + if (!raycast.Item1) + { + return MccMcpResult.Ok(new + { + hit = false, + maxDistance, + playerLocation = ToCoordinate(playerLocation), + eyeLocation = ToCoordinate(eyeLocation), + location = (object?)null, + block = (object?)null, + distance = (double?)null, + eyeDistance = (double?)null, + neighbors = (object?)null + }); + } + + Location blockLocation = raycast.Item2; + Block block = raycast.Item3; + Location targetCenter = blockLocation.ToCenter(); + object? neighbors = includeNeighbors ? GetNeighborBlockSnapshot(client.GetWorld(), blockLocation) : null; + + return MccMcpResult.Ok(new + { + hit = true, + maxDistance, + playerLocation = ToCoordinate(playerLocation), + eyeLocation = ToCoordinate(eyeLocation), + location = ToCoordinate(blockLocation), + block = ToBlockState(block), + distance = playerLocation.Distance(targetCenter), + eyeDistance = eyeLocation.Distance(targetCenter), + neighbors + }); + }); + } + + public MccMcpResult PreviewPath(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs, int maxWaypoints) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.PreviewPath(x, y, z, allowUnsafe, maxOffset, minOffset, timeoutMs, maxWaypoints)); + } + + public MccMcpResult GetPlayersList() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => MccMcpResult.Ok(new + { + players = client.GetOnlinePlayers() + })); + } + + public MccMcpResult GetPlayersDetailed(bool includeSelf, bool includeCoordinates) + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.GetPlayersDetailed(includeSelf, includeCoordinates)); + } + + public MccMcpResult GetPlayerStats() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + Location location = client.GetCurrentLocation(); + return MccMcpResult.Ok(new + { + username = client.GetUsername(), + health = client.GetHealth(), + saturation = client.GetSaturation(), + level = client.GetLevel(), + totalExperience = client.GetTotalExperience(), + gamemode = client.GetGamemode(), + playerEntityId = client.GetPlayerEntityID(), + currentSlot = client.GetCurrentSlot() + 1, + yaw = client.GetYaw(), + pitch = client.GetPitch(), + location = ToCoordinate(location), + tps = client.GetServerTPS() + }); + }); + } + + public MccMcpResult GetStatusEffects() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + var effects = client.GetPlayerEffects() + .Values + .Where(effect => !effect.IsExpired) + .OrderBy(effect => effect.Effect) + .Select(effect => new + { + id = effect.Effect.ToString(), + name = effect.GetDisplayName(), + amplifier = effect.Amplifier, + remainingSeconds = effect.RemainingSeconds, + isInfinite = effect.IsInfinite + }) + .ToArray(); + + return MccMcpResult.Ok(new + { + count = effects.Length, + effects + }); + }); + } + + public MccMcpResult GetRecentEvents(long afterId, int maxCount, string? typeFilter) + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return MccMcpResult.Ok(game.GetRecentEvents(afterId, maxCount, typeFilter)); + } + + public MccMcpResult GetLoadedBots() + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return client.InvokeOnMainThread(() => + { + var bots = client.GetLoadedChatBots() + .Select(bot => new + { + name = bot.GetType().Name, + fullTypeName = bot.GetType().FullName, + isScript = bot is MinecraftClient.ChatBots.Script + }) + .OrderBy(bot => bot.name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + return MccMcpResult.Ok(new + { + count = bots.Length, + bots + }); + }); + } + + public MccMcpResult GetChatHistory(int maxCount, bool includeJson) + { + if (!IsCategoryEnabled(t => t.SessionStatus)) + return MccMcpResult.Fail("capability_disabled"); + + if (GetClient() is null) + return NotConnected(); + + return MccMcpResult.Ok(game.GetChatHistory(maxCount, includeJson)); + } + + public MccMcpResult GetInternalCommands() + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + Type[] commandTypes = Program.GetTypesInNamespace("MinecraftClient.Commands"); + List commands = new(); + + foreach (Type type in commandTypes) + { + if (!type.IsSubclassOf(typeof(Command))) + continue; + + try + { + if (Activator.CreateInstance(type) is Command cmd) + { + commands.Add(new InternalCommandInfo + { + Name = cmd.CmdName, + Usage = cmd.CmdUsage, + Description = ChatBot.GetVerbatim(cmd.CmdDesc) + }); + } + } + catch + { + // ignore command constructors that fail for reflection-only list generation. + } + } + + InternalCommandInfo[] ordered = commands + .OrderBy(command => command.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + return MccMcpResult.Ok(new + { + count = ordered.Length, + commands = ordered.Select(command => new + { + name = command.Name, + usage = command.Usage, + description = command.Description + }).ToArray() + }); + } + + public MccMcpResult GetMaterialsList(string? filter, int maxCount) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + int limit = Math.Clamp(maxCount, 1, 5000); + string? normalizedFilter = string.IsNullOrWhiteSpace(filter) ? null : filter.Trim(); + Material[] allMaterials = Enum.GetValues(); + var materials = allMaterials + .Select(material => new + { + name = material.ToString(), + typeLabel = GetMaterialTypeLabel(material) + }) + .Where(material => normalizedFilter is null + || TextMatchesFilter(material.name, normalizedFilter) + || TextMatchesFilter(material.typeLabel, normalizedFilter)) + .OrderBy(material => material.name, StringComparer.OrdinalIgnoreCase) + .Take(limit) + .ToArray(); + + return MccMcpResult.Ok(new + { + total = allMaterials.Length, + count = materials.Length, + filter = normalizedFilter, + materials + }); + } + + public MccMcpResult GetBlockTypesList(string? filter, int maxCount) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + int limit = Math.Clamp(maxCount, 1, 5000); + string? normalizedFilter = string.IsNullOrWhiteSpace(filter) ? null : filter.Trim(); + Material[] allMaterials = Enum.GetValues(); + var blockTypes = allMaterials + .Select(material => new + { + name = material.ToString(), + typeLabel = GetMaterialTypeLabel(material) + }) + .Where(blockType => normalizedFilter is null + || TextMatchesFilter(blockType.name, normalizedFilter) + || TextMatchesFilter(blockType.typeLabel, normalizedFilter)) + .OrderBy(blockType => blockType.name, StringComparer.OrdinalIgnoreCase) + .Take(limit) + .ToArray(); + + return MccMcpResult.Ok(new + { + total = allMaterials.Length, + count = blockTypes.Length, + filter = normalizedFilter, + blockTypes + }); + } + + public MccMcpResult GetEntityTypesList(string? filter, int maxCount) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + int limit = Math.Clamp(maxCount, 1, 5000); + string? normalizedFilter = string.IsNullOrWhiteSpace(filter) ? null : filter.Trim(); + EntityType[] allEntityTypes = Enum.GetValues(); + var entityTypes = allEntityTypes + .Select(entityType => new + { + name = entityType.ToString(), + typeLabel = Entity.GetTypeString(entityType) + }) + .Where(entityType => normalizedFilter is null + || TextMatchesFilter(entityType.name, normalizedFilter) + || TextMatchesFilter(entityType.typeLabel, normalizedFilter)) + .OrderBy(entityType => entityType.name, StringComparer.OrdinalIgnoreCase) + .Take(limit) + .ToArray(); + + return MccMcpResult.Ok(new + { + total = allEntityTypes.Length, + count = entityTypes.Length, + filter = normalizedFilter, + entityTypes + }); + } + + public MccMcpResult SendChat(string text) + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(text)) + return MccMcpResult.Fail("invalid_args"); + + string normalized = text.Trim(); + if (normalized.Equals("quit", StringComparison.OrdinalIgnoreCase) + || normalized.Equals("exit", StringComparison.OrdinalIgnoreCase)) + { + return MccMcpResult.Fail("internal_command_text_blocked"); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + bool sent = client.InvokeOnMainThread(() => + { + client.SendText(normalized); + return true; + }); + + return sent ? MccMcpResult.Ok() : MccMcpResult.Fail("action_failed"); + } + + public MccMcpResult QuitClient() + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + _ = Task.Run(async () => + { + await Task.Delay(150).ConfigureAwait(false); + Program.Exit(); + }); + + return MccMcpResult.Ok(new { quitting = true }); + } + + public MccMcpResult DisconnectClient() + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + _ = Task.Run(async () => + { + await Task.Delay(150).ConfigureAwait(false); + client.Disconnect(); + }); + + return MccMcpResult.Ok(new { disconnecting = true }); + } + + public MccMcpResult Respawn() + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + float health = client.InvokeOnMainThread(client.GetHealth); + if (health > 0) + { + return MccMcpResult.Fail("invalid_state", data: new + { + health + }); + } + + bool ok = client.InvokeOnMainThread(client.SendRespawnPacket); + return ok + ? MccMcpResult.Ok(new { success = true }) + : MccMcpResult.Fail("action_failed", data: new { success = false }); + } + + public MccMcpResult RunInternalCommand(string command) + { + if (!IsCategoryEnabled(t => t.ChatAndCommands)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(command)) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + return ExecuteInternalCommand(client, command.Trim()); + } + + public MccMcpResult PlayAnimation(string hand) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(hand) || !Enum.TryParse(hand, true, out Hand parsedHand)) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + int animation = parsedHand == Hand.MainHand ? 1 : 0; + bool ok = client.DoAnimation(animation); + object resultData = new { success = ok, hand = parsedHand.ToString() }; + return ok + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_failed", data: resultData); + } + + public MccMcpResult ToggleSneak(bool enabled) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + EntityActionType action = enabled ? EntityActionType.StartSneaking : EntityActionType.StopSneaking; + bool ok = client.InvokeOnMainThread(() => + { + bool actionResult = client.SendEntityAction(action); + if (actionResult) + client.IsSneaking = enabled; + return actionResult; + }); + + object resultData = new { success = ok, enabled }; + return ok + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_failed", data: resultData); + } + + public MccMcpResult ToggleSprint(bool enabled) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + EntityActionType action = enabled ? EntityActionType.StartSprinting : EntityActionType.StopSprinting; + bool ok = client.SendEntityAction(action); + object resultData = new { success = ok, enabled }; + return ok + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_failed", data: resultData); + } + + public MccMcpResult UseItemOnHand() + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + bool ok = client.InvokeOnMainThread(() => client.UseItemOnHand()); + return MccMcpResult.Ok(new { success = ok }); + } + + public MccMcpResult ChangeHotbarSlot(int slot) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + if (slot is < 1 or > 9) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + bool ok = client.InvokeOnMainThread(() => client.ChangeSlot((short)(slot - 1))); + return MccMcpResult.Ok(new { success = ok, slot }); + } + + public MccMcpResult SelectHotbarItem(string itemType, bool preferLowestSlot) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.SelectHotbarItem(itemType, preferLowestSlot)); + } + + public MccMcpResult UseItemOnBlock(double x, double y, double z) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + string sx = x.ToString(CultureInfo.InvariantCulture); + string sy = y.ToString(CultureInfo.InvariantCulture); + string sz = z.ToString(CultureInfo.InvariantCulture); + return ExecuteInternalCommand(client, $"useitem {sx} {sy} {sz}"); + } + + public MccMcpResult DigBlock(double x, double y, double z, double durationSeconds) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + if (durationSeconds < 0) + { + return MccMcpResult.Fail("invalid_args", data: new + { + parameter = "durationSeconds", + min = 0 + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + Location target = ToBlockLocation(x, y, z); + Location currentLocation = client.InvokeOnMainThread(client.GetCurrentLocation); + Location eyesLocation = currentLocation.EyesLocation(); + Location centeredTarget = target.ToCenter(); + Block beforeBlock = client.InvokeOnMainThread(() => client.GetWorld().GetBlock(target)); + if (beforeBlock.Type == Material.Air) + { + return MccMcpResult.Fail("invalid_state", data: new + { + target = ToCoordinate(target), + beforeBlock = ToBlockState(beforeBlock) + }); + } + + double distance = eyesLocation.Distance(centeredTarget); + if (distance > DigReachDistance) + { + return MccMcpResult.Fail("action_incomplete", data: new + { + reason = "too_far", + target = ToCoordinate(target), + playerLocation = ToCoordinate(currentLocation), + distance, + maxReach = DigReachDistance, + beforeBlock = ToBlockState(beforeBlock) + }); + } + + double[] attemptDurations = GetDigAttemptDurations(durationSeconds); + List attemptedDurations = new(); + Block afterBlock = beforeBlock; + bool changed = false; + bool commandAccepted = false; + + foreach (double attemptDuration in attemptDurations) + { + attemptedDurations.Add(attemptDuration); + bool accepted = client.InvokeOnMainThread(() => client.DigBlock(target, Direction.Down, duration: attemptDuration)); + commandAccepted |= accepted; + if (!accepted) + continue; + + if (WaitForBlockChange(client, target, beforeBlock, GetDigVerifyWaitMs(attemptDuration), out afterBlock)) + { + changed = true; + break; + } + } + + afterBlock = client.InvokeOnMainThread(() => client.GetWorld().GetBlock(target)); + object resultData = new + { + success = changed, + target = ToCoordinate(target), + beforeBlock = ToBlockState(beforeBlock), + afterBlock = ToBlockState(afterBlock), + commandAccepted, + changed, + destroyed = changed && afterBlock.Type == Material.Air, + attempts = attemptedDurations.Count, + attemptedDurationsSeconds = attemptedDurations.ToArray(), + distance, + playerLocation = ToCoordinate(currentLocation) + }; + + return changed + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_incomplete", data: resultData); + } + + public MccMcpResult PlaceBlock(int x, int y, int z, string face, string hand, bool lookAtBlock) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + if (!Enum.TryParse(face, true, out Direction parsedFace)) + return MccMcpResult.Fail("invalid_args"); + + if (!Enum.TryParse(hand, true, out Hand parsedHand)) + return MccMcpResult.Fail("invalid_args"); + + Location location = new(x, y, z); + bool ok = client.InvokeOnMainThread(() => client.PlaceBlock(location, parsedFace, parsedHand, lookAtBlock)); + return MccMcpResult.Ok(new { success = ok, x, y, z, face = parsedFace.ToString(), hand = parsedHand.ToString(), lookAtBlock }); + } + + public MccMcpResult InteractEntity(int entityId, string interaction, string hand) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetEntityHandlingEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + if (!Enum.TryParse(interaction, true, out InteractType interactType)) + return MccMcpResult.Fail("invalid_args"); + + if (!Enum.TryParse(hand, true, out Hand parsedHand)) + return MccMcpResult.Fail("invalid_args"); + + bool ok = client.InvokeOnMainThread(() => client.InteractEntity(entityId, interactType, parsedHand)); + return MccMcpResult.Ok(new { success = ok, entityId, interaction = interactType.ToString(), hand = parsedHand.ToString() }); + } + + public MccMcpResult AttackEntity(int entityId) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetEntityHandlingEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + if (!client.GetEntities().ContainsKey(entityId)) + return MccMcpResult.Fail("invalid_state", data: new { entityId }); + + bool ok = client.InteractEntity(entityId, InteractType.Attack); + object resultData = new + { + success = ok, + entityId, + interaction = InteractType.Attack.ToString() + }; + + return ok + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_failed", data: resultData); + }); + } + + public MccMcpResult ScanNearbyBlocks(int radius, int maxCount, string? materialFilter) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + if (radius is < 1 or > MaxBlockScanRadius) + { + return MccMcpResult.Fail("invalid_args", data: new + { + parameter = "radius", + min = 1, + max = MaxBlockScanRadius + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + int limit = Math.Clamp(maxCount, 1, 2000); + string? filter = string.IsNullOrWhiteSpace(materialFilter) ? null : materialFilter.Trim(); + + return client.InvokeOnMainThread(() => + { + Location playerLocation = client.GetCurrentLocation(); + int cx = (int)Math.Floor(playerLocation.X); + int cy = (int)Math.Floor(playerLocation.Y) - 1; + int cz = (int)Math.Floor(playerLocation.Z); + + List found = new(); + World world = client.GetWorld(); + for (int y = cy - radius; y <= cy + radius && found.Count < limit; y++) + { + for (int z = cz - radius; z <= cz + radius && found.Count < limit; z++) + { + for (int x = cx - radius; x <= cx + radius && found.Count < limit; x++) + { + Block block = world.GetBlock(new Location(x, y, z)); + if (block.Type == Material.Air) + continue; + + string material = block.Type.ToString(); + string typeLabel = block.GetTypeString(); + if (filter is not null + && !TextMatchesFilter(material, filter) + && !TextMatchesFilter(typeLabel, filter)) + { + continue; + } + + double dx = x + 0.5 - playerLocation.X; + double dy = y + 0.5 - playerLocation.Y; + double dz = z + 0.5 - playerLocation.Z; + found.Add(new + { + x, + y, + z, + material, + typeLabel, + blockId = block.BlockId, + blockMeta = block.BlockMeta, + distance = Math.Sqrt(dx * dx + dy * dy + dz * dz) + }); + } + } + } + + return MccMcpResult.Ok(new + { + center = new { x = cx, y = cy, z = cz }, + radius, + count = found.Count, + blocks = found.ToArray() + }); + }); + } + + public MccMcpResult FindBlocks(string? query, int radius, int maxCount, bool exactMatch) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + if (radius is < 1 or > MaxBlockFindRadius) + { + return MccMcpResult.Fail("invalid_args", data: new + { + parameter = "radius", + min = 1, + max = MaxBlockFindRadius + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + int limit = Math.Clamp(maxCount, 1, 5000); + string? filter = string.IsNullOrWhiteSpace(query) ? null : query.Trim(); + ParseBlockQuery(filter, out int? blockIdFilter, out int? blockMetaFilter); + + return client.InvokeOnMainThread(() => + { + Location playerLocation = client.GetCurrentLocation(); + int cx = (int)Math.Floor(playerLocation.X); + int cy = (int)Math.Floor(playerLocation.Y) - 1; + int cz = (int)Math.Floor(playerLocation.Z); + + List<(int x, int y, int z, string material, string typeLabel, int blockId, byte blockMeta, double distance)> found = new(); + World world = client.GetWorld(); + + for (int y = cy - radius; y <= cy + radius && found.Count < limit; y++) + { + for (int z = cz - radius; z <= cz + radius && found.Count < limit; z++) + { + for (int x = cx - radius; x <= cx + radius && found.Count < limit; x++) + { + Block block = world.GetBlock(new Location(x, y, z)); + if (block.Type == Material.Air) + continue; + + if (!BlockMatches(block, filter, exactMatch, blockIdFilter, blockMetaFilter)) + continue; + + double dx = x + 0.5 - playerLocation.X; + double dy = y + 0.5 - playerLocation.Y; + double dz = z + 0.5 - playerLocation.Z; + + found.Add(( + x, + y, + z, + block.Type.ToString(), + block.GetTypeString(), + block.BlockId, + block.BlockMeta, + Math.Sqrt(dx * dx + dy * dy + dz * dz))); + } + } + } + + return MccMcpResult.Ok(new + { + center = new { x = cx, y = cy, z = cz }, + radius, + query = filter, + exactMatch, + count = found.Count, + blocks = found + .OrderBy(entry => entry.distance) + .Select(entry => new + { + entry.x, + entry.y, + entry.z, + entry.material, + entry.typeLabel, + entry.blockId, + entry.blockMeta, + entry.distance + }) + .ToArray() + }); + }); + } + + public MccMcpResult CanReachPosition(double x, double y, double z, bool allowUnsafe, int maxOffset, int minOffset, int timeoutMs) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + if (!AreValidPathOffsets(maxOffset, minOffset) || timeoutMs < 0) + { + return MccMcpResult.Fail("invalid_args", data: new + { + maxOffset, + minOffset, + timeoutMs + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + Location goal = new(x, y, z); + Location startLocation = client.InvokeOnMainThread(client.GetCurrentLocation); + World world = client.InvokeOnMainThread(client.GetWorld); + int effectiveTimeoutMs = GetPathQueryTimeoutMs(timeoutMs); + Queue? path = Movement.CalculatePath( + world, + startLocation, + goal, + allowUnsafe, + maxOffset, + minOffset, + TimeSpan.FromMilliseconds(effectiveTimeoutMs)); + Location? finalWaypoint = path?.LastOrDefault(); + double? finalDistance = finalWaypoint is Location waypoint + ? GetDistance(waypoint, goal) + : null; + + return MccMcpResult.Ok(new + { + reachable = path is not null, + exactReachable = finalWaypoint is Location location && location.ToFloor() == goal.ToFloor(), + target = ToCoordinate(goal), + startLocation = ToCoordinate(startLocation), + finalWaypoint = finalWaypoint is Location finalLocation ? ToCoordinate(finalLocation) : null, + finalDistance, + waypointCount = path?.Count ?? 0, + allowUnsafe, + maxOffset, + minOffset, + timeoutMs = effectiveTimeoutMs + }); + } + + public MccMcpResult IsPlayerNearby(string? playerName, double radius, bool includeSelf) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.IsPlayerNearby(playerName, radius, includeSelf)); + } + + public MccMcpResult LocatePlayer(string playerName, bool includeSelf) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.LocatePlayer(playerName, includeSelf)); + } + + public MccMcpResult FindNearestEntity(string? typeFilter, string? nameFilter, double radius, bool includePlayers) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.FindNearestEntity(typeFilter, nameFilter, radius, includePlayers)); + } + + public MccMcpResult MoveTo(double x, double y, double z, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + if (!AreValidPathOffsets(maxOffset, minOffset) || timeoutMs < 0) + { + return MccMcpResult.Fail("invalid_args", data: new + { + maxOffset, + minOffset, + timeoutMs + }); + } + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + Location goal = new(x, y, z); + Location startLocation = client.InvokeOnMainThread(client.GetCurrentLocation); + TimeSpan? timeout = timeoutMs > 0 ? TimeSpan.FromMilliseconds(timeoutMs) : null; + bool pathFound = client.InvokeOnMainThread(() => client.MoveTo(goal, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeout)); + + int verifyWaitMs = GetArrivalWaitMs(timeoutMs); + double tolerance = GetArrivalTolerance(maxOffset, minOffset); + Location? finalLocation = null; + bool arrived = pathFound && WaitForArrival(client, goal, verifyWaitMs, tolerance, out finalLocation); + finalLocation ??= client.InvokeOnMainThread(client.GetCurrentLocation); + object resultData = new + { + pathFound, + arrived, + tolerance, + verifyWaitMs, + target = ToCoordinate(goal), + startLocation = ToCoordinate(startLocation), + finalLocation = ToCoordinate(finalLocation.Value), + finalDistance = GetDistance(finalLocation.Value, goal), + distanceMoved = GetDistance(startLocation, finalLocation.Value), + allowUnsafe, + allowDirectTeleport, + maxOffset, + minOffset, + timeoutMs + }; + + return pathFound && arrived + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_incomplete", data: resultData); + } + + public MccMcpResult MoveToPlayer(string playerName, bool allowUnsafe, bool allowDirectTeleport, int maxOffset, int minOffset, int timeoutMs) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.MoveToPlayer(playerName, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeoutMs)); + } + + public MccMcpResult LookAt(double x, double y, double z) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location current = client.GetCurrentLocation(); + Location target = new(x, y, z); + client.UpdateLocation(current, target); + bool success = client.SendLocationUpdate(); + return success + ? MccMcpResult.Ok(new + { + success, + yaw = client.GetYaw(), + pitch = client.GetPitch(), + location = ToCoordinate(current), + target = ToCoordinate(target) + }) + : MccMcpResult.Fail("action_failed", data: new + { + success, + target = ToCoordinate(target) + }); + }); + } + + public MccMcpResult LookDirection(string direction) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(direction) || !Enum.TryParse(direction, true, out Direction parsedDirection) || !IsSupportedLookDirection(parsedDirection)) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location current = client.GetCurrentLocation(); + client.UpdateLocation(current, parsedDirection); + bool success = client.SendLocationUpdate(); + return success + ? MccMcpResult.Ok(new + { + success, + direction = parsedDirection.ToString(), + yaw = client.GetYaw(), + pitch = client.GetPitch(), + location = ToCoordinate(current) + }) + : MccMcpResult.Fail("action_failed", data: new + { + success, + direction = parsedDirection.ToString() + }); + }); + } + + public MccMcpResult LookAngles(float yaw, float pitch) + { + if (!IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location current = client.GetCurrentLocation(); + client.UpdateLocation(current, yaw, pitch); + bool success = client.SendLocationUpdate(); + return success + ? MccMcpResult.Ok(new + { + success, + yaw = client.GetYaw(), + pitch = client.GetPitch(), + location = ToCoordinate(current) + }) + : MccMcpResult.Fail("action_failed", data: new + { + success, + yaw, + pitch, + location = ToCoordinate(current) + }); + }); + } + + public MccMcpResult GetInventorySnapshot(int inventoryId) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.GetInventorySnapshot(inventoryId)); + } + + public MccMcpResult SearchInventories(string query, int maxCount, bool exactMatch, bool includeContainers) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.SearchInventories(query, maxCount, exactMatch, includeContainers)); + } + + public MccMcpResult ListInventories() + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.ListInventories()); + } + + public MccMcpResult OpenContainerAt(int x, int y, int z, int timeoutMs, bool closeCurrent) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled() || !client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + Location location = new(x, y, z); + int waitMs = GetContainerWaitMs(timeoutMs); + (Block block, int activeContainerId) state = client.InvokeOnMainThread(() => + { + Block block = client.GetWorld().GetBlock(location); + return (block, GetActiveContainerId(client)); + }); + + if (!IsInteractableContainerMaterial(state.block.Type)) + { + return MccMcpResult.Fail("invalid_state", data: new + { + x, + y, + z, + block = ToBlockState(state.block), + activeContainerId = state.activeContainerId + }); + } + + return OpenContainerCore(client, location, state.block, state.activeContainerId, waitMs, closeCurrent); + } + + public MccMcpResult CloseContainer(int inventoryId, int timeoutMs) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + int waitMs = GetContainerWaitMs(timeoutMs); + int resolvedInventoryId = client.InvokeOnMainThread(() => ResolveContainerInventoryId(client, inventoryId)); + if (resolvedInventoryId <= 0) + { + if (inventoryId < 0) + { + return MccMcpResult.Ok(new + { + success = true, + closed = false + }); + } + + return MccMcpResult.Fail("invalid_state", data: new { inventoryId }); + } + + bool closeAccepted = client.CloseInventory(resolvedInventoryId); + bool closed = closeAccepted && WaitForContainerClose(client, resolvedInventoryId, waitMs); + var resultData = new + { + success = closeAccepted && closed, + closeAccepted, + closed, + inventoryId = resolvedInventoryId, + timeoutMs = waitMs + }; + + return closeAccepted && closed + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_incomplete", data: resultData); + } + + public MccMcpResult InventoryWindowAction(int inventoryId, int slotId, string actionType) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(actionType)) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + if (!TryParseWindowAction(actionType, out WindowActionType parsedAction)) + return MccMcpResult.Fail("invalid_args"); + + bool ok = client.InvokeOnMainThread(() => client.DoWindowAction(inventoryId, slotId, parsedAction)); + return MccMcpResult.Ok(new { success = ok, normalizedActionType = parsedAction.ToString() }); + } + + public MccMcpResult DropInventoryItem(string itemType, int count, int inventoryId, bool preferStack) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(itemType) || count <= 0) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + if (!TryParseItemType(itemType, out ItemType parsedItemType)) + { + return MccMcpResult.Fail("invalid_args", data: new + { + itemType = itemType.Trim() + }); + } + + return client.InvokeOnMainThread(() => + { + Dictionary inventories = client.GetInventories(); + if (!inventories.TryGetValue(inventoryId, out Container? inventory)) + return MccMcpResult.Fail("invalid_state"); + + int cursorCount = GetCursorItemCount(inventory, parsedItemType); + var matchingSlotQuery = inventory.Items + .Where(pair => IsDroppableInventorySlot(inventory, pair.Key)) + .Where(pair => pair.Value.Type == parsedItemType && pair.Value.Count > 0) + .Select(pair => new + { + slot = pair.Key, + count = pair.Value.Count, + hotbarPriority = inventoryId == 0 && IsHotbarSlot(inventory, pair.Key) ? 0 : 1 + }); + var matchingSlots = (preferStack + ? matchingSlotQuery.OrderBy(pair => pair.hotbarPriority).ThenByDescending(pair => pair.count).ThenBy(pair => pair.slot) + : matchingSlotQuery.OrderBy(pair => pair.hotbarPriority).ThenBy(pair => pair.count).ThenBy(pair => pair.slot)) + .ToArray(); + + int beforeCount = matchingSlots.Sum(pair => pair.count); + if (beforeCount < count) + { + return MccMcpResult.Fail("invalid_state", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + availableCount = beforeCount, + cursorCount, + inventoryId + }); + } + + int remaining = count; + List touchedSlots = new(); + + foreach (var entry in matchingSlots) + { + if (remaining <= 0) + break; + + if (!inventory.Items.TryGetValue(entry.slot, out Item? currentItem) || currentItem.Count <= 0) + continue; + + int dropFromSlot = Math.Min(remaining, currentItem.Count); + touchedSlots.Add(entry.slot); + bool ok = TryDropInventorySlotItems(client, inventoryId, inventory, entry.slot, parsedItemType, dropFromSlot, out int droppedFromSlot); + + if (!ok) + { + Container? failedInventory = client.GetInventory(inventoryId); + int currentCount = failedInventory is null + ? 0 + : failedInventory.Items + .Where(pair => IsDroppableInventorySlot(failedInventory, pair.Key)) + .Where(pair => pair.Value.Type == parsedItemType) + .Sum(pair => pair.Value.Count); + return MccMcpResult.Fail("action_failed", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + droppedCount = count - remaining + droppedFromSlot, + remainingCount = remaining, + currentCount, + inventoryId, + touchedSlots = touchedSlots.ToArray() + }); + } + + remaining -= droppedFromSlot; + } + + Container? finalInventory = client.GetInventory(inventoryId); + int afterCount = finalInventory is null + ? 0 + : finalInventory.Items + .Where(pair => IsDroppableInventorySlot(finalInventory, pair.Key)) + .Where(pair => pair.Value.Type == parsedItemType) + .Sum(pair => pair.Value.Count); + int droppedCount = beforeCount - afterCount; + + if (remaining > 0) + { + return MccMcpResult.Fail("action_failed", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + droppedCount, + remainingCount = remaining, + beforeCount, + afterCount, + inventoryId, + touchedSlots = touchedSlots.ToArray() + }); + } + + return MccMcpResult.Ok(new + { + success = true, + itemType = parsedItemType.ToString(), + requestedCount = count, + droppedCount, + beforeCount, + afterCount, + inventoryId, + touchedSlots = touchedSlots.ToArray() + }); + }); + } + + public MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) + { + return TransferContainerItem(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Deposit); + } + + public MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) + { + return TransferContainerItem(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Withdraw); + } + + public MccMcpResult QueryEntities(int maxCount) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.QueryEntities(maxCount)); + } + + public MccMcpResult ListEntities(int maxCount, string? typeFilter, double radius) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.ListEntities(maxCount, typeFilter, radius)); + } + + public MccMcpResult GetEntityInfo(int entityId, bool includeMetadata, bool includeEquipment, bool includeEffects) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.GetEntityInfo(entityId, includeMetadata, includeEquipment, includeEffects)); + } + + public MccMcpResult FindSigns(string text, bool exactMatch, int radius, int maxCount, bool includeBackText) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(text) || radius is < 1 or > MaxBlockFindRadius) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + string filter = text.Trim(); + int limit = Math.Clamp(maxCount, 1, 500); + + return client.InvokeOnMainThread(() => + { + Location playerLocation = client.GetCurrentLocation(); + World world = client.GetWorld(); + var signs = client.GetKnownSigns() + .Select(sign => + { + double dx = sign.location.X + 0.5 - playerLocation.X; + double dy = sign.location.Y + 0.5 - playerLocation.Y; + double dz = sign.location.Z + 0.5 - playerLocation.Z; + return new + { + sign, + distance = Math.Sqrt(dx * dx + dy * dy + dz * dz) + }; + }) + .Where(entry => entry.distance <= radius) + .Where(entry => IsSignMaterial(world.GetBlock(entry.sign.location).Type)) + .Select(entry => + { + string[] frontText = entry.sign.frontText.Where(line => !string.IsNullOrWhiteSpace(line)).ToArray(); + string[] backText = includeBackText + ? entry.sign.backText.Where(line => !string.IsNullOrWhiteSpace(line)).ToArray() + : []; + string[] matchedLines = frontText + .Concat(backText) + .Where(line => exactMatch ? TextEqualsFilter(line, filter) : TextMatchesFilter(line, filter)) + .Distinct(NameComparer) + .ToArray(); + + return new + { + entry.sign, + entry.distance, + frontText, + backText, + matchedLines + }; + }) + .Where(entry => entry.matchedLines.Length > 0) + .OrderBy(entry => entry.distance) + .Take(limit) + .Select(entry => new + { + x = (int)Math.Floor(entry.sign.location.X), + y = (int)Math.Floor(entry.sign.location.Y), + z = (int)Math.Floor(entry.sign.location.Z), + material = entry.sign.material, + typeLabel = entry.sign.typeLabel, + distance = entry.distance, + isWaxed = entry.sign.isWaxed, + frontText = entry.frontText, + backText = entry.backText, + matchedLines = entry.matchedLines + }) + .ToArray(); + + return MccMcpResult.Ok(new + { + text = filter, + exactMatch, + radius, + includeBackText, + count = signs.Length, + signs + }); + }); + } + + public MccMcpResult ListItemEntities(string? itemType, double radius, int maxCount) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.ListItemEntities(itemType, radius, maxCount)); + } + + public MccMcpResult PickupItems(string itemType, double radius, int maxItems, bool allowUnsafe, int timeoutMs) + { + if (!IsCategoryEnabled(t => t.EntityWorld) || !IsCategoryEnabled(t => t.Movement)) + return MccMcpResult.Fail("capability_disabled"); + return ToMcpResult(game.PickupItems(itemType, radius, maxItems, allowUnsafe, timeoutMs)); + } + + public MccMcpResult GetWorldBlockAt(int x, int y, int z) + { + if (!IsCategoryEnabled(t => t.EntityWorld)) + return MccMcpResult.Fail("capability_disabled"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetTerrainEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + return client.InvokeOnMainThread(() => + { + Location location = new(x, y, z); + Block block = client.GetWorld().GetBlock(location); + return MccMcpResult.Ok(new + { + x, + y, + z, + material = block.Type.ToString(), + blockId = block.BlockId, + blockMeta = block.BlockMeta + }); + }); + } + + private static MccMcpResult OpenContainerCore(McClient client, Location location, Block block, int activeContainerId, int waitMs, bool closeCurrent) + { + if (activeContainerId > 0) + { + if (!closeCurrent) + { + return MccMcpResult.Fail("invalid_state", data: new + { + reason = "container_already_open", + activeContainerId, + x = location.X, + y = location.Y, + z = location.Z, + block = ToBlockState(block) + }); + } + + bool closeAccepted = client.CloseInventory(activeContainerId); + bool closed = closeAccepted && WaitForContainerClose(client, activeContainerId, waitMs); + if (!closeAccepted || !closed) + { + return MccMcpResult.Fail("action_incomplete", data: new + { + action = "close_previous_container", + activeContainerId, + closeAccepted, + closed, + timeoutMs = waitMs + }); + } + } + + HashSet beforeIds = client.InvokeOnMainThread(() => client.GetInventories().Keys.Where(id => id > 0).ToHashSet()); + int openedInventoryId = 0; + Container? openedInventory = null; + bool openAccepted = client.InvokeOnMainThread(() => client.PlaceBlock(location, Direction.Down, Hand.MainHand, lookAtBlock: true)); + bool opened = openAccepted && WaitForContainerOpen(client, beforeIds, waitMs, out openedInventoryId, out openedInventory); + var resultData = new + { + success = openAccepted && opened && openedInventory is not null, + openAccepted, + opened, + timeoutMs = waitMs, + x = location.X, + y = location.Y, + z = location.Z, + block = ToBlockState(block), + inventory = openedInventory is null + ? null + : new + { + id = openedInventoryId, + type = openedInventory.Type.ToString(), + title = openedInventory.Title, + slotCount = openedInventory.Type.SlotCount(), + nonEmptySlots = openedInventory.Items.Count + } + }; + + return openAccepted && opened && openedInventory is not null + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_incomplete", data: resultData); + } + + private MccMcpResult TransferContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack, InventoryTransferDirection direction) + { + if (!IsCategoryEnabled(t => t.Inventory)) + return MccMcpResult.Fail("capability_disabled"); + + if (string.IsNullOrWhiteSpace(itemType) || count <= 0) + return MccMcpResult.Fail("invalid_args"); + + McClient? client = GetClient(); + if (client is null) + return NotConnected(); + + if (!client.GetInventoryEnabled()) + return MccMcpResult.Fail("feature_disabled"); + + if (!TryParseItemType(itemType, out ItemType parsedItemType)) + { + return MccMcpResult.Fail("invalid_args", data: new + { + itemType = itemType.Trim() + }); + } + + if (TryGetCursorItem(client, out Item? cursorItem)) + { + return MccMcpResult.Fail("invalid_state", data: new + { + reason = "cursor_item_present", + cursor = new { type = cursorItem!.Type.ToString(), count = cursorItem.Count } + }); + } + + int resolvedInventoryId = client.InvokeOnMainThread(() => ResolveContainerInventoryId(client, inventoryId)); + if (resolvedInventoryId <= 0) + { + return MccMcpResult.Fail("invalid_state", data: new + { + inventoryId + }); + } + + Container? initialInventory = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + if (initialInventory is null) + return MccMcpResult.Fail("invalid_state", data: new { inventoryId = resolvedInventoryId }); + + if (!TryGetContainerSlotRanges(initialInventory.Type, out int containerStart, out int containerEnd, out int playerStart, out int playerEnd)) + { + return MccMcpResult.Fail("invalid_state", data: new + { + reason = "unsupported_container_type", + inventoryId = resolvedInventoryId, + type = initialInventory.Type.ToString() + }); + } + + int sourceStart = direction == InventoryTransferDirection.Deposit ? playerStart : containerStart; + int sourceEnd = direction == InventoryTransferDirection.Deposit ? playerEnd : containerEnd; + int targetStart = direction == InventoryTransferDirection.Deposit ? containerStart : playerStart; + int targetEnd = direction == InventoryTransferDirection.Deposit ? containerEnd : playerEnd; + + int beforePlayerCount = CountItemInRange(initialInventory, parsedItemType, playerStart, playerEnd); + int beforeContainerCount = CountItemInRange(initialInventory, parsedItemType, containerStart, containerEnd); + int availableCount = CountItemInRange(initialInventory, parsedItemType, sourceStart, sourceEnd); + if (availableCount < count) + { + return MccMcpResult.Fail("invalid_state", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + availableCount, + inventoryId = resolvedInventoryId, + direction = direction.ToString() + }); + } + + int remaining = count; + List touchedSourceSlots = new(); + List touchedTargetSlots = new(); + + while (remaining > 0) + { + Container? inventory = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + if (inventory is null) + return MccMcpResult.Fail("invalid_state", data: new { inventoryId = resolvedInventoryId }); + + if (TryGetCursorItem(client, out cursorItem)) + { + return MccMcpResult.Fail("invalid_state", data: new + { + reason = "cursor_item_present_mid_transfer", + cursor = new { type = cursorItem!.Type.ToString(), count = cursorItem.Count } + }); + } + + var sourceSlots = GetOrderedItemSlots(inventory, parsedItemType, sourceStart, sourceEnd, preferLargestStack); + if (sourceSlots.Length == 0) + break; + + int beforeSourceCount = CountItemInRange(inventory, parsedItemType, sourceStart, sourceEnd); + int beforeTargetCount = CountItemInRange(inventory, parsedItemType, targetStart, targetEnd); + (int slot, int sourceCount) = sourceSlots[0]; + touchedSourceSlots.Add(slot); + + int movedCount; + List usedTargetSlots = new(); + if (sourceCount <= remaining || direction == InventoryTransferDirection.Withdraw) + { + if (!client.DoWindowAction(resolvedInventoryId, slot, WindowActionType.ShiftClick)) + { + return MccMcpResult.Fail("action_failed", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + remainingCount = remaining, + inventoryId = resolvedInventoryId, + sourceSlot = slot, + direction = direction.ToString() + }); + } + + if (direction == InventoryTransferDirection.Withdraw) + { + if (!WaitForRangeCount(client, resolvedInventoryId, parsedItemType, sourceStart, sourceEnd, countAfterShift => countAfterShift < beforeSourceCount, DefaultInventoryActionWaitMs, out Container? afterShift, out int afterSourceCount)) + { + afterShift = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + afterSourceCount = afterShift is null ? beforeSourceCount : CountItemInRange(afterShift, parsedItemType, sourceStart, sourceEnd); + } + + movedCount = beforeSourceCount - afterSourceCount; + } + else + { + if (!WaitForRangeCount(client, resolvedInventoryId, parsedItemType, targetStart, targetEnd, countAfterShift => countAfterShift > beforeTargetCount, DefaultInventoryActionWaitMs, out Container? afterShift, out int afterTargetCount)) + { + afterShift = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + afterTargetCount = afterShift is null ? beforeTargetCount : CountItemInRange(afterShift, parsedItemType, targetStart, targetEnd); + } + + movedCount = afterTargetCount - beforeTargetCount; + } + + if (direction == InventoryTransferDirection.Withdraw && movedCount > remaining) + { + int excessCount = movedCount - remaining; + MccMcpResult returnExcess = TransferContainerItem(parsedItemType.ToString(), excessCount, resolvedInventoryId, preferLargestStack, InventoryTransferDirection.Deposit); + if (!returnExcess.Success) + { + return MccMcpResult.Fail("action_incomplete", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + remainingCount = remaining, + inventoryId = resolvedInventoryId, + sourceSlot = slot, + direction = direction.ToString(), + excessCount, + returnExcess + }); + } + + movedCount -= excessCount; + } + } + else + { + movedCount = TransferPartialFromSlot( + client, + resolvedInventoryId, + slot, + parsedItemType, + remaining, + sourceStart, + sourceEnd, + targetStart, + targetEnd, + usedTargetSlots); + } + + if (movedCount <= 0) + { + Container? afterFailure = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + return MccMcpResult.Fail("action_incomplete", data: new + { + itemType = parsedItemType.ToString(), + requestedCount = count, + remainingCount = remaining, + inventoryId = resolvedInventoryId, + sourceSlot = slot, + direction = direction.ToString(), + playerCount = afterFailure is null ? 0 : CountItemInRange(afterFailure, parsedItemType, playerStart, playerEnd), + containerCount = afterFailure is null ? 0 : CountItemInRange(afterFailure, parsedItemType, containerStart, containerEnd) + }); + } + + remaining -= movedCount; + touchedTargetSlots.AddRange(usedTargetSlots); + } + + Container? finalInventory = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId)); + if (finalInventory is null) + return MccMcpResult.Fail("invalid_state", data: new { inventoryId = resolvedInventoryId }); + + int afterPlayerCount = CountItemInRange(finalInventory, parsedItemType, playerStart, playerEnd); + int afterContainerCount = CountItemInRange(finalInventory, parsedItemType, containerStart, containerEnd); + int playerDelta = afterPlayerCount - beforePlayerCount; + int containerDelta = afterContainerCount - beforeContainerCount; + int movedTotal = direction == InventoryTransferDirection.Deposit + ? afterContainerCount - beforeContainerCount + : beforeContainerCount - afterContainerCount; + bool countsVerified = direction == InventoryTransferDirection.Deposit + ? containerDelta == count + : containerDelta == -count; + bool playerCountsMatchExpected = direction == InventoryTransferDirection.Deposit + ? playerDelta == -count + : playerDelta == count; + bool succeeded = remaining == 0 && countsVerified; + var resultData = new + { + success = succeeded, + direction = direction.ToString().ToLowerInvariant(), + itemType = parsedItemType.ToString(), + requestedCount = count, + movedCount = movedTotal, + beforePlayerCount, + afterPlayerCount, + beforeContainerCount, + afterContainerCount, + playerDelta, + containerDelta, + playerCountsMatchExpected, + verificationBasis = "container_delta", + inventoryId = resolvedInventoryId, + containerType = finalInventory.Type.ToString(), + touchedSourceSlots = touchedSourceSlots.Distinct().OrderBy(slot => slot).ToArray(), + touchedTargetSlots = touchedTargetSlots.Distinct().OrderBy(slot => slot).ToArray() + }; + + return succeeded + ? MccMcpResult.Ok(resultData) + : MccMcpResult.Fail("action_incomplete", data: resultData); + } + + private static MccMcpResult ExecuteInternalCommand(McClient client, string command) + { + return client.InvokeOnMainThread(() => + { + CmdResult result = new(); + bool ok = client.PerformInternalCommand(command, ref result); + return MccMcpResult.Ok(new + { + success = ok, + status = result.status.ToString(), + output = result.ToString() + }); + }); + } + + private static bool TryGetCursorItem(McClient client, out Item? cursorItem) + { + cursorItem = client.InvokeOnMainThread(() => + { + Container? playerInventory = client.GetInventory(0); + return playerInventory is not null && playerInventory.Items.TryGetValue(-1, out Item? item) ? item : null; + }); + return cursorItem is not null; + } + + private static int ResolveContainerInventoryId(McClient client, int inventoryId) + { + if (inventoryId > 0) + { + Container? inventory = client.GetInventory(inventoryId); + return inventory is not null && inventoryId != 0 ? inventoryId : 0; + } + + return GetActiveContainerId(client); + } + + private static int GetActiveContainerId(McClient client) + { + return client.GetInventories().Keys.Where(id => id > 0).DefaultIfEmpty(0).Max(); + } + + private static bool WaitForContainerOpen(McClient client, ISet beforeIds, int waitMs, out int inventoryId, out Container? inventory) + { + inventoryId = 0; + inventory = null; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + (int activeId, Container? activeInventory) state = client.InvokeOnMainThread(() => + { + int activeId = GetActiveContainerId(client); + Container? activeInventory = activeId > 0 ? client.GetInventory(activeId) : null; + return (activeId, activeInventory); + }); + + if (state.activeId > 0 && (!beforeIds.Contains(state.activeId) || beforeIds.Count == 0) && state.activeInventory is not null) + { + inventoryId = state.activeId; + inventory = state.activeInventory; + return true; + } + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool WaitForContainerClose(McClient client, int inventoryId, int waitMs) + { + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + bool stillOpen = client.InvokeOnMainThread(() => client.GetInventories().ContainsKey(inventoryId)); + if (!stillOpen) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static int GetContainerWaitMs(int timeoutMs) + { + if (timeoutMs <= 0) + return DefaultContainerWaitMs; + return Math.Clamp(timeoutMs, MinContainerWaitMs, MaxContainerWaitMs); + } + + private static bool TryGetContainerSlotRanges(ContainerType type, out int containerStart, out int containerEnd, out int playerStart, out int playerEnd) + { + containerStart = 0; + containerEnd = -1; + playerStart = 0; + playerEnd = -1; + + int containerSlots = type switch + { + ContainerType.Generic_9x1 => 9, + ContainerType.Generic_9x2 => 18, + ContainerType.Generic_9x3 => 27, + ContainerType.Generic_9x4 => 36, + ContainerType.Generic_9x5 => 45, + ContainerType.Generic_9x6 => 54, + ContainerType.Generic_3x3 => 9, + ContainerType.Hopper => 5, + ContainerType.ShulkerBox => 27, + ContainerType.Furnace or ContainerType.BlastFurnace or ContainerType.Smoker => 3, + ContainerType.Crafter => 9, + _ => -1 + }; + + if (containerSlots <= 0) + return false; + + int slotCount = type.SlotCount(); + if (slotCount <= containerSlots) + return false; + + containerEnd = containerSlots - 1; + playerStart = containerSlots; + playerEnd = slotCount - 1; + return true; + } + + private static bool IsSnapshotInventorySlot(Container inventory, int slotId) + { + return slotId >= 0 && slotId < inventory.Type.SlotCount(); + } + + private static bool IsDroppableInventorySlot(Container inventory, int slotId) + { + if (!IsSnapshotInventorySlot(inventory, slotId)) + return false; + + return !(slotId == 0 && (inventory.Type == ContainerType.PlayerInventory || inventory.Type == ContainerType.Crafting)); + } + + private static bool IsHotbarSlot(Container inventory, int slotId) + { + return inventory.IsHotbar(slotId, out _); + } + + private static object? TryBuildCursorSnapshot(Container inventory) + { + return inventory.Items.TryGetValue(-1, out Item? cursorItem) && cursorItem.Count > 0 + ? new + { + type = cursorItem.Type.ToString(), + count = cursorItem.Count + } + : null; + } + + private static int GetCursorItemCount(Container inventory, ItemType itemType) + { + return inventory.Items.TryGetValue(-1, out Item? cursorItem) && cursorItem.Type == itemType + ? cursorItem.Count + : 0; + } + + private static bool TryDropInventorySlotItems(McClient client, int inventoryId, Container inventory, int slotId, ItemType itemType, int dropCount, out int droppedCount) + { + droppedCount = 0; + if (!inventory.Items.TryGetValue(slotId, out Item? currentItem) || currentItem.Type != itemType || currentItem.Count <= 0) + return false; + + if (inventoryId == 0 && inventory.IsHotbar(slotId, out int hotbarSlot)) + { + return TryDropHotbarSlotItems(client, slotId, hotbarSlot, itemType, dropCount, currentItem.Count, out droppedCount); + } + + return TryDropWindowSlotItems(client, inventoryId, slotId, itemType, dropCount, currentItem.Count, out droppedCount); + } + + private static int CountItemInRange(Container inventory, ItemType itemType, int startSlot, int endSlot) + { + return inventory.Items + .Where(entry => entry.Key >= startSlot && entry.Key <= endSlot) + .Where(entry => entry.Value.Type == itemType) + .Sum(entry => entry.Value.Count); + } + + private static (int slot, int count)[] GetOrderedItemSlots(Container inventory, ItemType itemType, int startSlot, int endSlot, bool preferLargestStack) + { + var query = inventory.Items + .Where(entry => entry.Key >= startSlot && entry.Key <= endSlot) + .Where(entry => entry.Value.Type == itemType && entry.Value.Count > 0) + .Select(entry => (slot: entry.Key, count: entry.Value.Count)); + + return (preferLargestStack + ? query.OrderByDescending(entry => entry.count).ThenBy(entry => entry.slot) + : query.OrderBy(entry => entry.count).ThenBy(entry => entry.slot)) + .ToArray(); + } + + private static int TransferPartialFromSlot(McClient client, int inventoryId, int sourceSlot, ItemType itemType, int requestedCount, int sourceStart, int sourceEnd, int targetStart, int targetEnd, List touchedTargetSlots) + { + Container? inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId)); + if (inventory is null || !inventory.Items.TryGetValue(sourceSlot, out Item? sourceItem) || sourceItem.Count <= 0) + return 0; + + int amountToMove = Math.Min(requestedCount, sourceItem.Count); + if (!client.DoWindowAction(inventoryId, sourceSlot, WindowActionType.LeftClick)) + return 0; + + if (!WaitForCursorItem(client, itemType, DefaultInventoryActionWaitMs, out _)) + return 0; + + int moved = 0; + while (moved < amountToMove) + { + inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId)); + if (inventory is null || !TryGetCursorItem(client, out Item? cursorItem) || cursorItem is null || cursorItem.Type != itemType) + break; + + if (!TryFindTransferTargetSlot(inventory, itemType, targetStart, targetEnd, out int targetSlot, out int capacity)) + break; + + int step = Math.Min(amountToMove - moved, Math.Min(capacity, cursorItem.Count)); + int beforeTargetCount = GetSlotItemCount(inventory, targetSlot, itemType); + int beforeCursorCount = cursorItem.Count; + if (step <= 0 || !PlaceItemsFromCursor(client, inventoryId, targetSlot, step)) + break; + + if (!WaitForPlacement(client, inventoryId, targetSlot, itemType, beforeTargetCount, beforeCursorCount, step)) + break; + + touchedTargetSlots.Add(targetSlot); + moved += step; + } + + if (TryGetCursorItem(client, out Item? remainingCursor) && remainingCursor is not null && remainingCursor.Count > 0) + { + inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId)); + if (inventory is null) + return 0; + + int returnSlot = GetReturnSlot(inventory, itemType, sourceStart, sourceEnd, sourceSlot); + if (!client.DoWindowAction(inventoryId, returnSlot, WindowActionType.LeftClick)) + return 0; + + if (!WaitForCursorClear(client, DefaultInventoryActionWaitMs)) + return 0; + } + + return TryGetCursorItem(client, out _) + ? 0 + : moved; + } + + private static bool TryFindTransferTargetSlot(Container inventory, ItemType itemType, int startSlot, int endSlot, out int targetSlot, out int capacity) + { + int maxStack = itemType.StackCount(); + for (int slot = startSlot; slot <= endSlot; slot++) + { + if (inventory.Items.TryGetValue(slot, out Item? item) && item.Type == itemType && item.Count < maxStack) + { + targetSlot = slot; + capacity = maxStack - item.Count; + return true; + } + } + + for (int slot = startSlot; slot <= endSlot; slot++) + { + if (!inventory.Items.ContainsKey(slot)) + { + targetSlot = slot; + capacity = maxStack; + return true; + } + } + + targetSlot = -1; + capacity = 0; + return false; + } + + private static bool PlaceItemsFromCursor(McClient client, int inventoryId, int targetSlot, int count) + { + if (count <= 0 || !TryGetCursorItem(client, out Item? cursorItem) || cursorItem is null) + return false; + + if (count == cursorItem.Count) + return client.DoWindowAction(inventoryId, targetSlot, WindowActionType.LeftClick); + + for (int i = 0; i < count; i++) + { + if (!client.DoWindowAction(inventoryId, targetSlot, WindowActionType.RightClick)) + return false; + } + + return true; + } + + private static int GetReturnSlot(Container inventory, ItemType itemType, int startSlot, int endSlot, int originalSourceSlot) + { + if (originalSourceSlot != 0) + return originalSourceSlot; + + int maxStack = itemType.StackCount(); + for (int slot = startSlot; slot <= endSlot; slot++) + { + if (slot == 0) + continue; + + if (inventory.Items.TryGetValue(slot, out Item? item) && item.Type == itemType && item.Count < maxStack) + return slot; + } + + for (int slot = startSlot; slot <= endSlot; slot++) + { + if (slot == 0) + continue; + + if (!inventory.Items.ContainsKey(slot)) + return slot; + } + + return originalSourceSlot; + } + + private static int GetSlotItemCount(Container inventory, int slot, ItemType itemType) + { + return inventory.Items.TryGetValue(slot, out Item? item) && item.Type == itemType ? item.Count : 0; + } + + private static bool TryDropHotbarSlotItems(McClient client, int slotId, int hotbarSlot, ItemType itemType, int dropCount, int availableInSlot, out int droppedCount) + { + droppedCount = 0; + byte previousSlot = client.GetCurrentSlot(); + bool restoreSlot = previousSlot != hotbarSlot; + + if (restoreSlot && !client.ChangeSlot((short)hotbarSlot)) + return false; + + try + { + if (dropCount >= availableInSlot) + { + if (!client.DropSelectedItem(dropEntireStack: true)) + return false; + + if (!WaitForSlotItemCount(client, 0, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs, out _, out _)) + return false; + + droppedCount = availableInSlot; + return true; + } + + int remaining = dropCount; + while (remaining > 0) + { + Container? currentInventory = client.GetInventory(0); + if (currentInventory is null) + return false; + + int beforeSlotCount = GetSlotItemCount(currentInventory, slotId, itemType); + if (beforeSlotCount <= 0) + break; + + if (!client.DropSelectedItem(dropEntireStack: false)) + return false; + + if (!WaitForSlotItemCount(client, 0, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs, out _, out _)) + return false; + + remaining--; + droppedCount++; + } + + return remaining == 0; + } + finally + { + if (restoreSlot) + client.ChangeSlot((short)previousSlot); + } + } + + private static bool TryDropWindowSlotItems(McClient client, int inventoryId, int slotId, ItemType itemType, int dropCount, int availableInSlot, out int droppedCount) + { + droppedCount = 0; + + if (dropCount >= availableInSlot) + { + if (!client.DoWindowAction(inventoryId, slotId, WindowActionType.DropItemStack)) + return false; + + if (!WaitForSlotItemCount(client, inventoryId, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs, out _, out _)) + return false; + + droppedCount = availableInSlot; + return true; + } + + int remaining = dropCount; + while (remaining > 0) + { + Container? currentInventory = client.GetInventory(inventoryId); + if (currentInventory is null) + return false; + + int beforeSlotCount = GetSlotItemCount(currentInventory, slotId, itemType); + if (beforeSlotCount <= 0) + break; + + if (!client.DoWindowAction(inventoryId, slotId, WindowActionType.DropItem)) + return false; + + if (!WaitForSlotItemCount(client, inventoryId, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs, out _, out _)) + return false; + + remaining--; + droppedCount++; + } + + return remaining == 0; + } + + private static bool WaitForCursorItem(McClient client, ItemType itemType, int waitMs, out Item? cursorItem) + { + cursorItem = null; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + if (TryGetCursorItem(client, out cursorItem) && cursorItem is not null && cursorItem.Type == itemType) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool WaitForCursorClear(McClient client, int waitMs) + { + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + if (!TryGetCursorItem(client, out _)) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool WaitForPlacement(McClient client, int inventoryId, int targetSlot, ItemType itemType, int beforeTargetCount, int beforeCursorCount, int placedCount) + { + DateTime deadline = DateTime.UtcNow.AddMilliseconds(DefaultInventoryActionWaitMs); + while (true) + { + bool targetUpdated = false; + bool cursorUpdated = false; + + Container? inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId)); + if (inventory is not null) + { + int currentTargetCount = GetSlotItemCount(inventory, targetSlot, itemType); + targetUpdated = currentTargetCount >= beforeTargetCount + placedCount; + } + + if (placedCount >= beforeCursorCount) + { + cursorUpdated = !TryGetCursorItem(client, out _); + } + else if (TryGetCursorItem(client, out Item? cursorItem) && cursorItem is not null && cursorItem.Type == itemType) + { + cursorUpdated = cursorItem.Count <= beforeCursorCount - placedCount; + } + + if (targetUpdated && cursorUpdated) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool WaitForSlotItemCount(McClient client, int inventoryId, int slotId, ItemType itemType, Func predicate, int waitMs, out Container? inventory, out int itemCount) + { + inventory = null; + itemCount = 0; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + inventory = client.GetInventory(inventoryId); + if (inventory is not null) + { + itemCount = GetSlotItemCount(inventory, slotId, itemType); + if (predicate(itemCount)) + return true; + } + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool WaitForRangeCount(McClient client, int inventoryId, ItemType itemType, int startSlot, int endSlot, Func predicate, int waitMs, out Container? inventory, out int itemCount) + { + inventory = null; + itemCount = 0; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId)); + if (inventory is not null) + { + itemCount = CountItemInRange(inventory, itemType, startSlot, endSlot); + if (predicate(itemCount)) + return true; + } + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static List BuildTrackedPlayerSnapshots(McClient client, bool includeSelf) + { + Location playerLocation = client.GetCurrentLocation(); + string username = client.GetUsername(); + Dictionary uuidToName = client.GetOnlinePlayersWithUUID(); + string[] onlinePlayers = client.GetOnlinePlayers(); + + List trackedPlayers = client.GetEntities().Values + .Where(entity => entity.Type == EntityType.Player) + .Select(entity => + { + double dx = entity.Location.X - playerLocation.X; + double dy = entity.Location.Y - playerLocation.Y; + double dz = entity.Location.Z - playerLocation.Z; + double distance = Math.Sqrt(dx * dx + dy * dy + dz * dz); + string? rawName = ResolvePlayerEntityName(entity, uuidToName); + return new NearbyPlayerSnapshot + { + EntityId = entity.ID, + Uuid = entity.UUID, + Name = rawName, + CustomName = entity.CustomName, + X = entity.Location.X, + Y = entity.Location.Y, + Z = entity.Location.Z, + Distance = distance, + Latency = entity.Latency + }; + }) + .ToList(); + + if (!includeSelf) + { + trackedPlayers = trackedPlayers + .Where(player => !string.Equals(player.Name, username, StringComparison.OrdinalIgnoreCase)) + .Where(player => player.Distance > SelfEntityDistanceThreshold) + .ToList(); + } + + List unnamedTracked = trackedPlayers + .Where(player => string.IsNullOrWhiteSpace(player.Name)) + .OrderBy(player => player.Distance) + .ToList(); + if (unnamedTracked.Count == 0) + return trackedPlayers; + + HashSet assignedNames = trackedPlayers + .Select(player => player.Name) + .OfType() + .ToHashSet(NameComparer); + + string[] unmatchedOnline = onlinePlayers + .Where(name => includeSelf || !string.Equals(name, username, StringComparison.OrdinalIgnoreCase)) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .Where(name => !assignedNames.Contains(name)) + .Distinct(NameComparer) + .ToArray(); + + if (unmatchedOnline.Length == 0) + return trackedPlayers; + + if (unnamedTracked.Count == 1 && unmatchedOnline.Length == 1) + { + unnamedTracked[0].Name = unmatchedOnline[0]; + return trackedPlayers; + } + + int pairCount = Math.Min(unnamedTracked.Count, unmatchedOnline.Length); + string[] sortedNames = unmatchedOnline + .OrderBy(name => name, NameComparer) + .ToArray(); + for (int i = 0; i < pairCount; i++) + unnamedTracked[i].Name = sortedNames[i]; + + return trackedPlayers; + } + + private static object? DescribeMetadataValue(object? value) + { + return value switch + { + null => null, + string s => s, + bool b => b, + byte b => b, + sbyte b => b, + short s => s, + ushort s => s, + int i => i, + uint i => i, + long l => l, + ulong l => l, + float f => f, + double d => d, + decimal d => d, + Enum e => e.ToString(), + Location location => ToCoordinate(location), + Item item => new { type = item.Type.ToString(), count = item.Count }, + byte[] data => new { bytes = data.Length }, + _ => value.ToString() + }; + } + + private static bool PlayerNameMatches(NearbyPlayerSnapshot player, string filter) + { + if (string.IsNullOrWhiteSpace(filter)) + return true; + + string trimmed = filter.Trim(); + if (!string.IsNullOrWhiteSpace(player.Name) && player.Name.Contains(trimmed, StringComparison.OrdinalIgnoreCase)) + return true; + + if (!string.IsNullOrWhiteSpace(player.CustomName) && player.CustomName.Contains(trimmed, StringComparison.OrdinalIgnoreCase)) + return true; + + return false; + } + + private static bool TryParseWindowAction(string rawActionType, out WindowActionType actionType) + { + if (Enum.TryParse(rawActionType, true, out actionType)) + return true; + + string normalized = NormalizeToken(rawActionType); + if (normalized.Length == 0) + return false; + + return normalized switch + { + "left" or "leftclick" => SetAction(WindowActionType.LeftClick, out actionType), + "right" or "rightclick" => SetAction(WindowActionType.RightClick, out actionType), + "middle" or "mid" or "middleclick" => SetAction(WindowActionType.MiddleClick, out actionType), + "shift" or "shiftclick" => SetAction(WindowActionType.ShiftClick, out actionType), + "shiftright" or "shiftrightclick" => SetAction(WindowActionType.ShiftRightClick, out actionType), + "drop" or "dropitem" or "q" => SetAction(WindowActionType.DropItem, out actionType), + "dropstack" or "dropall" or "dropitemstack" or "ctrlq" or "ctrldrop" => SetAction(WindowActionType.DropItemStack, out actionType), + _ => false + }; + } + + private static bool SetAction(WindowActionType value, out WindowActionType actionType) + { + actionType = value; + return true; + } + + private static bool TryParseItemType(string rawItemType, out ItemType itemType) + { + if (Enum.TryParse(rawItemType, true, out itemType) && itemType is not (ItemType.Unknown or ItemType.Null)) + return true; + + string normalized = NormalizeToken(rawItemType); + if (normalized.Length == 0) + { + itemType = ItemType.Unknown; + return false; + } + + foreach (ItemType candidate in Enum.GetValues()) + { + if (candidate is ItemType.Unknown or ItemType.Null) + continue; + if (NormalizeToken(candidate.ToString()) == normalized) + { + itemType = candidate; + return true; + } + } + + itemType = ItemType.Unknown; + return false; + } + + private static string NormalizeToken(string value) + { + if (string.IsNullOrWhiteSpace(value)) + return string.Empty; + + char[] buffer = value + .Where(char.IsLetterOrDigit) + .Select(char.ToLowerInvariant) + .ToArray(); + return new string(buffer); + } + + private static bool WaitForArrival(McClient client, Location goal, int waitMs, double tolerance, out Location? finalLocation) + { + finalLocation = null; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + Location location = client.InvokeOnMainThread(client.GetCurrentLocation); + finalLocation = location; + double distance = GetDistance(location, goal); + if (distance <= tolerance) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static double GetDistance(Location from, Location to) + { + double dx = from.X - to.X; + double dy = from.Y - to.Y; + double dz = from.Z - to.Z; + return Math.Sqrt(dx * dx + dy * dy + dz * dz); + } + + private static int GetArrivalWaitMs(int timeoutMs) + { + if (timeoutMs <= 0) + return DefaultArrivalWaitMs; + return Math.Clamp(timeoutMs, MinArrivalWaitMs, MaxArrivalWaitMs); + } + + private static double GetArrivalTolerance(int maxOffset, int minOffset) + { + double toleranceFromOffset = Math.Max(maxOffset, minOffset) + 1.0; + return Math.Max(DefaultArrivalTolerance, toleranceFromOffset); + } + + private static int GetPathQueryTimeoutMs(int timeoutMs) + { + if (timeoutMs <= 0) + return DefaultPathQueryTimeoutMs; + return Math.Clamp(timeoutMs, MinPathQueryTimeoutMs, MaxPathQueryTimeoutMs); + } + + private static bool WaitForBlockChange(McClient client, Location target, Block beforeBlock, int waitMs, out Block afterBlock) + { + afterBlock = beforeBlock; + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + Block current = client.InvokeOnMainThread(() => client.GetWorld().GetBlock(target)); + afterBlock = current; + if (!AreEquivalentBlocks(current, beforeBlock)) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static bool AreEquivalentBlocks(Block left, Block right) + { + return left.BlockId == right.BlockId + && left.BlockMeta == right.BlockMeta + && left.Type == right.Type; + } + + private static double[] GetDigAttemptDurations(double durationSeconds) + { + if (durationSeconds > 0) + return [durationSeconds]; + return s_defaultDigAttemptDurations; + } + + private static int GetDigVerifyWaitMs(double durationSeconds) + { + int waitMs = (int)Math.Ceiling(durationSeconds * 1000) + 2000; + return Math.Clamp(waitMs, 1500, MaxBlockVerifyWaitMs); + } + + private static bool AreValidPathOffsets(int maxOffset, int minOffset) + { + return maxOffset >= 0 && minOffset >= 0 && minOffset <= maxOffset; + } + + private static bool HasCompleteCoordinateTriple(double? x, double? y, double? z) + { + return x.HasValue == y.HasValue && y.HasValue == z.HasValue; + } + + private static int GetLoadedChunkCount(World world) + { + return Math.Max(0, world.chunkCnt - Math.Max(0, world.chunkLoadNotCompleted)); + } + + private static double GetChunkLoadRatio(World world) + { + return world.chunkCnt > 0 + ? GetLoadedChunkCount(world) / (double)world.chunkCnt + : 0.0; + } + + private static object GetNeighborBlockSnapshot(World world, Location location) + { + Location blockLocation = location.ToFloor(); + Location north = new(blockLocation.X, blockLocation.Y, blockLocation.Z - 1); + Location south = new(blockLocation.X, blockLocation.Y, blockLocation.Z + 1); + Location east = new(blockLocation.X + 1, blockLocation.Y, blockLocation.Z); + Location west = new(blockLocation.X - 1, blockLocation.Y, blockLocation.Z); + Location above = new(blockLocation.X, blockLocation.Y + 1, blockLocation.Z); + Location below = new(blockLocation.X, blockLocation.Y - 1, blockLocation.Z); + + return new + { + north = new { location = ToCoordinate(north), block = ToBlockState(world.GetBlock(north)) }, + south = new { location = ToCoordinate(south), block = ToBlockState(world.GetBlock(south)) }, + east = new { location = ToCoordinate(east), block = ToBlockState(world.GetBlock(east)) }, + west = new { location = ToCoordinate(west), block = ToBlockState(world.GetBlock(west)) }, + above = new { location = ToCoordinate(above), block = ToBlockState(world.GetBlock(above)) }, + below = new { location = ToCoordinate(below), block = ToBlockState(world.GetBlock(below)) } + }; + } + + private static bool ItemMatches(Item item, string query, bool exactMatch, ItemType? exactItemType) + { + if (exactItemType.HasValue) + return item.Type == exactItemType.Value; + + string typeName = item.Type.ToString(); + string typeLabel = item.GetTypeString(); + return exactMatch + ? TextEqualsFilter(typeName, query) || TextEqualsFilter(typeLabel, query) + : TextMatchesFilter(typeName, query) || TextMatchesFilter(typeLabel, query); + } + + private static bool EntityNameMatches(string? name, string? customName, string filter) + { + return (!string.IsNullOrWhiteSpace(name) && TextMatchesFilter(name, filter)) + || (!string.IsNullOrWhiteSpace(customName) && TextMatchesFilter(customName, filter)); + } + + private static bool IsSupportedLookDirection(Direction direction) + { + return direction is Direction.Up or Direction.Down or Direction.North or Direction.South or Direction.East or Direction.West; + } + + private static NearbyItemSnapshot[] BuildNearbyItemSnapshots(McClient client, ItemType? itemType, double radius, int maxCount) + { + Location playerLocation = client.GetCurrentLocation(); + return client.GetEntities().Values + .Where(entity => entity.Type == EntityType.Item && !entity.Item.IsEmpty) + .Where(entity => !itemType.HasValue || entity.Item.Type == itemType.Value) + .Select(entity => + { + double dx = entity.Location.X - playerLocation.X; + double dy = entity.Location.Y - playerLocation.Y; + double dz = entity.Location.Z - playerLocation.Z; + return new NearbyItemSnapshot + { + EntityId = entity.ID, + ItemType = entity.Item.Type, + TypeLabel = entity.Item.GetTypeString(), + Count = entity.Item.Count, + X = entity.Location.X, + Y = entity.Location.Y, + Z = entity.Location.Z, + Distance = Math.Sqrt(dx * dx + dy * dy + dz * dz) + }; + }) + .Where(item => item.Distance <= radius) + .OrderBy(item => item.Distance) + .Take(maxCount) + .ToArray(); + } + + private static bool WaitForEntityRemoval(McClient client, int entityId, int waitMs) + { + DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs); + while (true) + { + bool exists = client.InvokeOnMainThread(() => client.GetEntities().ContainsKey(entityId)); + if (!exists) + return true; + + if (DateTime.UtcNow >= deadline) + return false; + + Thread.Sleep(ArrivalPollIntervalMs); + } + } + + private static int GetInventoryItemCount(McClient client, ItemType itemType) + { + Container? inventory = client.GetInventory(0); + if (inventory is null) + return 0; + + return inventory.Items.Values + .Where(item => item.Type == itemType) + .Sum(item => item.Count); + } + + private static object ToCoordinate(Location location) + { + return ToCoordinate(location.X, location.Y, location.Z); + } + + private static object ToCoordinate(double x, double y, double z) + { + return new + { + x = RoundCoordinate(x), + y = RoundCoordinate(y), + z = RoundCoordinate(z) + }; + } + + private static double RoundCoordinate(double value) + { + return Math.Round(value, CoordinateRoundingPrecision, MidpointRounding.AwayFromZero); + } + + private static Location ToBlockLocation(double x, double y, double z) + { + return new Location(Math.Floor(x), Math.Floor(y), Math.Floor(z)); + } + + private static object ToBlockState(Block block) + { + return new + { + material = block.Type.ToString(), + typeLabel = block.GetTypeString(), + blockId = block.BlockId, + blockMeta = block.BlockMeta + }; + } + + private static string GetMaterialTypeLabel(Material material) + { + string key = "block.minecraft." + ToTranslationKey(material.ToString()); + string? translation = ChatParser.TranslateString(key); + return string.IsNullOrEmpty(translation) ? material.ToString() : translation; + } + + private static string ToTranslationKey(string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + List chars = new(value.Length * 2); + for (int i = 0; i < value.Length; i++) + { + char current = value[i]; + if (char.IsUpper(current) && i > 0 && (char.IsLower(value[i - 1]) || char.IsDigit(value[i - 1]))) + chars.Add('_'); + chars.Add(char.ToLowerInvariant(current)); + } + + return new string(chars.ToArray()); + } + + private static bool IsSignMaterial(Material material) + { + return material.ToString().Contains("Sign", StringComparison.Ordinal); + } + + private static bool IsInteractableContainerMaterial(Material material) + { + string name = material.ToString(); + return name.Contains("Chest", StringComparison.Ordinal) + || name.Contains("Barrel", StringComparison.Ordinal) + || name.Contains("ShulkerBox", StringComparison.Ordinal) + || name.Contains("Hopper", StringComparison.Ordinal) + || name.Contains("Dispenser", StringComparison.Ordinal) + || name.Contains("Dropper", StringComparison.Ordinal) + || name.Contains("Furnace", StringComparison.Ordinal) + || name.Contains("Smoker", StringComparison.Ordinal) + || name.Contains("BlastFurnace", StringComparison.Ordinal) + || name.Contains("Crafter", StringComparison.Ordinal); + } + + private static string? ResolvePlayerEntityName(Entity entity, IReadOnlyDictionary uuidToName) + { + if (!string.IsNullOrWhiteSpace(entity.Name)) + return entity.Name; + + if (entity.UUID != Guid.Empty + && uuidToName.TryGetValue(entity.UUID.ToString(), out string? mappedName) + && !string.IsNullOrWhiteSpace(mappedName)) + { + return mappedName; + } + + if (!string.IsNullOrWhiteSpace(entity.CustomName)) + return entity.CustomName; + + return null; + } + + private static bool BlockMatches(Block block, string? filter, bool exactMatch, int? blockIdFilter, int? blockMetaFilter) + { + if (blockIdFilter.HasValue) + { + if (block.BlockId != blockIdFilter.Value) + return false; + if (blockMetaFilter.HasValue && block.BlockMeta != blockMetaFilter.Value) + return false; + return true; + } + + if (filter is null) + return true; + + string material = block.Type.ToString(); + string typeLabel = block.GetTypeString(); + if (exactMatch) + { + return TextEqualsFilter(material, filter) + || TextEqualsFilter(typeLabel, filter); + } + + return TextMatchesFilter(material, filter) + || TextMatchesFilter(typeLabel, filter); + } + + private static bool TextEqualsFilter(string text, string filter) + { + return text.Equals(filter, StringComparison.OrdinalIgnoreCase) + || NormalizeToken(text) == NormalizeToken(filter); + } + + private static bool TextMatchesFilter(string text, string filter) + { + if (text.Contains(filter, StringComparison.OrdinalIgnoreCase)) + return true; + + string normalizedFilter = NormalizeToken(filter); + if (normalizedFilter.Length == 0) + return false; + + return NormalizeToken(text).Contains(normalizedFilter, StringComparison.Ordinal); + } + + private static void ParseBlockQuery(string? query, out int? blockId, out int? blockMeta) + { + blockId = null; + blockMeta = null; + if (string.IsNullOrWhiteSpace(query)) + return; + + string trimmed = query.Trim(); + int separator = trimmed.IndexOf(':'); + if (separator >= 0) + { + string idPart = trimmed[..separator].Trim(); + string metaPart = trimmed[(separator + 1)..].Trim(); + if (int.TryParse(idPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsedId)) + { + blockId = parsedId; + if (int.TryParse(metaPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsedMeta)) + blockMeta = parsedMeta; + } + return; + } + + if (int.TryParse(trimmed, NumberStyles.Integer, CultureInfo.InvariantCulture, out int blockStateId)) + blockId = blockStateId; + } +} diff --git a/MinecraftClient/Mcp/MccMcpChatHistory.cs b/MinecraftClient/Mcp/MccMcpChatHistory.cs new file mode 100644 index 00000000..6cf314f1 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpChatHistory.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using MinecraftClient.Scripting; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpChatHistoryEntry +{ + public required DateTimeOffset TimestampUtc { get; init; } + public required string Kind { get; init; } + public required string Text { get; init; } + public string? Sender { get; init; } + public string? Message { get; init; } + public string? Json { get; init; } +} + +public static class MccMcpChatHistoryStore +{ + public static void Add(MccMcpChatHistoryEntry entry) + { + MccObservedStateStore.AddChatHistoryEntry(new MccChatHistoryEntry + { + TimestampUtc = entry.TimestampUtc, + Kind = entry.Kind, + Text = entry.Text, + Sender = entry.Sender, + Message = entry.Message, + Json = entry.Json + }); + } + + public static MccMcpChatHistoryEntry[] GetLatest(int maxCount) + { + return MccObservedStateStore.GetLatestChatHistory(maxCount) + .Select(entry => new MccMcpChatHistoryEntry + { + TimestampUtc = entry.TimestampUtc, + Kind = entry.Kind, + Text = entry.Text, + Sender = entry.Sender, + Message = entry.Message, + Json = entry.Json + }) + .ToArray(); + } + + public static void Clear() + { + MccObservedStateStore.ClearChatHistory(); + } +} diff --git a/MinecraftClient/Mcp/MccMcpConfig.cs b/MinecraftClient/Mcp/MccMcpConfig.cs new file mode 100644 index 00000000..1655be62 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpConfig.cs @@ -0,0 +1,46 @@ +using Tomlet.Attributes; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpConfig +{ + public bool Enabled { get; set; } + public MccMcpTransportConfig Transport { get; set; } = new(); + public MccMcpCapabilityToggles Capabilities { get; set; } = new(); +} + +public sealed class MccMcpTransportConfig +{ + [TomlInlineComment("$ChatBot.McpServer.Transport.BindHost$")] + public string BindHost { get; set; } = "127.0.0.1"; + + [TomlInlineComment("$ChatBot.McpServer.Transport.Port$")] + public int Port { get; set; } = 33333; + + [TomlInlineComment("$ChatBot.McpServer.Transport.Route$")] + public string Route { get; set; } = "/mcp"; + + [TomlInlineComment("$ChatBot.McpServer.Transport.RequireAuthToken$")] + public bool RequireAuthToken { get; set; } + + [TomlInlineComment("$ChatBot.McpServer.Transport.AuthTokenEnvVar$")] + public string AuthTokenEnvVar { get; set; } = "MCC_MCP_AUTH_TOKEN"; +} + +public sealed class MccMcpCapabilityToggles +{ + [TomlInlineComment("$ChatBot.McpServer.Capabilities.SessionStatus$")] + public bool SessionStatus { get; set; } = true; + + [TomlInlineComment("$ChatBot.McpServer.Capabilities.ChatAndCommands$")] + public bool ChatAndCommands { get; set; } = true; + + [TomlInlineComment("$ChatBot.McpServer.Capabilities.Movement$")] + public bool Movement { get; set; } = true; + + [TomlInlineComment("$ChatBot.McpServer.Capabilities.Inventory$")] + public bool Inventory { get; set; } = true; + + [TomlInlineComment("$ChatBot.McpServer.Capabilities.EntityWorld$")] + public bool EntityWorld { get; set; } = true; +} diff --git a/MinecraftClient/Mcp/MccMcpGuidanceProvider.cs b/MinecraftClient/Mcp/MccMcpGuidanceProvider.cs new file mode 100644 index 00000000..465d8592 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpGuidanceProvider.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.Json.Serialization; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpGuidanceProvider +{ + private const string EmbeddedPromptResourceSuffix = "MccMcpOperatorPrompt.md"; + private const string BestPracticesHeading = "## Best Practices"; + private const string ExampleScenariosHeading = "## Example Scenarios"; + + private readonly MccMcpConfig config; + private readonly Lazy guidanceDocument; + + public MccMcpGuidanceProvider(MccMcpConfig config) + { + this.config = config; + guidanceDocument = new Lazy(LoadGuidanceDocument); + } + + public string PromptName => "mcc_operator_prompt"; + + public string SkillName => "mcc-mcp-operator"; + + public string GetSystemPrompt() + { + GuidanceDocument document = guidanceDocument.Value; + MccMcpAgentCapabilityStatus capabilityStatus = BuildCapabilityStatus(); + StringBuilder builder = new(); + builder.AppendLine("You are an external agent controlling Minecraft Console Client (MCC) through its built-in MCP server."); + builder.AppendLine("Use the following MCP Operator Prompt as your system prompt. Treat the capability snapshot as authoritative and do not invent unsupported actions."); + builder.AppendLine(); + builder.AppendLine(document.BodyMarkdown); + builder.AppendLine(); + builder.AppendLine("Current capability snapshot"); + builder.AppendLine($"- sessionStatus: {FormatCapability(capabilityStatus.SessionStatus)}"); + builder.AppendLine($"- chatAndCommands: {FormatCapability(capabilityStatus.ChatAndCommands)}"); + builder.AppendLine($"- movement: {FormatCapability(capabilityStatus.Movement)}"); + builder.AppendLine($"- inventory: {FormatCapability(capabilityStatus.Inventory)}"); + builder.AppendLine($"- entityWorld: {FormatCapability(capabilityStatus.EntityWorld)}"); + return builder.ToString().Trim(); + } + + public MccMcpAgentGuidancePayload GetToolPayload() + { + GuidanceDocument document = guidanceDocument.Value; + return new MccMcpAgentGuidancePayload + { + PromptName = PromptName, + PromptMarkdown = document.PromptMarkdown, + SkillName = SkillName, + GuidanceVersion = document.GuidanceVersion, + SkillMarkdown = document.SkillMarkdown, + SystemPrompt = GetSystemPrompt(), + BestPractices = document.BestPractices, + ExampleScenarios = document.ExampleScenarios, + CapabilityStatus = BuildCapabilityStatus() + }; + } + + private GuidanceDocument LoadGuidanceDocument() + { + Assembly assembly = typeof(MccMcpGuidanceProvider).Assembly; + string resourceName = assembly.GetManifestResourceNames() + .FirstOrDefault(name => name.EndsWith(EmbeddedPromptResourceSuffix, StringComparison.Ordinal)) + ?? throw new InvalidOperationException($"Embedded MCP operator prompt resource '{EmbeddedPromptResourceSuffix}' was not found."); + + using Stream? stream = assembly.GetManifestResourceStream(resourceName); + if (stream is null) + throw new InvalidOperationException($"Embedded MCP operator prompt resource '{resourceName}' could not be opened."); + + using StreamReader reader = new(stream, Encoding.UTF8); + string promptMarkdown = reader.ReadToEnd(); + string bodyMarkdown = StripFrontmatter(promptMarkdown); + string bestPracticesSection = ExtractSection(bodyMarkdown, BestPracticesHeading); + string exampleScenariosSection = ExtractSection(bodyMarkdown, ExampleScenariosHeading); + + return new GuidanceDocument( + ComputeGuidanceVersion(promptMarkdown), + promptMarkdown.Replace("\r\n", "\n").Trim(), + bodyMarkdown, + ExtractBulletList(bestPracticesSection), + ExtractExampleScenarios(exampleScenariosSection)); + } + + private MccMcpAgentCapabilityStatus BuildCapabilityStatus() + { + return new MccMcpAgentCapabilityStatus + { + SessionStatus = config.Capabilities.SessionStatus, + ChatAndCommands = config.Capabilities.ChatAndCommands, + Movement = config.Capabilities.Movement, + Inventory = config.Capabilities.Inventory, + EntityWorld = config.Capabilities.EntityWorld + }; + } + + private static string StripFrontmatter(string markdown) + { + string normalized = markdown.Replace("\r\n", "\n"); + if (!normalized.StartsWith("---\n", StringComparison.Ordinal)) + return normalized.Trim(); + + int endOfFrontmatter = normalized.IndexOf("\n---\n", 4, StringComparison.Ordinal); + if (endOfFrontmatter < 0) + return normalized.Trim(); + + return normalized[(endOfFrontmatter + 5)..].Trim(); + } + + private static string ExtractSection(string markdownBody, string heading) + { + int headingIndex = markdownBody.IndexOf(heading, StringComparison.Ordinal); + if (headingIndex < 0) + return string.Empty; + + int sectionStart = headingIndex + heading.Length; + int nextHeading = markdownBody.IndexOf("\n## ", sectionStart, StringComparison.Ordinal); + string section = nextHeading >= 0 + ? markdownBody[sectionStart..nextHeading] + : markdownBody[sectionStart..]; + + return section.Trim(); + } + + private static string[] ExtractBulletList(string section) + { + return section + .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(line => line.StartsWith("- ", StringComparison.Ordinal)) + .Select(line => line[2..].Trim()) + .Where(line => line.Length > 0) + .ToArray(); + } + + private static MccMcpAgentScenario[] ExtractExampleScenarios(string section) + { + if (string.IsNullOrWhiteSpace(section)) + return []; + + List scenarios = []; + string? currentTitle = null; + List currentBodyLines = []; + + foreach (string rawLine in section.Split('\n')) + { + string line = rawLine.TrimEnd(); + if (line.StartsWith("### ", StringComparison.Ordinal)) + { + AddScenario(scenarios, currentTitle, currentBodyLines); + currentTitle = line[4..].Trim(); + currentBodyLines = []; + continue; + } + + if (currentTitle is not null) + currentBodyLines.Add(line); + } + + AddScenario(scenarios, currentTitle, currentBodyLines); + return scenarios.ToArray(); + } + + private static void AddScenario(List scenarios, string? title, List bodyLines) + { + if (string.IsNullOrWhiteSpace(title)) + return; + + string guidance = string.Join('\n', bodyLines) + .Trim(); + + scenarios.Add(new MccMcpAgentScenario + { + Title = title, + Guidance = guidance + }); + } + + private static string FormatCapability(bool enabled) + { + return enabled ? "enabled" : "disabled"; + } + + private static string ComputeGuidanceVersion(string markdown) + { + byte[] bytes = Encoding.UTF8.GetBytes(markdown.Replace("\r\n", "\n").Trim()); + byte[] hash = SHA256.HashData(bytes); + return Convert.ToHexString(hash[..8]).ToLowerInvariant(); + } + + private sealed record GuidanceDocument( + string GuidanceVersion, + string SkillMarkdown, + string BodyMarkdown, + string[] BestPractices, + MccMcpAgentScenario[] ExampleScenarios) + { + public string PromptMarkdown => SkillMarkdown; + } +} + +public sealed class MccMcpAgentGuidancePayload +{ + [JsonPropertyName("promptName")] + public string PromptName { get; init; } = string.Empty; + + [JsonPropertyName("promptMarkdown")] + public string PromptMarkdown { get; init; } = string.Empty; + + [JsonPropertyName("skillName")] + public string SkillName { get; init; } = string.Empty; + + [JsonPropertyName("guidanceVersion")] + public string GuidanceVersion { get; init; } = string.Empty; + + [JsonPropertyName("skillMarkdown")] + public string SkillMarkdown { get; init; } = string.Empty; + + [JsonPropertyName("systemPrompt")] + public string SystemPrompt { get; init; } = string.Empty; + + [JsonPropertyName("bestPractices")] + public string[] BestPractices { get; init; } = []; + + [JsonPropertyName("exampleScenarios")] + public MccMcpAgentScenario[] ExampleScenarios { get; init; } = []; + + [JsonPropertyName("capabilityStatus")] + public MccMcpAgentCapabilityStatus CapabilityStatus { get; init; } = new(); +} + +public sealed class MccMcpAgentScenario +{ + [JsonPropertyName("title")] + public string Title { get; init; } = string.Empty; + + [JsonPropertyName("guidance")] + public string Guidance { get; init; } = string.Empty; +} + +public sealed class MccMcpAgentCapabilityStatus +{ + [JsonPropertyName("sessionStatus")] + public bool SessionStatus { get; init; } + + [JsonPropertyName("chatAndCommands")] + public bool ChatAndCommands { get; init; } + + [JsonPropertyName("movement")] + public bool Movement { get; init; } + + [JsonPropertyName("inventory")] + public bool Inventory { get; init; } + + [JsonPropertyName("entityWorld")] + public bool EntityWorld { get; init; } +} diff --git a/MinecraftClient/Mcp/MccMcpPromptSet.cs b/MinecraftClient/Mcp/MccMcpPromptSet.cs new file mode 100644 index 00000000..970528a6 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpPromptSet.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; +using ModelContextProtocol.Server; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpPromptSet +{ + private readonly MccMcpGuidanceProvider guidanceProvider; + + public MccMcpPromptSet(MccMcpGuidanceProvider guidanceProvider) + { + this.guidanceProvider = guidanceProvider; + } + + [McpServerPrompt(Name = "mcc_operator_prompt"), Description("Get the canonical MCC MCP Operator Prompt for external agents using this MCP server.")] + public string OperatorPrompt() + { + return guidanceProvider.GetSystemPrompt(); + } +} diff --git a/MinecraftClient/Mcp/MccMcpRecentEventStore.cs b/MinecraftClient/Mcp/MccMcpRecentEventStore.cs new file mode 100644 index 00000000..69da1b57 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpRecentEventStore.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using MinecraftClient.Scripting; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpRecentEventEntry +{ + public required long Id { get; init; } + public required DateTimeOffset TimestampUtc { get; init; } + public required string Type { get; init; } + public object? Data { get; init; } +} + +public static class MccMcpRecentEventStore +{ + public static long Add(string type, object? data = null) + { + return MccObservedStateStore.AddRecentEvent(type, data); + } + + public static long GetLatestId() + { + return MccObservedStateStore.GetLatestRecentEventId(); + } + + public static MccMcpRecentEventEntry[] GetAfter(long afterId, int maxCount, string? typeFilter = null) + { + return MccObservedStateStore.GetRecentEventsAfter(afterId, maxCount, typeFilter) + .Select(entry => new MccMcpRecentEventEntry + { + Id = entry.Id, + TimestampUtc = entry.TimestampUtc, + Type = entry.Type, + Data = entry.Data + }) + .ToArray(); + } + + public static void Clear() + { + MccObservedStateStore.ClearRecentEvents(); + } +} diff --git a/MinecraftClient/Mcp/MccMcpResult.cs b/MinecraftClient/Mcp/MccMcpResult.cs new file mode 100644 index 00000000..0c12fbf2 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpResult.cs @@ -0,0 +1,33 @@ +using System; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpResult +{ + public bool Success { get; init; } + public string? ErrorCode { get; init; } + public string? Message { get; init; } + public object? Data { get; init; } + + public static MccMcpResult Ok(object? data = null, string? message = null) + { + return new MccMcpResult + { + Success = true, + Data = data, + Message = message + }; + } + + public static MccMcpResult Fail(string errorCode, string? message = null, object? data = null) + { + ArgumentException.ThrowIfNullOrEmpty(errorCode); + return new MccMcpResult + { + Success = false, + ErrorCode = errorCode, + Message = message, + Data = data + }; + } +} diff --git a/MinecraftClient/Mcp/MccMcpRuntimeStateStore.cs b/MinecraftClient/Mcp/MccMcpRuntimeStateStore.cs new file mode 100644 index 00000000..c9c3daf0 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpRuntimeStateStore.cs @@ -0,0 +1,47 @@ +using System; +using MinecraftClient.Scripting; + +namespace MinecraftClient.Mcp; + +public sealed class MccMcpRuntimeStateSnapshot +{ + public long? WorldAge { get; init; } + public long? TimeOfDay { get; init; } + public float? RainLevel { get; init; } + public float? ThunderLevel { get; init; } +} + +public static class MccMcpRuntimeStateStore +{ + public static void SetTime(long newWorldAge, long newTimeOfDay) + { + MccObservedStateStore.SetTime(newWorldAge, newTimeOfDay); + } + + public static void SetRainLevel(float level) + { + MccObservedStateStore.SetRainLevel(level); + } + + public static void SetThunderLevel(float level) + { + MccObservedStateStore.SetThunderLevel(level); + } + + public static MccMcpRuntimeStateSnapshot GetSnapshot() + { + MccRuntimeStateSnapshot snapshot = MccObservedStateStore.GetRuntimeStateSnapshot(); + return new MccMcpRuntimeStateSnapshot + { + WorldAge = snapshot.WorldAge, + TimeOfDay = snapshot.TimeOfDay, + RainLevel = snapshot.RainLevel, + ThunderLevel = snapshot.ThunderLevel + }; + } + + public static void Clear() + { + MccObservedStateStore.ClearRuntimeState(); + } +} diff --git a/MinecraftClient/Mcp/MccMcpToolSet.cs b/MinecraftClient/Mcp/MccMcpToolSet.cs new file mode 100644 index 00000000..84305e34 --- /dev/null +++ b/MinecraftClient/Mcp/MccMcpToolSet.cs @@ -0,0 +1,401 @@ +using System.ComponentModel; +using ModelContextProtocol.Server; + +namespace MinecraftClient.Mcp; + +[McpServerToolType] +public sealed class MccMcpToolSet +{ + private readonly IMccMcpCapabilities capabilities; + private readonly MccMcpGuidanceProvider guidanceProvider; + + public MccMcpToolSet(IMccMcpCapabilities capabilities, MccMcpGuidanceProvider guidanceProvider) + { + this.capabilities = capabilities; + this.guidanceProvider = guidanceProvider; + } + + [McpServerTool(Name = "mcc_session_status"), Description("Get current MCC session and feature status.")] + public object SessionStatus() + { + return capabilities.GetSessionStatus(); + } + + [McpServerTool(Name = "mcc_server_info"), Description("Get active MCC server connection info and current TPS.")] + public object ServerInfo() + { + return capabilities.GetServerInfo(); + } + + [McpServerTool(Name = "mcc_player_state"), Description("Get current controlled player state.")] + public object PlayerState() + { + return capabilities.GetPlayerState(); + } + + [McpServerTool(Name = "mcc_world_state"), Description("Get current world state, chunk loading progress, and last observed runtime time/weather values.")] + public object WorldState() + { + return capabilities.GetWorldState(); + } + + [McpServerTool(Name = "mcc_chunk_status"), Description("Get chunk loading status for the player location or an explicit world coordinate.")] + public object ChunkStatus(double? x = null, double? y = null, double? z = null) + { + return capabilities.GetChunkStatus(x, y, z); + } + + [McpServerTool(Name = "mcc_raycast_block"), Description("Raycast from the player's current view and return the first non-air block hit.")] + public object RaycastBlock(double maxDistance = 8.0, bool includeNeighbors = false) + { + return capabilities.RaycastBlock(maxDistance, includeNeighbors); + } + + [McpServerTool(Name = "mcc_path_preview"), Description("Compute a path preview to a target world coordinate without moving there.")] + public object PathPreview(double x, double y, double z, bool allowUnsafe = false, int maxOffset = 0, int minOffset = 0, int timeoutMs = 0, int maxWaypoints = 128) + { + return capabilities.PreviewPath(x, y, z, allowUnsafe, maxOffset, minOffset, timeoutMs, maxWaypoints); + } + + [McpServerTool(Name = "mcc_players_list"), Description("List currently known online players.")] + public object PlayersList() + { + return capabilities.GetPlayersList(); + } + + [McpServerTool(Name = "mcc_players_detailed"), Description("List online players with UUID, latency, gamemode, and tracked coordinates when available.")] + public object PlayersDetailed(bool includeSelf = false, bool includeCoordinates = true) + { + return capabilities.GetPlayersDetailed(includeSelf, includeCoordinates); + } + + [McpServerTool(Name = "mcc_player_stats"), Description("Get current controlled player stats, orientation, and location.")] + public object PlayerStats() + { + return capabilities.GetPlayerStats(); + } + + [McpServerTool(Name = "mcc_status_effects"), Description("Get active player status effects only.")] + public object StatusEffects() + { + return capabilities.GetStatusEffects(); + } + + [McpServerTool(Name = "mcc_recent_events"), Description("Get recent high-signal MCP runtime events after a given event ID.")] + public object RecentEvents(long afterId = 0, int maxCount = 50, string? typeFilter = null) + { + return capabilities.GetRecentEvents(afterId, maxCount, typeFilter); + } + + [McpServerTool(Name = "mcc_loaded_bots"), Description("List currently loaded MCC bots and scripts.")] + public object LoadedBots() + { + return capabilities.GetLoadedBots(); + } + + [McpServerTool(Name = "mcc_chat_history"), Description("Get recent chat/system lines seen by MCC.")] + public object ChatHistory(int maxCount = 50, bool includeJson = false) + { + return capabilities.GetChatHistory(maxCount, includeJson); + } + + [McpServerTool(Name = "mcc_internal_commands_list"), Description("List available MCC internal commands with usage and description.")] + public object InternalCommandsList() + { + return capabilities.GetInternalCommands(); + } + + [McpServerTool(Name = "mcc_agent_guidance"), Description("Get the canonical MCC MCP Operator Prompt bundle for external agents using this MCP server.")] + public object AgentGuidance() + { + return guidanceProvider.GetToolPayload(); + } + + [McpServerTool(Name = "mcc_materials_list"), Description("List known MCC material names with optional filtering.")] + public object MaterialsList(string? filter = null, int maxCount = 500) + { + return capabilities.GetMaterialsList(filter, maxCount); + } + + [McpServerTool(Name = "mcc_block_types_list"), Description("List known MCC block type names with optional filtering.")] + public object BlockTypesList(string? filter = null, int maxCount = 500) + { + return capabilities.GetBlockTypesList(filter, maxCount); + } + + [McpServerTool(Name = "mcc_entity_types_list"), Description("List known MCC entity type names with optional filtering.")] + public object EntityTypesList(string? filter = null, int maxCount = 500) + { + return capabilities.GetEntityTypesList(filter, maxCount); + } + + [McpServerTool(Name = "mcc_send_chat"), Description("Send chat text or slash-command to the connected Minecraft server.")] + public object SendChat([Description("Text to send to server chat.")] string text) + { + return capabilities.SendChat(text); + } + + [McpServerTool(Name = "mcc_quit_client"), Description("Quit MCC client process cleanly.")] + public object QuitClient() + { + return capabilities.QuitClient(); + } + + [McpServerTool(Name = "mcc_disconnect"), Description("Disconnect MCC from the current server without quitting the process.")] + public object Disconnect() + { + return capabilities.DisconnectClient(); + } + + [McpServerTool(Name = "mcc_respawn"), Description("Send the respawn packet when the controlled player is dead.")] + public object Respawn() + { + return capabilities.Respawn(); + } + + [McpServerTool(Name = "mcc_run_internal_command"), Description("Run an internal MCC command.")] + public object RunInternalCommand([Description("MCC command line without leading slash.")] string command) + { + return capabilities.RunInternalCommand(command); + } + + [McpServerTool(Name = "mcc_animation"), Description("Play a hand-swing animation with the selected hand.")] + public object Animation(string hand = "MainHand") + { + return capabilities.PlayAnimation(hand); + } + + [McpServerTool(Name = "mcc_toggle_sneak"), Description("Explicitly enable or disable sneaking.")] + public object ToggleSneak(bool enabled) + { + return capabilities.ToggleSneak(enabled); + } + + [McpServerTool(Name = "mcc_toggle_sprint"), Description("Explicitly send start or stop sprinting entity actions.")] + public object ToggleSprint(bool enabled) + { + return capabilities.ToggleSprint(enabled); + } + + [McpServerTool(Name = "mcc_change_hotbar_slot"), Description("Change active hotbar slot (1-9).")] + public object ChangeHotbarSlot(int slot) + { + return capabilities.ChangeHotbarSlot(slot); + } + + [McpServerTool(Name = "mcc_select_item"), Description("Select a hotbar item by item type without rearranging inventory contents.")] + public object SelectItem(string itemType, bool preferLowestSlot = true) + { + return capabilities.SelectHotbarItem(itemType, preferLowestSlot); + } + + [McpServerTool(Name = "mcc_use_item_on_hand"), Description("Use the currently held item.")] + public object UseItemOnHand() + { + return capabilities.UseItemOnHand(); + } + + [McpServerTool(Name = "mcc_use_item_on_block"), Description("Use currently held item on a target block location.")] + public object UseItemOnBlock(double x, double y, double z) + { + return capabilities.UseItemOnBlock(x, y, z); + } + + [McpServerTool(Name = "mcc_dig_block"), Description("Dig a block at target location.")] + public object DigBlock(double x, double y, double z, double durationSeconds = 0) + { + return capabilities.DigBlock(x, y, z, durationSeconds); + } + + [McpServerTool(Name = "mcc_place_block"), Description("Place the currently held block/item at a target block location.")] + public object PlaceBlock(int x, int y, int z, string face = "Up", string hand = "MainHand", bool lookAtBlock = false) + { + return capabilities.PlaceBlock(x, y, z, face, hand, lookAtBlock); + } + + [McpServerTool(Name = "mcc_entity_interact"), Description("Interact with a tracked entity.")] + public object EntityInteract(int entityId, string interaction = "Interact", string hand = "MainHand") + { + return capabilities.InteractEntity(entityId, interaction, hand); + } + + [McpServerTool(Name = "mcc_entity_attack"), Description("Attack a tracked entity explicitly.")] + public object EntityAttack(int entityId) + { + return capabilities.AttackEntity(entityId); + } + + [McpServerTool(Name = "mcc_block_scan"), Description("Scan nearby blocks around player location.")] + public object BlockScan(int radius = 3, int maxCount = 200, string? materialFilter = null) + { + return capabilities.ScanNearbyBlocks(radius, maxCount, materialFilter); + } + + [McpServerTool(Name = "mcc_blocks_find"), Description("Find nearby blocks by block name/type query or block ID.")] + public object BlocksFind(string? query = null, int radius = 6, int maxCount = 200, bool exactMatch = false) + { + return capabilities.FindBlocks(query, radius, maxCount, exactMatch); + } + + [McpServerTool(Name = "mcc_player_nearby"), Description("Check if any player, or a specific player, is nearby.")] + public object PlayerNearby(string? playerName = null, double radius = 32, bool includeSelf = false) + { + return capabilities.IsPlayerNearby(playerName, radius, includeSelf); + } + + [McpServerTool(Name = "mcc_player_locate"), Description("Locate a tracked player entity by name and return exact coordinates when available.")] + public object PlayerLocate(string playerName, bool includeSelf = false) + { + return capabilities.LocatePlayer(playerName, includeSelf); + } + + [McpServerTool(Name = "mcc_entity_nearest"), Description("Return the nearest tracked entity matching the requested filters.")] + public object EntityNearest(string? typeFilter = null, string? nameFilter = null, double radius = 64.0, bool includePlayers = true) + { + return capabilities.FindNearestEntity(typeFilter, nameFilter, radius, includePlayers); + } + + [McpServerTool(Name = "mcc_can_reach_position"), Description("Check whether MCC can currently path to a world coordinate without moving there.")] + public object CanReachPosition(double x, double y, double z, bool allowUnsafe = false, int maxOffset = 0, int minOffset = 0, int timeoutMs = 0) + { + return capabilities.CanReachPosition(x, y, z, allowUnsafe, maxOffset, minOffset, timeoutMs); + } + + [McpServerTool(Name = "mcc_move_to"), Description("Request movement/pathing to a world coordinate and verify arrival.")] + public object MoveTo(double x, double y, double z, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, int timeoutMs = 0) + { + return capabilities.MoveTo(x, y, z, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeoutMs); + } + + [McpServerTool(Name = "mcc_move_to_player"), Description("Locate a tracked player entity, request movement/pathing, and verify arrival.")] + public object MoveToPlayer(string playerName, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, int timeoutMs = 0) + { + return capabilities.MoveToPlayer(playerName, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeoutMs); + } + + [McpServerTool(Name = "mcc_look_at"), Description("Rotate player view toward world coordinates.")] + public object LookAt(double x, double y, double z) + { + return capabilities.LookAt(x, y, z); + } + + [McpServerTool(Name = "mcc_look_direction"), Description("Rotate player view to a cardinal direction or straight up/down.")] + public object LookDirection(string direction) + { + return capabilities.LookDirection(direction); + } + + [McpServerTool(Name = "mcc_look_angles"), Description("Rotate player view to explicit yaw and pitch angles.")] + public object LookAngles(float yaw, float pitch) + { + return capabilities.LookAngles(yaw, pitch); + } + + [McpServerTool(Name = "mcc_inventory_snapshot"), Description("Get a snapshot of one inventory.")] + public object InventorySnapshot([Description("Inventory ID. 0 is the player inventory.")] int inventoryId = 0) + { + return capabilities.GetInventorySnapshot(inventoryId); + } + + [McpServerTool(Name = "mcc_inventory_search"), Description("Search the player inventory and optionally open containers for items matching a query.")] + public object InventorySearch(string query, int maxCount = 100, bool exactMatch = false, bool includeContainers = true) + { + return capabilities.SearchInventories(query, maxCount, exactMatch, includeContainers); + } + + [McpServerTool(Name = "mcc_inventories_list"), Description("List currently open inventories and containers known to MCC.")] + public object InventoriesList() + { + return capabilities.ListInventories(); + } + + [McpServerTool(Name = "mcc_container_open_at"), Description("Open an interactable container block at world coordinates and wait for the container inventory to appear.")] + public object ContainerOpenAt(int x, int y, int z, int timeoutMs = 0, bool closeCurrent = true) + { + return capabilities.OpenContainerAt(x, y, z, timeoutMs, closeCurrent); + } + + [McpServerTool(Name = "mcc_container_close"), Description("Close an open non-player container. Use inventoryId=-1 to close the active container.")] + public object ContainerClose([Description("Container inventory ID, or -1 for the active non-player container.")] int inventoryId = -1, int timeoutMs = 0) + { + return capabilities.CloseContainer(inventoryId, timeoutMs); + } + + [McpServerTool(Name = "mcc_inventory_window_action"), Description("Perform a window action on an inventory slot.")] + public object InventoryWindowAction(int inventoryId, int slotId, [Description("WindowActionType enum name, e.g. LeftClick or ShiftClick.")] string actionType) + { + return capabilities.InventoryWindowAction(inventoryId, slotId, actionType); + } + + [McpServerTool(Name = "mcc_inventory_drop_item"), Description("Drop an exact item count from an inventory by item type.")] + public object InventoryDropItem( + [Description("Item type enum name (e.g. Diamond).")] string itemType, + [Description("Exact number of items to drop.")] int count, + [Description("Inventory ID. 0 is the player inventory.")] int inventoryId = 0, + [Description("Prefer dropping from larger stacks first when true.")] bool preferStack = false) + { + return capabilities.DropInventoryItem(itemType, count, inventoryId, preferStack); + } + + [McpServerTool(Name = "mcc_container_deposit_item"), Description("Move an exact item count from the player inventory into an open container and verify the transfer.")] + public object ContainerDepositItem( + [Description("Item type enum name (e.g. Diamond).")] string itemType, + [Description("Exact number of items to move into the container.")] int count, + [Description("Container inventory ID, or -1 for the active non-player container.")] int inventoryId = -1, + [Description("Prefer larger source stacks first when true.")] bool preferLargestStack = true) + { + return capabilities.DepositContainerItem(itemType, count, inventoryId, preferLargestStack); + } + + [McpServerTool(Name = "mcc_container_withdraw_item"), Description("Move an exact item count from an open container into the player inventory and verify the transfer.")] + public object ContainerWithdrawItem( + [Description("Item type enum name (e.g. Diamond).")] string itemType, + [Description("Exact number of items to move into the player inventory.")] int count, + [Description("Container inventory ID, or -1 for the active non-player container.")] int inventoryId = -1, + [Description("Prefer larger source stacks first when true.")] bool preferLargestStack = true) + { + return capabilities.WithdrawContainerItem(itemType, count, inventoryId, preferLargestStack); + } + + [McpServerTool(Name = "mcc_entities_query"), Description("Query tracked entities.")] + public object EntitiesQuery([Description("Maximum entities to return.")] int maxCount = 50) + { + return capabilities.QueryEntities(maxCount); + } + + [McpServerTool(Name = "mcc_entities_list"), Description("List tracked entities with optional type and radius filtering.")] + public object EntitiesList(int maxCount = 100, string? typeFilter = null, double radius = 0) + { + return capabilities.ListEntities(maxCount, typeFilter, radius); + } + + [McpServerTool(Name = "mcc_entity_info"), Description("Get detailed info for one tracked entity.")] + public object EntityInfo(int entityId, bool includeMetadata = false, bool includeEquipment = true, bool includeEffects = true) + { + return capabilities.GetEntityInfo(entityId, includeMetadata, includeEquipment, includeEffects); + } + + [McpServerTool(Name = "mcc_signs_find"), Description("Find nearby signs whose text exactly matches or contains the requested text.")] + public object SignsFind(string text, bool exactMatch = false, int radius = 16, int maxCount = 50, bool includeBackText = true) + { + return capabilities.FindSigns(text, exactMatch, radius, maxCount, includeBackText); + } + + [McpServerTool(Name = "mcc_items_list"), Description("List nearby dropped item entities with optional item type filtering.")] + public object ItemsList(string? itemType = null, double radius = 32, int maxCount = 100) + { + return capabilities.ListItemEntities(itemType, radius, maxCount); + } + + [McpServerTool(Name = "mcc_items_pickup"), Description("Move to and pick up nearby dropped items of a given item type.")] + public object ItemsPickup(string itemType, double radius = 32, int maxItems = 20, bool allowUnsafe = false, int timeoutMs = 0) + { + return capabilities.PickupItems(itemType, radius, maxItems, allowUnsafe, timeoutMs); + } + + [McpServerTool(Name = "mcc_world_block_at"), Description("Get block information at world coordinates.")] + public object WorldBlockAt(int x, int y, int z) + { + return capabilities.GetWorldBlockAt(x, y, z); + } +} diff --git a/MinecraftClient/Mcp/Prompts/MccMcpOperatorPrompt.md b/MinecraftClient/Mcp/Prompts/MccMcpOperatorPrompt.md new file mode 100644 index 00000000..7a24541d --- /dev/null +++ b/MinecraftClient/Mcp/Prompts/MccMcpOperatorPrompt.md @@ -0,0 +1,139 @@ +# MCC MCP Operator Prompt + +Use the MCC MCP toolset as the source of truth for game state and action results. +Do not guess what happened from intent alone. +If tool results and fresh observations disagree, trust the freshest direct observation and report the conflict honestly. + +## Operating Loop + +1. Inspect the current situation before acting. +2. Make the shortest plan that can succeed. +3. Use the smallest set of high-signal tools needed to act. +4. Verify the outcome with fresh tool calls that read the state again after the action. +5. Report only what is verified, and clearly label anything inferred, conflicting, or still unknown. + +If the request is purely conversational and does not require MCC state, answer directly instead of wasting tool calls. +If the user asks for a before/after comparison, keep or obtain explicit before and after observations. If you do not have a verified baseline, say so instead of reconstructing history from memory. + +## Tool Selection Rules + +- Start with `mcc_session_status` whenever connection state, enabled capabilities, or feature availability is uncertain. +- Prefer direct inspection tools such as `mcc_world_state`, `mcc_chunk_status`, `mcc_player_state`, `mcc_player_stats`, `mcc_players_detailed`, `mcc_entities_list`, `mcc_entity_nearest`, `mcc_blocks_find`, `mcc_raycast_block`, `mcc_items_list`, `mcc_inventory_snapshot`, and `mcc_inventory_search` before taking physical actions. +- For player-relative tasks, prefer `mcc_player_locate` or `mcc_players_detailed` before movement so the target is grounded in a fresh tracked position. +- Prefer purpose-built action tools over low-level escape hatches. +- Prefer `mcc_container_open_at`, `mcc_container_deposit_item`, and `mcc_container_withdraw_item` over `mcc_inventory_window_action` for chest or container work. +- Use `mcc_path_preview`, `mcc_can_reach_position`, or a locating tool before pathing when reachability or final approach quality is uncertain. +- Use `mcc_select_item` instead of manual slot changes when the goal is "hold the right item now". +- Use `mcc_look_direction`, `mcc_look_angles`, or `mcc_look_at` before `mcc_raycast_block`, `mcc_use_item_on_block`, or precise block interaction when view direction matters. +- Use `mcc_recent_events` when verifying outcomes that should produce a clear runtime event, such as `inventory_open`, `inventory_close`, `death`, `respawn`, `title`, or `actionbar`. +- Use `mcc_status_effects` when active effects matter, instead of inferring them from health or movement behavior. +- Use `mcc_loaded_bots` when bot/script presence could affect observed behavior. +- For "give this item to that player" requests, remember there is usually no direct inventory-to-inventory transfer tool. The normal MCP-compatible handoff is to move near the player and drop the item near them unless a more specific interaction tool clearly applies. +- Use `mcc_run_internal_command` only when no purpose-built MCP tool covers the task cleanly. +- Treat `success=false`, `action_incomplete`, `capability_disabled`, `feature_disabled`, and `invalid_args` as failed or partial observations, not success. +- After `invalid_args`, simplify the call and try at most one nearby variant. Do not spam near-duplicate guesses. +- Do not repeat the same failing action over and over. If an action fails or arrives short, gather one new observation that changes the plan before retrying. + +## Verification Rules + +- World-state assumptions should be verified with `mcc_world_state` or `mcc_chunk_status` when chunk loading, dimension, or time/weather readiness affects the plan. +- When the user cares about deltas, keep explicit before and after evidence for the exact thing that changed. +- If an action tool reports success but a fresh observation disagrees, trust the fresh observation and report the action as failed, partial, or inconclusive. +- Movement is not complete just because a move request was accepted. Confirm `arrived=true` or verify the new location with a fresh state read. +- A later successful retry proves the final state, not that every earlier attempt also succeeded. +- A path preview is not proof of arrival. Treat `mcc_path_preview` as planning evidence only, then verify the actual move separately. +- Digging is not complete just because `mcc_dig_block` was invoked. Re-check the target block or nearby block search results. +- View-dependent block interaction should be verified with `mcc_raycast_block` or `mcc_world_block_at` before and after the action when precision matters. +- Item pickup is not complete just because the bot moved over an item. Re-check inventory state or nearby dropped-item entities. +- Hotbar selection is not complete just because `mcc_select_item` returned success. Confirm the selected slot or held state with `mcc_player_stats` or a fresh inventory read. +- Dropping an item is not fully verified from intent alone. Re-check the inventory with a fresh `mcc_inventory_snapshot`. When useful, also inspect nearby dropped-item entities with `mcc_items_list`. +- Removing an item from your inventory does not prove another player received it. It only proves the item left your inventory or moved elsewhere. Claim that you "gave" or "delivered" an item to a player only if the stronger claim is supported. Otherwise say that you dropped it near them. +- Container transfers are not complete just because a click or transfer request was accepted. Verify the resulting counts after the transfer. +- Entity targeting should be verified with `mcc_entity_nearest`, `mcc_entity_info`, or another fresh entity read if the target could have moved or despawned. +- Use `mcc_recent_events` to verify eventful outcomes such as inventory open/close, death, respawn, title/actionbar messages, or similar runtime signals. +- Chat or command effects should be verified through state changes, chat history, or another direct observation when possible. +- When evidence is partial, say exactly what was verified and what remains unverified. +- When observations conflict, report the conflict plainly instead of smoothing it over. + +## Best Practices + +- Query first, act second, verify third. +- Keep plans short and concrete. Long speculative tool chains usually make the result worse. +- Prefer high-signal tools that answer the real question directly. +- Prefer newer structured reads like `mcc_world_state`, `mcc_player_stats`, `mcc_players_detailed`, `mcc_inventory_search`, and `mcc_recent_events` when they answer the question more directly than older generic tools. +- Use structured inventory and container tools instead of raw slot manipulation whenever possible. +- Do not claim success from acceptance alone. Always pair actions with a follow-up observation. +- Use fresh post-action reads for high-value or irreversible actions such as dropping items, moving items between inventories, placing blocks, digging blocks, attacking entities, disconnecting, or quitting. +- Preserve baselines when the user is likely to ask "what changed?" or "what did you have before?" later. +- Distinguish verified facts, reasonable inferences, and unknowns in the final answer. +- Use precise verbs. "Moved near", "dropped", "opened", "deposited", "withdrew", and "picked up" are stronger and safer than vague success language. +- Reserve "gave to player", "delivered", or similar wording for cases where that stronger claim is actually supported by evidence. +- If a tool says a capability or feature is disabled, stop using tools from that category and explain the limitation. +- If a path fails or arrives short, revise the plan using the latest position instead of blindly retrying the same action. +- Use `mcc_quit_client` to stop MCC. Do not send bare `quit` or `exit` through chat. +- Keep the final response concise and grounded in the evidence you actually collected. + +## Example Scenarios + +### Move to a player and confirm proximity + +User intent: "Find Zarko and move near them." + +Good flow: +- call `mcc_player_locate` or `mcc_players_list` to confirm the player is known +- if needed, call `mcc_players_detailed` for exact coordinates and `mcc_path_preview` or `mcc_can_reach_position` for the target area +- call `mcc_move_to_player` +- verify `arrived=true` or confirm the new position with `mcc_player_stats` +- report whether proximity was verified or only partially achieved + +### Drop an item for a player and report the result honestly + +User intent: "Move to Zarko and give them one dirt." + +Good flow: +- call `mcc_player_locate` to ground the target player +- call `mcc_inventory_snapshot` or `mcc_inventory_search` if item availability is uncertain +- call `mcc_move_to_player` +- call `mcc_inventory_drop_item` +- verify the result with a fresh `mcc_inventory_snapshot` +- if useful, call `mcc_items_list` nearby to confirm a dropped item entity exists near the handoff location +- report "dropped 1 dirt near Zarko" unless you actually observed stronger delivery evidence such as pickup + +### Open a chest, move an exact item count, and verify the result + +User intent: "Put 5 diamonds in the chest at 11000 64 11021." + +Good flow: +- call `mcc_container_open_at` +- inspect current state with `mcc_inventory_search` or `mcc_inventory_snapshot` if item availability is unclear +- call `mcc_container_deposit_item` or `mcc_container_withdraw_item` +- verify the resulting counts from the transfer result and, when useful, a fresh inventory snapshot or `mcc_recent_events` +- report the exact verified delta, not just that the action was attempted + +### Collect nearby dropped items or dig target blocks and verify the outcome + +User intent: "Pick up nearby apples" or "Break those logs and collect them." + +Good flow: +- call `mcc_items_list`, `mcc_blocks_find`, or `mcc_raycast_block` to locate the target +- move only if the target is not already reachable from the current position +- call `mcc_items_pickup` for dropped items, or `mcc_dig_block` in a sensible order for blocks +- verify the result with `mcc_items_list`, `mcc_inventory_snapshot`, or a fresh block query +- if the result is partial, say what changed and what still remains + +### Answer a before/after question without inventing history + +User intent: "What changed after that action?" or "What did you have before?" + +Good flow: +- if you already captured before and after reads, compare them directly +- if you only have current state, say what is currently verified and explicitly note that the earlier baseline was not captured +- do not backfill a historical claim just because it would make the story sound consistent + +## Output Style + +- Lead with the outcome the user cares about. +- Include the small set of observations that justify the answer. +- If something failed, say what failed, what was verified anyway, and the next sensible step. +- If you dropped an item near a player but did not observe pickup, say exactly that. +- Do not embellish uncertain results. diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 70470019..4307393e 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -1,6 +1,6 @@ - net7.0 + net10.0 Exe publish\ false @@ -8,6 +8,9 @@ enable true true + + false false @@ -18,6 +21,12 @@ MinecraftClient.Program + + + + + + @@ -28,48 +37,29 @@ - - - - + + + + + - - - - - - - - + + + + + + + + + + NU1701 - - + - - - - - - - - - - - - - - - - - - - - - - + diff --git a/MinecraftClient/Physics/Aabb.cs b/MinecraftClient/Physics/Aabb.cs new file mode 100644 index 00000000..5ccff502 --- /dev/null +++ b/MinecraftClient/Physics/Aabb.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace MinecraftClient.Physics +{ + /// + /// Axis-aligned bounding box, mirrors net.minecraft.world.phys.AABB. + /// Immutable — mutating methods return new instances. + /// + public readonly struct Aabb : IEquatable + { + public static readonly Aabb Empty = new(0, 0, 0, 0, 0, 0); + + public readonly double MinX, MinY, MinZ; + public readonly double MaxX, MaxY, MaxZ; + + public Aabb(double x1, double y1, double z1, double x2, double y2, double z2) + { + MinX = Math.Min(x1, x2); + MinY = Math.Min(y1, y2); + MinZ = Math.Min(z1, z2); + MaxX = Math.Max(x1, x2); + MaxY = Math.Max(y1, y2); + MaxZ = Math.Max(z1, z2); + } + + /// + /// Create a player-style AABB centered on feetX/Z with given width and height + /// + public static Aabb OfSize(double centerX, double feetY, double centerZ, double width, double height) + { + double hw = width / 2.0; + return new Aabb(centerX - hw, feetY, centerZ - hw, centerX + hw, feetY + height, centerZ + hw); + } + + /// + /// Full block AABB at given integer position + /// + public static Aabb BlockAt(int x, int y, int z) => + new(x, y, z, x + 1.0, y + 1.0, z + 1.0); + + public double XSize => MaxX - MinX; + public double YSize => MaxY - MinY; + public double ZSize => MaxZ - MinZ; + + public double Min(int axis) => axis switch { 0 => MinX, 1 => MinY, _ => MinZ }; + public double Max(int axis) => axis switch { 0 => MaxX, 1 => MaxY, _ => MaxZ }; + + /// + /// Expand toward a movement direction (vanilla expandTowards) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb ExpandTowards(double dx, double dy, double dz) + { + double minX = MinX, minY = MinY, minZ = MinZ; + double maxX = MaxX, maxY = MaxY, maxZ = MaxZ; + if (dx < 0) minX += dx; else if (dx > 0) maxX += dx; + if (dy < 0) minY += dy; else if (dy > 0) maxY += dy; + if (dz < 0) minZ += dz; else if (dz > 0) maxZ += dz; + return new Aabb(minX, minY, minZ, maxX, maxY, maxZ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb ExpandTowards(Vec3d v) => ExpandTowards(v.X, v.Y, v.Z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb Inflate(double x, double y, double z) => + new(MinX - x, MinY - y, MinZ - z, MaxX + x, MaxY + y, MaxZ + z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb Inflate(double v) => Inflate(v, v, v); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb Deflate(double x, double y, double z) => Inflate(-x, -y, -z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb Move(double dx, double dy, double dz) => + new(MinX + dx, MinY + dy, MinZ + dz, MaxX + dx, MaxY + dy, MaxZ + dz); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Aabb Move(Vec3d v) => Move(v.X, v.Y, v.Z); + + /// + /// Strict overlap test (vanilla uses < and >, not <=) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Intersects(Aabb other) => + MinX < other.MaxX && MaxX > other.MinX && + MinY < other.MaxY && MaxY > other.MinY && + MinZ < other.MaxZ && MaxZ > other.MinZ; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Intersects(double x1, double y1, double z1, double x2, double y2, double z2) => + MinX < x2 && MaxX > x1 && MinY < y2 && MaxY > y1 && MinZ < z2 && MaxZ > z1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(double x, double y, double z) => + x >= MinX && x < MaxX && y >= MinY && y < MaxY && z >= MinZ && z < MaxZ; + + /// + /// Collide this AABB along a single axis against another AABB. + /// Returns the clamped movement distance. + /// + /// + /// Clip entity movement along X against a block shape (other). + /// Vanilla semantics: VoxelShape.collide(Axis.X, entityBox, movement). + /// + public double CollideX(Aabb other, double movement) + { + if (other.MaxY <= MinY || other.MinY >= MaxY || other.MaxZ <= MinZ || other.MinZ >= MaxZ) + return movement; + if (movement > 0.0 && other.MinX >= MaxX) + { + double d = other.MinX - MaxX; + if (d < movement) movement = d; + } + else if (movement < 0.0 && other.MaxX <= MinX) + { + double d = other.MaxX - MinX; + if (d > movement) movement = d; + } + return movement; + } + + public double CollideY(Aabb other, double movement) + { + if (other.MaxX <= MinX || other.MinX >= MaxX || other.MaxZ <= MinZ || other.MinZ >= MaxZ) + return movement; + if (movement > 0.0 && other.MinY >= MaxY) + { + double d = other.MinY - MaxY; + if (d < movement) movement = d; + } + else if (movement < 0.0 && other.MaxY <= MinY) + { + double d = other.MaxY - MinY; + if (d > movement) movement = d; + } + return movement; + } + + public double CollideZ(Aabb other, double movement) + { + if (other.MaxX <= MinX || other.MinX >= MaxX || other.MaxY <= MinY || other.MinY >= MaxY) + return movement; + if (movement > 0.0 && other.MinZ >= MaxZ) + { + double d = other.MinZ - MaxZ; + if (d < movement) movement = d; + } + else if (movement < 0.0 && other.MaxZ <= MinZ) + { + double d = other.MaxZ - MinZ; + if (d > movement) movement = d; + } + return movement; + } + + /// + /// Collide along an axis (0=X, 1=Y, 2=Z) against another AABB + /// + public double Collide(int axis, Aabb other, double movement) + { + return axis switch + { + 0 => CollideX(other, movement), + 1 => CollideY(other, movement), + 2 => CollideZ(other, movement), + _ => movement + }; + } + + public Vec3d GetCenter() => new( + (MinX + MaxX) * 0.5, + (MinY + MaxY) * 0.5, + (MinZ + MaxZ) * 0.5); + + public Vec3d GetBottomCenter() => new( + (MinX + MaxX) * 0.5, + MinY, + (MinZ + MaxZ) * 0.5); + + public bool Equals(Aabb other) => + MinX == other.MinX && MinY == other.MinY && MinZ == other.MinZ && + MaxX == other.MaxX && MaxY == other.MaxY && MaxZ == other.MaxZ; + + public override bool Equals(object? obj) => obj is Aabb a && Equals(a); + public override int GetHashCode() => HashCode.Combine(MinX, MinY, MinZ, MaxX, MaxY, MaxZ); + public override string ToString() => $"AABB[{MinX:F3},{MinY:F3},{MinZ:F3} -> {MaxX:F3},{MaxY:F3},{MaxZ:F3}]"; + + public static bool operator ==(Aabb a, Aabb b) => a.Equals(b); + public static bool operator !=(Aabb a, Aabb b) => !a.Equals(b); + } +} diff --git a/MinecraftClient/Physics/BlockShapeData.json b/MinecraftClient/Physics/BlockShapeData.json new file mode 100644 index 00000000..2a7f10ff --- /dev/null +++ b/MinecraftClient/Physics/BlockShapeData.json @@ -0,0 +1 @@ +{"shapes":{"0":[],"1":[[0.0,0.0,0.0,1.0,1.0,1.0]],"2":[[0.0,0.0,0.0,0.1875,0.5625,0.1875],[0.8125,0.0,0.0,1.0,0.5625,0.1875],[0.0,0.1875,0.1875,1.0,0.5625,1.0],[0.1875,0.1875,0.0,0.8125,0.5625,0.1875]],"3":[[0.0,0.0,0.8125,0.1875,0.5625,1.0],[0.8125,0.0,0.8125,1.0,0.5625,1.0],[0.0,0.1875,0.0,1.0,0.5625,0.8125],[0.1875,0.1875,0.8125,0.8125,0.5625,1.0]],"4":[[0.0,0.0,0.0,0.1875,0.5625,0.1875],[0.0,0.0,0.8125,0.1875,0.5625,1.0],[0.0,0.1875,0.1875,1.0,0.5625,0.8125],[0.1875,0.1875,0.0,1.0,0.5625,0.1875],[0.1875,0.1875,0.8125,1.0,0.5625,1.0]],"5":[[0.8125,0.0,0.0,1.0,0.5625,0.1875],[0.8125,0.0,0.8125,1.0,0.5625,1.0],[0.0,0.1875,0.0,0.8125,0.5625,1.0],[0.8125,0.1875,0.1875,1.0,0.5625,0.8125]],"6":[[0.0,0.0,0.25,1.0,1.0,1.0]],"7":[[0.0,0.0,0.0,0.75,1.0,1.0]],"8":[[0.0,0.0,0.0,1.0,1.0,0.75]],"9":[[0.25,0.0,0.0,1.0,1.0,1.0]],"10":[[0.0,0.0,0.0,1.0,0.75,1.0]],"11":[[0.0,0.25,0.0,1.0,1.0,1.0]],"12":[[0.0,0.0,0.0,1.0,1.0,0.25],[0.375,0.375,0.25,0.625,0.625,1.0]],"13":[[0.0,0.0,0.0,1.0,1.0,0.25],[0.375,0.375,0.25,0.625,0.625,1.25]],"14":[[0.75,0.0,0.0,1.0,1.0,1.0],[0.0,0.375,0.375,0.75,0.625,0.625]],"15":[[0.75,0.0,0.0,1.0,1.0,1.0],[-0.25,0.375,0.375,0.75,0.625,0.625]],"16":[[0.0,0.0,0.75,1.0,1.0,1.0],[0.375,0.375,0.0,0.625,0.625,0.75]],"17":[[0.0,0.0,0.75,1.0,1.0,1.0],[0.375,0.375,-0.25,0.625,0.625,0.75]],"18":[[0.0,0.0,0.0,0.25,1.0,1.0],[0.25,0.375,0.375,1.0,0.625,0.625]],"19":[[0.0,0.0,0.0,0.25,1.0,1.0],[0.25,0.375,0.375,1.25,0.625,0.625]],"20":[[0.375,0.0,0.375,0.625,1.0,0.625],[0.0,0.75,0.0,0.375,1.0,1.0],[0.375,0.75,0.0,1.0,1.0,0.375],[0.375,0.75,0.625,1.0,1.0,1.0],[0.625,0.75,0.375,1.0,1.0,0.625]],"21":[[0.375,-0.25,0.375,0.625,1.0,0.625],[0.0,0.75,0.0,0.375,1.0,1.0],[0.375,0.75,0.0,1.0,1.0,0.375],[0.375,0.75,0.625,1.0,1.0,1.0],[0.625,0.75,0.375,1.0,1.0,0.625]],"22":[[0.0,0.0,0.0,1.0,0.25,1.0],[0.375,0.25,0.375,0.625,1.0,0.625]],"23":[[0.0,0.0,0.0,1.0,0.25,1.0],[0.375,0.25,0.375,0.625,1.25,0.625]],"24":[[0.0,0.0,0.6875,1.0,0.25,1.0],[0.0,0.25,0.8125,1.0,1.0,1.0],[0.0,0.75,0.6875,1.0,1.0,0.8125]],"25":[[0.0,0.0,0.0,1.0,0.25,0.3125],[0.0,0.25,0.0,1.0,1.0,0.1875],[0.0,0.75,0.1875,1.0,1.0,0.3125]],"26":[[0.6875,0.0,0.0,1.0,0.25,1.0],[0.8125,0.25,0.0,1.0,1.0,1.0],[0.6875,0.75,0.0,0.8125,1.0,1.0]],"27":[[0.0,0.0,0.0,0.3125,0.25,1.0],[0.0,0.25,0.0,0.1875,1.0,1.0],[0.1875,0.75,0.0,0.3125,1.0,1.0]],"28":[[0.0,0.0,0.0,1.0,1.0,0.5],[0.0,0.5,0.5,1.0,1.0,1.0]],"29":[[0.0,0.0,0.0,0.5,1.0,1.0],[0.5,0.0,0.0,1.0,1.0,0.5],[0.5,0.5,0.5,1.0,1.0,1.0]],"30":[[0.0,0.0,0.0,1.0,1.0,0.5],[0.5,0.0,0.5,1.0,1.0,1.0],[0.0,0.5,0.5,0.5,1.0,1.0]],"31":[[0.0,0.0,0.0,0.5,1.0,0.5],[0.0,0.5,0.5,1.0,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"32":[[0.5,0.0,0.0,1.0,1.0,0.5],[0.0,0.5,0.0,0.5,1.0,1.0],[0.5,0.5,0.5,1.0,1.0,1.0]],"33":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,1.0,1.0,0.5]],"34":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,0.5,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"35":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,1.0,1.0,0.5],[0.5,0.5,0.5,1.0,1.0,1.0]],"36":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,0.5,1.0,0.5]],"37":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"38":[[0.0,0.0,0.5,1.0,1.0,1.0],[0.0,0.5,0.0,1.0,1.0,0.5]],"39":[[0.0,0.0,0.5,1.0,1.0,1.0],[0.5,0.0,0.0,1.0,1.0,0.5],[0.0,0.5,0.0,0.5,1.0,0.5]],"40":[[0.0,0.0,0.0,0.5,1.0,1.0],[0.5,0.0,0.5,1.0,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"41":[[0.5,0.0,0.5,1.0,1.0,1.0],[0.0,0.5,0.0,0.5,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"42":[[0.0,0.0,0.5,0.5,1.0,1.0],[0.0,0.5,0.0,1.0,1.0,0.5],[0.5,0.5,0.5,1.0,1.0,1.0]],"43":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.5,1.0,1.0,1.0]],"44":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.5,1.0,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,0.5]],"45":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,0.5,1.0,1.0],[0.5,0.5,0.5,1.0,1.0,1.0]],"46":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.5,0.5,0.5,1.0,1.0,1.0]],"47":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.5,0.5,1.0,1.0]],"48":[[0.0,0.0,0.0,0.5,1.0,1.0],[0.5,0.5,0.0,1.0,1.0,1.0]],"49":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,0.5,1.0,1.0]],"50":[[0.5,0.0,0.0,1.0,1.0,1.0],[0.0,0.5,0.0,0.5,1.0,1.0]],"51":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.5,0.5,0.0,1.0,1.0,1.0]],"52":[[0.0625,0.0,0.0625,0.9375,0.875,0.9375]],"53":[[0.0625,0.0,0.0625,1.0,0.875,0.9375]],"54":[[0.0,0.0,0.0625,0.9375,0.875,0.9375]],"55":[[0.0625,0.0,0.0,0.9375,0.875,0.9375]],"56":[[0.0625,0.0,0.0625,0.9375,0.875,1.0]],"57":[[0.0,0.0,0.0,1.0,0.9375,1.0]],"58":[[0.0,0.0,0.0,0.1875,1.0,1.0]],"59":[[0.0,0.0,0.8125,1.0,1.0,1.0]],"60":[[0.8125,0.0,0.0,1.0,1.0,1.0]],"61":[[0.0,0.0,0.0,1.0,1.0,0.1875]],"62":[[0.0,0.0,0.8125,1.0,1.0,1.0]],"63":[[0.0,0.0,0.0,1.0,1.0,0.1875]],"64":[[0.8125,0.0,0.0,1.0,1.0,1.0]],"65":[[0.0,0.0,0.0,0.1875,1.0,1.0]],"66":[[0.0,0.875,0.375,1.0,1.0,0.625]],"67":[[0.375,0.875,0.0,0.625,1.0,1.0]],"68":[[0.0,0.0,0.0,1.0,0.125,1.0]],"69":[[0.0,0.0,0.0,1.0,0.25,1.0]],"70":[[0.0,0.0,0.0,1.0,0.375,1.0]],"71":[[0.0,0.0,0.0,1.0,0.5,1.0]],"72":[[0.0,0.0,0.0,1.0,0.625,1.0]],"73":[[0.0,0.0,0.0,1.0,0.75,1.0]],"74":[[0.0,0.0,0.0,1.0,0.875,1.0]],"75":[[0.0625,0.0,0.0625,0.9375,0.9375,0.9375]],"76":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"77":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"78":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"79":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"80":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"81":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"82":[[0.0,0.0,0.375,1.0,1.5,0.625]],"83":[[0.375,0.0,0.375,1.0,1.5,0.625]],"84":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"85":[[0.375,0.0,0.0,0.625,1.5,1.0]],"86":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"87":[[0.375,0.0,0.0,0.625,1.5,0.625]],"88":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"89":[[0.375,0.0,0.375,0.625,1.5,1.0]],"90":[[0.0,0.0,0.375,0.625,1.5,0.625]],"91":[[0.375,0.0,0.375,0.625,1.5,0.625]],"92":[[0.0,0.0,0.0,1.0,0.875,1.0]],"93":[[0.0625,0.0,0.0625,0.9375,0.5,0.9375]],"94":[[0.1875,0.0,0.0625,0.9375,0.5,0.9375]],"95":[[0.3125,0.0,0.0625,0.9375,0.5,0.9375]],"96":[[0.4375,0.0,0.0625,0.9375,0.5,0.9375]],"97":[[0.5625,0.0,0.0625,0.9375,0.5,0.9375]],"98":[[0.6875,0.0,0.0625,0.9375,0.5,0.9375]],"99":[[0.8125,0.0,0.0625,0.9375,0.5,0.9375]],"100":[[0.0,0.0,0.0,1.0,0.125,1.0]],"101":[[0.0,0.0,0.8125,1.0,1.0,1.0]],"102":[[0.0,0.8125,0.0,1.0,1.0,1.0]],"103":[[0.0,0.0,0.0,1.0,0.1875,1.0]],"104":[[0.0,0.0,0.0,1.0,1.0,0.1875]],"105":[[0.8125,0.0,0.0,1.0,1.0,1.0]],"106":[[0.0,0.0,0.0,0.1875,1.0,1.0]],"107":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"108":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"109":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"110":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"111":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"112":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"113":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"114":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"115":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"116":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"117":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"118":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"119":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"120":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"121":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"122":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"123":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"124":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"125":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"126":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"127":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"128":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"129":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"130":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"131":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"132":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"133":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"134":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"135":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"136":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"137":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"138":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"139":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"140":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"141":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"142":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"143":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"144":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"145":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"146":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"147":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"148":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"149":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"150":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"151":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"152":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"153":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"154":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"155":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"156":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"157":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"158":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"159":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"160":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"161":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"162":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"163":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"164":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"165":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"166":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"167":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"168":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"169":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"170":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"171":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"172":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"173":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"174":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"175":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"176":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"177":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"178":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"179":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"180":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"181":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"182":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"183":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"184":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"185":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"186":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"187":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"188":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"189":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"190":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"191":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"192":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"193":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"194":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"195":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"196":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"197":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"198":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"199":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"200":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"201":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"202":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"203":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"204":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"205":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"206":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"207":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"208":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"209":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"210":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"211":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"212":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"213":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"214":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"215":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"216":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"217":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"218":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"219":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"220":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"221":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"222":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"223":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"224":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"225":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"226":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"227":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"228":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"229":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"230":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"231":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"232":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"233":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"234":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"235":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"236":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"237":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"238":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"239":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"240":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"241":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"242":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"243":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"244":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"245":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"246":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"247":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"248":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"249":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"250":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"251":[[0.0,0.40625,0.40625,1.0,0.59375,0.59375]],"252":[[0.40625,0.0,0.40625,0.59375,1.0,0.59375]],"253":[[0.40625,0.40625,0.0,0.59375,0.59375,1.0]],"254":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"255":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"256":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"257":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"258":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"259":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"260":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"261":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"262":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"263":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"264":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"265":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"266":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"267":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"268":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"269":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"270":[[0.0,0.0,0.375,1.0,1.5,0.625]],"271":[[0.375,0.0,0.0,0.625,1.5,1.0]],"272":[[0.0625,0.0,0.0625,0.9375,0.09375,0.9375]],"273":[[0.0,0.5,0.0,1.0,1.0,1.0]],"274":[[0.0,0.0,0.0,1.0,0.5,1.0]],"275":[[0.25,0.0,0.25,0.75,1.5,0.75]],"276":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"277":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"278":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"279":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"280":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"281":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"282":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"283":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"284":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"285":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"286":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"287":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"288":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"289":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"290":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"291":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"292":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"293":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"294":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"295":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"296":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"297":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"298":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"299":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"300":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"301":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"302":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"303":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"304":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"305":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"306":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"307":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"308":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"309":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"310":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"311":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"312":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"313":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"314":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"315":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"316":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"317":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"318":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"319":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"320":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"321":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"322":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"323":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"324":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"325":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"326":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"327":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"328":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"329":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"330":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"331":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"332":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"333":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"334":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"335":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"336":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"337":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"338":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"339":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"340":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"341":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"342":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"343":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"344":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"345":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"346":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"347":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"348":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"349":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"350":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"351":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"352":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"353":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"354":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"355":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"356":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"357":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"358":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"359":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"360":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"361":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"362":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"363":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"364":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"365":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"366":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"367":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"368":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"369":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"370":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"371":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"372":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"373":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"374":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"375":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"376":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"377":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"378":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"379":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"380":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"381":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"382":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"383":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"384":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"385":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"386":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"387":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"388":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"389":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"390":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"391":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"392":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"393":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"394":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"395":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"396":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"397":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"398":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"399":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"400":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"401":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"402":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"403":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"404":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"405":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"406":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"407":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"408":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"409":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"410":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"411":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"412":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"413":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"414":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"415":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"416":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"417":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"418":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"419":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"420":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"421":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"422":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"423":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"424":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"425":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"426":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"427":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"428":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"429":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"430":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"431":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"432":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"433":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"434":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"435":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"436":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"437":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"438":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"439":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"440":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"441":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"442":[[0.0,0.0,0.375,1.0,1.5,0.625]],"443":[[0.375,0.0,0.375,1.0,1.5,0.625]],"444":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"445":[[0.375,0.0,0.0,0.625,1.5,1.0]],"446":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"447":[[0.375,0.0,0.0,0.625,1.5,0.625]],"448":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"449":[[0.375,0.0,0.375,0.625,1.5,1.0]],"450":[[0.0,0.0,0.375,0.625,1.5,0.625]],"451":[[0.375,0.0,0.375,0.625,1.5,0.625]],"452":[[0.0,0.0,0.0,1.0,0.75,1.0]],"453":[[0.0625,0.0,0.0625,0.9375,0.125,0.9375],[0.4375,0.125,0.4375,0.5625,0.875,0.5625]],"454":[[0.0,0.0,0.0,0.125,1.0,0.25],[0.0,0.0,0.75,0.125,1.0,1.0],[0.125,0.0,0.0,0.25,1.0,0.125],[0.125,0.0,0.875,0.25,1.0,1.0],[0.75,0.0,0.0,1.0,1.0,0.125],[0.75,0.0,0.875,1.0,1.0,1.0],[0.875,0.0,0.125,1.0,1.0,0.25],[0.875,0.0,0.75,1.0,1.0,0.875],[0.0,0.1875,0.25,1.0,0.25,0.75],[0.125,0.1875,0.125,0.875,0.25,0.25],[0.125,0.1875,0.75,0.875,0.25,0.875],[0.25,0.1875,0.0,0.75,1.0,0.125],[0.25,0.1875,0.875,0.75,1.0,1.0],[0.0,0.25,0.25,0.125,1.0,0.75],[0.875,0.25,0.25,1.0,1.0,0.75]],"455":[[0.0,0.0,0.0,1.0,0.8125,1.0],[0.25,0.8125,0.25,0.75,1.0,0.75]],"456":[[0.0,0.0,0.0,1.0,0.8125,1.0]],"457":[[0.0625,0.0,0.0625,0.9375,1.0,0.9375]],"458":[[0.375,0.4375,0.0625,0.625,0.75,0.3125]],"459":[[0.375,0.4375,0.6875,0.625,0.75,0.9375]],"460":[[0.0625,0.4375,0.375,0.3125,0.75,0.625]],"461":[[0.6875,0.4375,0.375,0.9375,0.75,0.625]],"462":[[0.3125,0.3125,0.0625,0.6875,0.75,0.4375]],"463":[[0.3125,0.3125,0.5625,0.6875,0.75,0.9375]],"464":[[0.0625,0.3125,0.3125,0.4375,0.75,0.6875]],"465":[[0.5625,0.3125,0.3125,0.9375,0.75,0.6875]],"466":[[0.25,0.1875,0.0625,0.75,0.75,0.5625]],"467":[[0.25,0.1875,0.4375,0.75,0.75,0.9375]],"468":[[0.0625,0.1875,0.25,0.5625,0.75,0.75]],"469":[[0.4375,0.1875,0.25,0.9375,0.75,0.75]],"470":[[0.0625,0.0,0.0625,0.9375,0.875,0.9375]],"471":[[0.25,0.0,0.25,0.75,1.5,0.75]],"472":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"473":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"474":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"475":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"476":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"477":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"478":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"479":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"480":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"481":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"482":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"483":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"484":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"485":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"486":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"487":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"488":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"489":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"490":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"491":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"492":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"493":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"494":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"495":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"496":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"497":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"498":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"499":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"500":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"501":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"502":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"503":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"504":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"505":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"506":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"507":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"508":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"509":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"510":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"511":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"512":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"513":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"514":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"515":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"516":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"517":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"518":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"519":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"520":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"521":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"522":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"523":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"524":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"525":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"526":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"527":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"528":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"529":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"530":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"531":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"532":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"533":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"534":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"535":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"536":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"537":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"538":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"539":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"540":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"541":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"542":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"543":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"544":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"545":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"546":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"547":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"548":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"549":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"550":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"551":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"552":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"553":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"554":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"555":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"556":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"557":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"558":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"559":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"560":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"561":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"562":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"563":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"564":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"565":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"566":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"567":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"568":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"569":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"570":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"571":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"572":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"573":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"574":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"575":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"576":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"577":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"578":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"579":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"580":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"581":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"582":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"583":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"584":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"585":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"586":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"587":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"588":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"589":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"590":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"591":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"592":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"593":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"594":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"595":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"596":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"597":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"598":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"599":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"600":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"601":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"602":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"603":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"604":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"605":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"606":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"607":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"608":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"609":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"610":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"611":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"612":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"613":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"614":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"615":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"616":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"617":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"618":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"619":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"620":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"621":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"622":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"623":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"624":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"625":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"626":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"627":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"628":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"629":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"630":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"631":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"632":[[0.25,0.0,0.25,0.75,1.5,0.75]],"633":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"634":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"635":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"636":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"637":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"638":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"639":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"640":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"641":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"642":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"643":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"644":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"645":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"646":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"647":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"648":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"649":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"650":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"651":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"652":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"653":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"654":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"655":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"656":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"657":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"658":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"659":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"660":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"661":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"662":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"663":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"664":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"665":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"666":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"667":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"668":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"669":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"670":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"671":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"672":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"673":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"674":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"675":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"676":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"677":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"678":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"679":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"680":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"681":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"682":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"683":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"684":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"685":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"686":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"687":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"688":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"689":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"690":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"691":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"692":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"693":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"694":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"695":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"696":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"697":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"698":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"699":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"700":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"701":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"702":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"703":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"704":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"705":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"706":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"707":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"708":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"709":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"710":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"711":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"712":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"713":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"714":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"715":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"716":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"717":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"718":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"719":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"720":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"721":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"722":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"723":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"724":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"725":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"726":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"727":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"728":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"729":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"730":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"731":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"732":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"733":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"734":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"735":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"736":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"737":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"738":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"739":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"740":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"741":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"742":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"743":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"744":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"745":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"746":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"747":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"748":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"749":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"750":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"751":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"752":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"753":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"754":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"755":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"756":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"757":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"758":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"759":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"760":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"761":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"762":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"763":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"764":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"765":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"766":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"767":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"768":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"769":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"770":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"771":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"772":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"773":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"774":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"775":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"776":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"777":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"778":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"779":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"780":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"781":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"782":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"783":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"784":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"785":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"786":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"787":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"788":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"789":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"790":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"791":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"792":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"793":[[0.3125,0.0,0.3125,0.6875,0.375,0.6875]],"794":[[0.25,0.0,0.25,0.75,0.5,0.75]],"795":[[0.25,0.25,0.5,0.75,0.75,1.0]],"796":[[0.25,0.25,0.0,0.75,0.75,0.5]],"797":[[0.5,0.25,0.25,1.0,0.75,0.75]],"798":[[0.0,0.25,0.25,0.5,0.75,0.75]],"799":[[0.1875,0.0,0.1875,0.8125,0.5,0.8125]],"800":[[0.1875,0.25,0.5,0.8125,0.75,1.0]],"801":[[0.1875,0.25,0.0,0.8125,0.75,0.5]],"802":[[0.5,0.25,0.1875,1.0,0.75,0.8125]],"803":[[0.0,0.25,0.1875,0.5,0.75,0.8125]],"804":[[0.125,0.0,0.125,0.875,0.25,0.875],[0.25,0.25,0.1875,0.75,0.3125,0.8125],[0.375,0.3125,0.25,0.625,1.0,0.75],[0.1875,0.625,0.0,0.375,1.0,1.0],[0.375,0.625,0.0,0.8125,1.0,0.25],[0.375,0.625,0.75,0.8125,1.0,1.0],[0.625,0.625,0.25,0.8125,1.0,0.75]],"805":[[0.125,0.0,0.125,0.875,0.25,0.875],[0.1875,0.25,0.25,0.8125,0.3125,0.75],[0.25,0.3125,0.375,0.75,1.0,0.625],[0.0,0.625,0.1875,0.25,1.0,0.8125],[0.25,0.625,0.1875,1.0,1.0,0.375],[0.25,0.625,0.625,1.0,1.0,0.8125],[0.75,0.625,0.375,1.0,1.0,0.625]],"806":[[0.0,0.0,0.0,1.0,0.375,1.0]],"807":[[0.375,0.0,0.375,0.625,0.6875,0.625],[0.25,0.25,0.25,0.375,0.6875,0.75],[0.375,0.25,0.25,0.75,0.6875,0.375],[0.375,0.25,0.625,0.75,0.6875,0.75],[0.625,0.25,0.375,0.75,0.6875,0.625],[0.0,0.625,0.0,0.25,0.6875,1.0],[0.25,0.625,0.0,1.0,0.6875,0.25],[0.25,0.625,0.75,1.0,0.6875,1.0],[0.75,0.625,0.25,1.0,0.6875,0.75],[0.0,0.6875,0.0,0.125,1.0,1.0],[0.125,0.6875,0.0,1.0,1.0,0.125],[0.125,0.6875,0.875,1.0,1.0,1.0],[0.875,0.6875,0.125,1.0,1.0,0.875]],"808":[[0.25,0.25,0.25,0.75,0.6875,0.75],[0.375,0.25,0.0,0.625,0.5,0.25],[0.0,0.625,0.0,0.25,0.6875,1.0],[0.25,0.625,0.0,1.0,0.6875,0.25],[0.25,0.625,0.75,1.0,0.6875,1.0],[0.75,0.625,0.25,1.0,0.6875,0.75],[0.0,0.6875,0.0,0.125,1.0,1.0],[0.125,0.6875,0.0,1.0,1.0,0.125],[0.125,0.6875,0.875,1.0,1.0,1.0],[0.875,0.6875,0.125,1.0,1.0,0.875]],"809":[[0.25,0.25,0.25,0.75,0.6875,0.75],[0.375,0.25,0.75,0.625,0.5,1.0],[0.0,0.625,0.0,0.25,0.6875,1.0],[0.25,0.625,0.0,1.0,0.6875,0.25],[0.25,0.625,0.75,1.0,0.6875,1.0],[0.75,0.625,0.25,1.0,0.6875,0.75],[0.0,0.6875,0.0,0.125,1.0,1.0],[0.125,0.6875,0.0,1.0,1.0,0.125],[0.125,0.6875,0.875,1.0,1.0,1.0],[0.875,0.6875,0.125,1.0,1.0,0.875]],"810":[[0.0,0.25,0.375,0.75,0.5,0.625],[0.25,0.25,0.25,0.75,0.6875,0.375],[0.25,0.25,0.625,0.75,0.6875,0.75],[0.25,0.5,0.375,0.75,0.6875,0.625],[0.0,0.625,0.0,0.25,0.6875,1.0],[0.25,0.625,0.0,1.0,0.6875,0.25],[0.25,0.625,0.75,1.0,0.6875,1.0],[0.75,0.625,0.25,1.0,0.6875,0.75],[0.0,0.6875,0.0,0.125,1.0,1.0],[0.125,0.6875,0.0,1.0,1.0,0.125],[0.125,0.6875,0.875,1.0,1.0,1.0],[0.875,0.6875,0.125,1.0,1.0,0.875]],"811":[[0.25,0.25,0.25,0.75,0.6875,0.75],[0.75,0.25,0.375,1.0,0.5,0.625],[0.0,0.625,0.0,0.25,0.6875,1.0],[0.25,0.625,0.0,1.0,0.6875,0.25],[0.25,0.625,0.75,1.0,0.6875,1.0],[0.75,0.625,0.25,1.0,0.6875,0.75],[0.0,0.6875,0.0,0.125,1.0,1.0],[0.125,0.6875,0.0,1.0,1.0,0.125],[0.125,0.6875,0.875,1.0,1.0,1.0],[0.875,0.6875,0.125,1.0,1.0,0.875]],"812":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"813":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"814":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"815":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"816":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"817":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"818":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"819":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"820":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"821":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"822":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"823":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"824":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"825":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"826":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"827":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"828":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"829":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"830":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"831":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"832":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"833":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"834":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"835":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"836":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"837":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"838":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"839":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"840":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"841":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"842":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"843":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"844":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"845":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"846":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"847":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"848":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"849":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"850":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"851":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"852":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"853":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"854":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"855":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"856":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"857":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"858":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"859":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"860":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"861":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"862":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"863":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"864":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"865":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"866":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"867":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"868":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"869":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"870":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"871":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"872":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"873":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"874":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"875":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"876":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"877":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"878":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"879":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"880":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"881":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"882":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"883":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"884":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"885":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"886":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"887":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"888":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"889":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"890":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"891":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"892":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"893":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"894":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"895":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"896":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"897":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"898":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"899":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"900":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"901":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"902":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"903":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"904":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"905":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"906":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"907":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"908":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"909":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"910":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"911":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"912":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"913":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"914":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"915":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"916":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"917":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"918":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"919":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"920":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"921":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"922":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"923":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"924":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"925":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"926":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"927":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"928":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"929":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"930":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"931":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"932":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"933":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"934":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"935":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"936":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"937":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"938":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"939":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"940":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"941":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"942":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"943":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"944":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"945":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"946":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"947":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"948":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"949":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"950":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"951":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"952":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"953":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"954":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"955":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"956":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"957":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"958":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"959":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"960":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"961":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"962":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"963":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"964":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"965":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"966":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"967":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"968":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"969":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"970":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"971":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"972":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"973":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"974":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"975":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"976":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"977":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"978":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"979":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"980":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"981":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"982":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"983":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"984":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"985":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"986":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"987":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"988":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"989":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"990":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"991":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"992":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"993":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"994":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"995":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"996":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"997":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"998":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"999":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"1000":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1001":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"1002":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"1003":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"1004":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1005":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1006":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1007":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1008":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1009":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1010":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"1011":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"1012":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1013":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"1014":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1015":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"1016":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1017":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"1018":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"1019":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"1020":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1021":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1022":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1023":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1024":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1025":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1026":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"1027":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"1028":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1029":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"1030":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1031":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"1032":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1033":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"1034":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"1035":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"1036":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1037":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1038":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1039":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1040":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1041":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1042":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"1043":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"1044":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1045":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"1046":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1047":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"1048":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1049":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"1050":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"1051":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"1052":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1053":[[0.4375,0.0,0.0,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1054":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1055":[[0.4375,0.0,0.0,0.5625,1.0,0.5625],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1056":[[0.0,0.0,0.4375,1.0,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1057":[[0.4375,0.0,0.4375,0.5625,1.0,1.0],[0.5625,0.0,0.4375,1.0,1.0,0.5625]],"1058":[[0.0,0.0,0.4375,1.0,1.0,0.5625]],"1059":[[0.4375,0.0,0.4375,1.0,1.0,0.5625]],"1060":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1061":[[0.4375,0.0,0.0,0.5625,1.0,1.0]],"1062":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.0,0.5625,1.0,0.4375]],"1063":[[0.4375,0.0,0.0,0.5625,1.0,0.5625]],"1064":[[0.0,0.0,0.4375,0.5625,1.0,0.5625],[0.4375,0.0,0.5625,0.5625,1.0,1.0]],"1065":[[0.4375,0.0,0.4375,0.5625,1.0,1.0]],"1066":[[0.0,0.0,0.4375,0.5625,1.0,0.5625]],"1067":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],"1068":[[0.0,0.0,0.0,1.0,0.0625,1.0]],"1069":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1070":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1071":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1072":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1073":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1074":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1075":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1076":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1077":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1078":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1079":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1080":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1081":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1082":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1083":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1084":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1085":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1086":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1087":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1088":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1089":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1090":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1091":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1092":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1093":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1094":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1095":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1096":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1097":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1098":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1099":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1100":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1101":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1102":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1103":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1104":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1105":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1106":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1107":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1108":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1109":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1110":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1111":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1112":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1113":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1114":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1115":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1116":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1117":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1118":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1119":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1120":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1121":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1122":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1123":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1124":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1125":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1126":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1127":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1128":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1129":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1130":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1131":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1132":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1133":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1134":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1135":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1136":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1137":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1138":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1139":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1140":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1141":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1142":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1143":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1144":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1145":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1146":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1147":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1148":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1149":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1150":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1151":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1152":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1153":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1154":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1155":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1156":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1157":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1158":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1159":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1160":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1161":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1162":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1163":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1164":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1165":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1166":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1167":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1168":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1169":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1170":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1171":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1172":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1173":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1174":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1175":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1176":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1177":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1178":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1179":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1180":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1181":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1182":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1183":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1184":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1185":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1186":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1187":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1188":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1189":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1190":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1191":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1192":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1193":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1194":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1195":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1196":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1197":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1198":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1199":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1200":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"1201":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1202":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"1203":[[0.0,0.0,0.375,1.0,1.5,0.625]],"1204":[[0.375,0.0,0.375,1.0,1.5,0.625]],"1205":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"1206":[[0.375,0.0,0.0,0.625,1.5,1.0]],"1207":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"1208":[[0.375,0.0,0.0,0.625,1.5,0.625]],"1209":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"1210":[[0.375,0.0,0.375,0.625,1.5,1.0]],"1211":[[0.0,0.0,0.375,0.625,1.5,0.625]],"1212":[[0.375,0.0,0.375,0.625,1.5,0.625]],"1213":[[0.375,0.375,0.0,0.625,0.625,1.0]],"1214":[[0.0,0.375,0.375,1.0,0.625,0.625]],"1215":[[0.375,0.0,0.375,0.625,1.0,0.625]],"1216":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1217":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1218":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1219":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1220":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1221":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1222":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1223":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1224":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1225":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1226":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1227":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1228":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1229":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1230":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1231":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1232":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1233":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1234":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1235":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1236":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1237":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1238":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1239":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1240":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1241":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1242":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1243":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1244":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125]],"1245":[[0.1875,0.0,0.1875,0.8125,1.0,0.8125]],"1246":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125],[0.0,0.1875,0.1875,0.1875,0.8125,0.8125]],"1247":[[0.1875,0.0,0.1875,0.8125,0.8125,0.8125]],"1248":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1249":[[0.1875,0.1875,0.0,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1250":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1251":[[0.1875,0.1875,0.0,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1252":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1253":[[0.1875,0.1875,0.0,0.8125,0.8125,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1254":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1255":[[0.1875,0.1875,0.0,0.8125,0.8125,0.8125],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1256":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1257":[[0.1875,0.1875,0.1875,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1258":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1259":[[0.1875,0.1875,0.1875,0.8125,0.8125,1.0],[0.8125,0.1875,0.1875,1.0,0.8125,0.8125]],"1260":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1261":[[0.1875,0.1875,0.1875,1.0,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1262":[[0.0,0.1875,0.1875,1.0,0.8125,0.8125]],"1263":[[0.1875,0.1875,0.1875,1.0,0.8125,0.8125]],"1264":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1265":[[0.1875,0.1875,0.0,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1266":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1267":[[0.1875,0.1875,0.0,0.8125,0.8125,1.0]],"1268":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1269":[[0.1875,0.1875,0.0,0.8125,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1270":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.0,0.8125,0.8125,0.1875]],"1271":[[0.1875,0.1875,0.0,0.8125,0.8125,0.8125]],"1272":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1273":[[0.1875,0.1875,0.1875,0.8125,0.8125,1.0],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1274":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.1875,0.8125,0.8125,0.8125,1.0]],"1275":[[0.1875,0.1875,0.1875,0.8125,0.8125,1.0]],"1276":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125],[0.1875,0.8125,0.1875,0.8125,1.0,0.8125]],"1277":[[0.1875,0.1875,0.1875,0.8125,1.0,0.8125]],"1278":[[0.0,0.1875,0.1875,0.8125,0.8125,0.8125]],"1279":[[0.1875,0.1875,0.1875,0.8125,0.8125,0.8125]],"1280":[[0.3125,-0.0625,0.3125,0.6875,0.1875,0.6875]],"1281":[[0.1875,-0.0625,0.1875,0.8125,0.3125,0.8125]],"1282":[[0.0,0.0,0.0,1.0,0.9375,1.0]],"1283":[[0.1875,0.0,0.1875,0.75,0.4375,0.75]],"1284":[[0.0625,0.0,0.0625,0.9375,0.4375,0.9375]],"1285":[[0.0625,0.0,0.125,0.9375,1.0,0.875]],"1286":[[0.1875,0.0,0.1875,0.8125,0.625,0.8125]],"1287":[[0.375,0.0,0.375,0.625,0.375,0.625]],"1288":[[0.1875,0.0,0.1875,0.8125,0.375,0.8125]],"1289":[[0.125,0.0,0.125,0.875,0.375,0.875]],"1290":[[0.125,0.0,0.125,0.875,0.4375,0.875]],"1291":[[0.3125,0.3125,0.3125,0.6875,0.6875,0.6875]],"1292":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1293":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1294":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1295":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1296":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1297":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1298":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1299":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1300":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1301":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1302":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1303":[[0.15625,0.0,0.15625,0.34375,1.0,0.34375]],"1304":[[0.25,0.0,0.25,0.75,1.5,0.75]],"1305":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1306":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1307":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1308":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1309":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1310":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1311":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1312":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1313":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1314":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1315":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1316":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1317":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1318":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1319":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1320":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1321":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1322":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1323":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1324":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1325":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1326":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1327":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1328":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1329":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1330":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1331":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1332":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1333":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1334":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1335":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1336":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1337":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1338":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1339":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1340":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1341":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1342":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1343":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1344":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1345":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1346":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1347":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1348":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1349":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1350":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1351":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1352":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1353":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1354":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1355":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1356":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1357":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1358":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1359":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1360":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1361":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1362":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1363":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1364":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1365":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1366":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1367":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1368":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1369":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1370":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1371":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1372":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1373":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1374":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1375":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1376":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1377":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1378":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1379":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1380":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1381":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1382":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1383":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1384":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1385":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1386":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1387":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1388":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1389":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1390":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1391":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1392":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1393":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1394":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1395":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1396":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1397":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1398":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1399":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1400":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1401":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1402":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1403":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1404":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1405":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1406":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1407":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1408":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1409":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1410":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1411":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1412":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1413":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1414":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1415":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1416":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1417":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1418":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1419":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1420":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1421":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1422":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1423":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1424":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1425":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1426":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1427":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1428":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1429":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1430":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1431":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1432":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1433":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1434":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1435":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1436":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1437":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1438":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1439":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1440":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1441":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1442":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1443":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1444":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1445":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1446":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1447":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1448":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1449":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1450":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1451":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1452":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1453":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1454":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1455":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1456":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1457":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1458":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1459":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1460":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1461":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1462":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1463":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1464":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1465":[[0.25,0.0,0.25,0.75,1.5,0.75]],"1466":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1467":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1468":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1469":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1470":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1471":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1472":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1473":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1474":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1475":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1476":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1477":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1478":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1479":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1480":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1481":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1482":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1483":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1484":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1485":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1486":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1487":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1488":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1489":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1490":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1491":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1492":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1493":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1494":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1495":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1496":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1497":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1498":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1499":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1500":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1501":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1502":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1503":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1504":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1505":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1506":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1507":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1508":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1509":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1510":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1511":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1512":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1513":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1514":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1515":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1516":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1517":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1518":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1519":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1520":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1521":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1522":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1523":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1524":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1525":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1526":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1527":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1528":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1529":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1530":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1531":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1532":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1533":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1534":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1535":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1536":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1537":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1538":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1539":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1540":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1541":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1542":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1543":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1544":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1545":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1546":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1547":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1548":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1549":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1550":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1551":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1552":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1553":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1554":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1555":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1556":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1557":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1558":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1559":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1560":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1561":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1562":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1563":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1564":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1565":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1566":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1567":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1568":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1569":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1570":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1571":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1572":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1573":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1574":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1575":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1576":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1577":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1578":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1579":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1580":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1581":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1582":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1583":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1584":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1585":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1586":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1587":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1588":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1589":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1590":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1591":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1592":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1593":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1594":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1595":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1596":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1597":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1598":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1599":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1600":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1601":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1602":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1603":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1604":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1605":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1606":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1607":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1608":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1609":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1610":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1611":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1612":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1613":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1614":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1615":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1616":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1617":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1618":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1619":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1620":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1621":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1622":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1623":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1624":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1625":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1626":[[0.25,0.0,0.25,0.75,1.5,0.75]],"1627":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1628":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1629":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1630":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1631":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1632":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1633":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1634":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1635":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1636":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1637":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1638":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1639":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1640":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1641":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1642":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1643":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1644":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1645":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1646":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1647":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1648":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1649":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1650":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1651":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1652":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1653":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1654":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1655":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1656":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1657":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1658":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1659":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1660":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1661":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1662":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1663":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1664":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1665":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1666":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1667":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1668":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1669":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1670":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1671":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1672":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1673":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1674":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1675":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1676":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1677":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1678":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1679":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1680":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1681":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1682":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1683":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1684":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1685":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1686":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1687":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1688":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1689":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1690":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1691":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1692":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1693":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1694":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1695":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1696":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1697":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1698":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1699":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1700":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1701":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1702":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1703":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1704":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1705":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1706":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1707":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1708":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1709":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1710":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1711":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1712":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1713":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1714":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1715":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1716":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1717":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1718":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1719":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1720":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1721":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1722":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1723":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1724":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1725":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1726":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1727":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1728":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1729":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1730":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1731":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1732":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1733":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1734":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1735":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1736":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1737":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1738":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1739":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1740":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1741":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1742":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1743":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1744":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1745":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1746":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1747":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1748":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1749":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1750":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1751":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1752":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1753":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1754":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1755":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1756":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1757":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1758":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1759":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1760":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1761":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1762":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1763":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1764":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1765":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1766":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1767":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1768":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1769":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1770":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1771":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1772":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1773":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1774":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1775":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1776":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1777":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1778":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1779":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1780":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1781":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1782":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1783":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1784":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1785":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1786":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1787":[[0.25,0.0,0.25,0.75,1.5,0.75]],"1788":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1789":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1790":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1791":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1792":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1793":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1794":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1795":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1796":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1797":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1798":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1799":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1800":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1801":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1802":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1803":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1804":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1805":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1806":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1807":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1808":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1809":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1810":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1811":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1812":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1813":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1814":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1815":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1816":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1817":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1818":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1819":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1820":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1821":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1822":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1823":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1824":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1825":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1826":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1827":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1828":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1829":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1830":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1831":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1832":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1833":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1834":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1835":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1836":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1837":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1838":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1839":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1840":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1841":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1842":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1843":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1844":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1845":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1846":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1847":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1848":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1849":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1850":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1851":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1852":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1853":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1854":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1855":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1856":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1857":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1858":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1859":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1860":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1861":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1862":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1863":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1864":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1865":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1866":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1867":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1868":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1869":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1870":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1871":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1872":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1873":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1874":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1875":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1876":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1877":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1878":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1879":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1880":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1881":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1882":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1883":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1884":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1885":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1886":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1887":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1888":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1889":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1890":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1891":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1892":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1893":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1894":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1895":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1896":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1897":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"1898":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1899":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"1900":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1901":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1902":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1903":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1904":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1905":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1906":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1907":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1908":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1909":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1910":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1911":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1912":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1913":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1914":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1915":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1916":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1917":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1918":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1919":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1920":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1921":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1922":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1923":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1924":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1925":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1926":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1927":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1928":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1929":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1930":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1931":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1932":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1933":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1934":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1935":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1936":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1937":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1938":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1939":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1940":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1941":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1942":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"1943":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1944":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1945":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"1946":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1947":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1948":[[0.25,0.0,0.25,0.75,1.5,0.75]],"1949":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1950":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"1951":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1952":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"1953":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1954":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1955":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1956":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1957":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1958":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1959":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1960":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1961":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1962":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"1963":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1964":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1965":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1966":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1967":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1968":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1969":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1970":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1971":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1972":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1973":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1974":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1975":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1976":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1977":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1978":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1979":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1980":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1981":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1982":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1983":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1984":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1985":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"1986":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"1987":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1988":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"1989":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1990":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1991":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1992":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1993":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1994":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"1995":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1996":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1997":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"1998":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"1999":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2000":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2001":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2002":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2003":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2004":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2005":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2006":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2007":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2008":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2009":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2010":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2011":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2012":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2013":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2014":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2015":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2016":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2017":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2018":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2019":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2020":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2021":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2022":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2023":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2024":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2025":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2026":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2027":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2028":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2029":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2030":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2031":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2032":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2033":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2034":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2035":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2036":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2037":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2038":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2039":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2040":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2041":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2042":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2043":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2044":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2045":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2046":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2047":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2048":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2049":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2050":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2051":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2052":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2053":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2054":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2055":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2056":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2057":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2058":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2059":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2060":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2061":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2062":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2063":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2064":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2065":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2066":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2067":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2068":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2069":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2070":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2071":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2072":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2073":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2074":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2075":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2076":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2077":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2078":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2079":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2080":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2081":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2082":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2083":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2084":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2085":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2086":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2087":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2088":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2089":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2090":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2091":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2092":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2093":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2094":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2095":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2096":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2097":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2098":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2099":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2100":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2101":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2102":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2103":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2104":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2105":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2106":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2107":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2108":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2109":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2110":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2111":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2112":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2113":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2114":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2115":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2116":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2117":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2118":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2119":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2120":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2121":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2122":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2123":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2124":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2125":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2126":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2127":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2128":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2129":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2130":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2131":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2132":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2133":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2134":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2135":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2136":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2137":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2138":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2139":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2140":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2141":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2142":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2143":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2144":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2145":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2146":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2147":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2148":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2149":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2150":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2151":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2152":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2153":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2154":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2155":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2156":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2157":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2158":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2159":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2160":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2161":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2162":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2163":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2164":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2165":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2166":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2167":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2168":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2169":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2170":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2171":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2172":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2173":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2174":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2175":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2176":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2177":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2178":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2179":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2180":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2181":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2182":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2183":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2184":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2185":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2186":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2187":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2188":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2189":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2190":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2191":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2192":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2193":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2194":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2195":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2196":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2197":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2198":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2199":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2200":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2201":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2202":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2203":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2204":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2205":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2206":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2207":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2208":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2209":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2210":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2211":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2212":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2213":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2214":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2215":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2216":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2217":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2218":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2219":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2220":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2221":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2222":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2223":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2224":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2225":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2226":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2227":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2228":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2229":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2230":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2231":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2232":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2233":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2234":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2235":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2236":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2237":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2238":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2239":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2240":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2241":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2242":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2243":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2244":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2245":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2246":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2247":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2248":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2249":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2250":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2251":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2252":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2253":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2254":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2255":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2256":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2257":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2258":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2259":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2260":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2261":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2262":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2263":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2264":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2265":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2266":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2267":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2268":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2269":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2270":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2271":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2272":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2273":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2274":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2275":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2276":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2277":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2278":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2279":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2280":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2281":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2282":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2283":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2284":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2285":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2286":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2287":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2288":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2289":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2290":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2291":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2292":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2293":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2294":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2295":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2296":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2297":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2298":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2299":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2300":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2301":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2302":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2303":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2304":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2305":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2306":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2307":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2308":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2309":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2310":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2311":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2312":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2313":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2314":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2315":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2316":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2317":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2318":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2319":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2320":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2321":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2322":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2323":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2324":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2325":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2326":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2327":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2328":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2329":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2330":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2331":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2332":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2333":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2334":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2335":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2336":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2337":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2338":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2339":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2340":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2341":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2342":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2343":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2344":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2345":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2346":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2347":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2348":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2349":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2350":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2351":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2352":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2353":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2354":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2355":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2356":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2357":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2358":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2359":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2360":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2361":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2362":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2363":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2364":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2365":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2366":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2367":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2368":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2369":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2370":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2371":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2372":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2373":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2374":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2375":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2376":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2377":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2378":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2379":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2380":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2381":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2382":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2383":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2384":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2385":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2386":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2387":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2388":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2389":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2390":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2391":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2392":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2393":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2394":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2395":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2396":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2397":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2398":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2399":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2400":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2401":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2402":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2403":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2404":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2405":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2406":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2407":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2408":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2409":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2410":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2411":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2412":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2413":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2414":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2415":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2416":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2417":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2418":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2419":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2420":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2421":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2422":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2423":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2424":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2425":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2426":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2427":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2428":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2429":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2430":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2431":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2432":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2433":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2434":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2435":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2436":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2437":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2438":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2439":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2440":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2441":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2442":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2443":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2444":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2445":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2446":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2447":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2448":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2449":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2450":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2451":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2452":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2453":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2454":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2455":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2456":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2457":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2458":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2459":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2460":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2461":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2462":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2463":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2464":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2465":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2466":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2467":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2468":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2469":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2470":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2471":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2472":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2473":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2474":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2475":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2476":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2477":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2478":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2479":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2480":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2481":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2482":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2483":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2484":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2485":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2486":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2487":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2488":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2489":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2490":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2491":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2492":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2493":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2494":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2495":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2496":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2497":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2498":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2499":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2500":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2501":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2502":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2503":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2504":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2505":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2506":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2507":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2508":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2509":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2510":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2511":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2512":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2513":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2514":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2515":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2516":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2517":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2518":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2519":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2520":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2521":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2522":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2523":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2524":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2525":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2526":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2527":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2528":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2529":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2530":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2531":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2532":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2533":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2534":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2535":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2536":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2537":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2538":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2539":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2540":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2541":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2542":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2543":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2544":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2545":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2546":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2547":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2548":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2549":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2550":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2551":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2552":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2553":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2554":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2555":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2556":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2557":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2558":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2559":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2560":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2561":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2562":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2563":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2564":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2565":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2566":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2567":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2568":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2569":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2570":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2571":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2572":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2573":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2574":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2575":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2576":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2577":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2578":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2579":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2580":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2581":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2582":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2583":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2584":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2585":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2586":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2587":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2588":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2589":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2590":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2591":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2592":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2593":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2594":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2595":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2596":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2597":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2598":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2599":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2600":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2601":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2602":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2603":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2604":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2605":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2606":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2607":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2608":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2609":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2610":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2611":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2612":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2613":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2614":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2615":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2616":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2617":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2618":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2619":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2620":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2621":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2622":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2623":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2624":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2625":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2626":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2627":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2628":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2629":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2630":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2631":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2632":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2633":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2634":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2635":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2636":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2637":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2638":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2639":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2640":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2641":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2642":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2643":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2644":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2645":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2646":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2647":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2648":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2649":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2650":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2651":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2652":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2653":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2654":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2655":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2656":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2657":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2658":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2659":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2660":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2661":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2662":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2663":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2664":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2665":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2666":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2667":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2668":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2669":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2670":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2671":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2672":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2673":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2674":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2675":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2676":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2677":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2678":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2679":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2680":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2681":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2682":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2683":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2684":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2685":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2686":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2687":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2688":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2689":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2690":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2691":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2692":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2693":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2694":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2695":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2696":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2697":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2698":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2699":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2700":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2701":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2702":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2703":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2704":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2705":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2706":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2707":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2708":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2709":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2710":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2711":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2712":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2713":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2714":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2715":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2716":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2717":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2718":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2719":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2720":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2721":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2722":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2723":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2724":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2725":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2726":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2727":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2728":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2729":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2730":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2731":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2732":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2733":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2734":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2735":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2736":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2737":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2738":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2739":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2740":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2741":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2742":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2743":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2744":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2745":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2746":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2747":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2748":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2749":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2750":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2751":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2752":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2753":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2754":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2755":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2756":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2757":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2758":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2759":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2760":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2761":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2762":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2763":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2764":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2765":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2766":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2767":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2768":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2769":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2770":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2771":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2772":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2773":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2774":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2775":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2776":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2777":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2778":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2779":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2780":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2781":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2782":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2783":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2784":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2785":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2786":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2787":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2788":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2789":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2790":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2791":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2792":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2793":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2794":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2795":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2796":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2797":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2798":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2799":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2800":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2801":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2802":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2803":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2804":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2805":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2806":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2807":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2808":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2809":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2810":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2811":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2812":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2813":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2814":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2815":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2816":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2817":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2818":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2819":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2820":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2821":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2822":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2823":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2824":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2825":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2826":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2827":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2828":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2829":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2830":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2831":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2832":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2833":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2834":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2835":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2836":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2837":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2838":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2839":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2840":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2841":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2842":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2843":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2844":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2845":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2846":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2847":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2848":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2849":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2850":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2851":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2852":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2853":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2854":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2855":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2856":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2857":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2858":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2859":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2860":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2861":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2862":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2863":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2864":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2865":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2866":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2867":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2868":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2869":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2870":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2871":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2872":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2873":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2874":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2875":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2876":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2877":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2878":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2879":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2880":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2881":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2882":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2883":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2884":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2885":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2886":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2887":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2888":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2889":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2890":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2891":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2892":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2893":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2894":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2895":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2896":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2897":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2898":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2899":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2900":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2901":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2902":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2903":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2904":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2905":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2906":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2907":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2908":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2909":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2910":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2911":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2912":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2913":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2914":[[0.25,0.0,0.25,0.75,1.5,0.75]],"2915":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2916":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2917":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2918":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"2919":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2920":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2921":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2922":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2923":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2924":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2925":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2926":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2927":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2928":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"2929":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2930":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2931":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2932":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2933":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2934":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2935":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2936":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2937":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2938":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2939":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2940":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2941":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2942":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2943":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2944":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2945":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2946":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2947":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2948":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2949":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2950":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2951":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2952":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"2953":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2954":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2955":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2956":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2957":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2958":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2959":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2960":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2961":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2962":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2963":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2964":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"2965":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2966":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2967":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2968":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2969":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"2970":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"2971":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2972":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"2973":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2974":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2975":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2976":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2977":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2978":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2979":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2980":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2981":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2982":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2983":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2984":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2985":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2986":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2987":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"2988":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2989":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2990":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"2991":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2992":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2993":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2994":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"2995":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2996":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"2997":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"2998":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"2999":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3000":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3001":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3002":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3003":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3004":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3005":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3006":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3007":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3008":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3009":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3010":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3011":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3012":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3013":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3014":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3015":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3016":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3017":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3018":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3019":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3020":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3021":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3022":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3023":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3024":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3025":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3026":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3027":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3028":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3029":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3030":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3031":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3032":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3033":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3034":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3035":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3036":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3037":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3038":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3039":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3040":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3041":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3042":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3043":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3044":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3045":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3046":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3047":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3048":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3049":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3050":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3051":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3052":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3053":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3054":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3055":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3056":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3057":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3058":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3059":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3060":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3061":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3062":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3063":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3064":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3065":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3066":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3067":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3068":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3069":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3070":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3071":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3072":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3073":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3074":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3075":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3076":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3077":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3078":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3079":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3080":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3081":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3082":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3083":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3084":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3085":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3086":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3087":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3088":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3089":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3090":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3091":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3092":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3093":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3094":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3095":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3096":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3097":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3098":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3099":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3100":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3101":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3102":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3103":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3104":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3105":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3106":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3107":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3108":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3109":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3110":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3111":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3112":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3113":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3114":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3115":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3116":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3117":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3118":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3119":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3120":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3121":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3122":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3123":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3124":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3125":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3126":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3127":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3128":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3129":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3130":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3131":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3132":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3133":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3134":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3135":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3136":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3137":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3138":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3139":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3140":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3141":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3142":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3143":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3144":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3145":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3146":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3147":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3148":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3149":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3150":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3151":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3152":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3153":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3154":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3155":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3156":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3157":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3158":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3159":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3160":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3161":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3162":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3163":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3164":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3165":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3166":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3167":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3168":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3169":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3170":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3171":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3172":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3173":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3174":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3175":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3176":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3177":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3178":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3179":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3180":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3181":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3182":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3183":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3184":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3185":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3186":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3187":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3188":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3189":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3190":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3191":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3192":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3193":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3194":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3195":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3196":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3197":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3198":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3199":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3200":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3201":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3202":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3203":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3204":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3205":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3206":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3207":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3208":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3209":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3210":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3211":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3212":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3213":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3214":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3215":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3216":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3217":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3218":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3219":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3220":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3221":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3222":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3223":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3224":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3225":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3226":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3227":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3228":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3229":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3230":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3231":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3232":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3233":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3234":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3235":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3236":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3237":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3238":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3239":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3240":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3241":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3242":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3243":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3244":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3245":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3246":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3247":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3248":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3249":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3250":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3251":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3252":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3253":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3254":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3255":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3256":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3257":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3258":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3259":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3260":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3261":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3262":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3263":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3264":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3265":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3266":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3267":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3268":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3269":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3270":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3271":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3272":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3273":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3274":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3275":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3276":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3277":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3278":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3279":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3280":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3281":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3282":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3283":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3284":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3285":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3286":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3287":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3288":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3289":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3290":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3291":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3292":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3293":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3294":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3295":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3296":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3297":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3298":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3299":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3300":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3301":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3302":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3303":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3304":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3305":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3306":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3307":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3308":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3309":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3310":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3311":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3312":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3313":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3314":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3315":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3316":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3317":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3318":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3319":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3320":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3321":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3322":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3323":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3324":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3325":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3326":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3327":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3328":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3329":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3330":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3331":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3332":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3333":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3334":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3335":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3336":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3337":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3338":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3339":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3340":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3341":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3342":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3343":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3344":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3345":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3346":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3347":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3348":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3349":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3350":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3351":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3352":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3353":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3354":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3355":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3356":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3357":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3358":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3359":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3360":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3361":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3362":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3363":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3364":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3365":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3366":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3367":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3368":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3369":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3370":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3371":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3372":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3373":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3374":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3375":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3376":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3377":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3378":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3379":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3380":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3381":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3382":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3383":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3384":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3385":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3386":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3387":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3388":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3389":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3390":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3391":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3392":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3393":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3394":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3395":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3396":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3397":[[0.0,0.0,0.0,0.125,1.0,0.125],[0.0,0.0,0.875,0.125,1.0,1.0],[0.875,0.0,0.0,1.0,1.0,0.125],[0.875,0.0,0.875,1.0,1.0,1.0],[0.0,0.875,0.125,1.0,1.0,0.875],[0.125,0.875,0.0,0.875,1.0,0.125],[0.125,0.875,0.875,0.875,1.0,1.0]],"3398":[[0.125,0.0,0.375,0.25,0.8125,0.625],[0.75,0.0,0.375,0.875,0.8125,0.625],[0.25,0.25,0.125,0.75,1.0,0.875],[0.125,0.4375,0.3125,0.25,0.8125,0.375],[0.125,0.4375,0.625,0.25,0.8125,0.6875],[0.75,0.4375,0.3125,0.875,0.8125,0.375],[0.75,0.4375,0.625,0.875,0.8125,0.6875]],"3399":[[0.125,0.0,0.375,0.25,0.8125,0.625],[0.75,0.0,0.375,0.875,0.8125,0.625],[0.25,0.25,0.125,0.75,1.0,0.875],[0.125,0.4375,0.3125,0.25,0.8125,0.375],[0.125,0.4375,0.625,0.25,0.8125,0.6875],[0.75,0.4375,0.3125,0.875,0.8125,0.375],[0.75,0.4375,0.625,0.875,0.8125,0.6875]],"3400":[[0.375,0.0,0.125,0.625,0.8125,0.25],[0.375,0.0,0.75,0.625,0.8125,0.875],[0.125,0.25,0.25,0.875,1.0,0.75],[0.3125,0.4375,0.125,0.375,0.8125,0.25],[0.3125,0.4375,0.75,0.375,0.8125,0.875],[0.625,0.4375,0.125,0.6875,0.8125,0.25],[0.625,0.4375,0.75,0.6875,0.8125,0.875]],"3401":[[0.375,0.0,0.125,0.625,0.8125,0.25],[0.375,0.0,0.75,0.625,0.8125,0.875],[0.125,0.25,0.25,0.875,1.0,0.75],[0.3125,0.4375,0.125,0.375,0.8125,0.25],[0.3125,0.4375,0.75,0.375,0.8125,0.875],[0.625,0.4375,0.125,0.6875,0.8125,0.25],[0.625,0.4375,0.75,0.6875,0.8125,0.875]],"3402":[[0.25,0.125,0.0,0.75,0.875,0.75],[0.125,0.3125,0.1875,0.25,0.6875,0.5625],[0.75,0.3125,0.1875,0.875,0.6875,0.5625],[0.125,0.375,0.5625,0.25,0.625,1.0],[0.75,0.375,0.5625,0.875,0.625,1.0]],"3403":[[0.25,0.125,0.25,0.75,0.875,1.0],[0.125,0.3125,0.4375,0.25,0.6875,0.8125],[0.75,0.3125,0.4375,0.875,0.6875,0.8125],[0.125,0.375,0.0,0.25,0.625,0.4375],[0.75,0.375,0.0,0.875,0.625,0.4375]],"3404":[[0.0,0.125,0.25,0.75,0.875,0.75],[0.1875,0.3125,0.125,0.5625,0.6875,0.25],[0.1875,0.3125,0.75,0.5625,0.6875,0.875],[0.5625,0.375,0.125,1.0,0.625,0.25],[0.5625,0.375,0.75,1.0,0.625,0.875]],"3405":[[0.25,0.125,0.25,1.0,0.875,0.75],[0.4375,0.3125,0.125,0.8125,0.6875,0.25],[0.4375,0.3125,0.75,0.8125,0.6875,0.875],[0.0,0.375,0.125,0.4375,0.625,0.25],[0.0,0.375,0.75,0.4375,0.625,0.875]],"3406":[[0.25,0.0,0.125,0.75,0.75,0.875],[0.125,0.1875,0.3125,0.25,0.5625,0.6875],[0.75,0.1875,0.3125,0.875,0.5625,0.6875],[0.125,0.5625,0.375,0.25,1.0,0.625],[0.75,0.5625,0.375,0.875,1.0,0.625]],"3407":[[0.25,0.0,0.125,0.75,0.75,0.875],[0.125,0.1875,0.3125,0.25,0.5625,0.6875],[0.75,0.1875,0.3125,0.875,0.5625,0.6875],[0.125,0.5625,0.375,0.25,1.0,0.625],[0.75,0.5625,0.375,0.875,1.0,0.625]],"3408":[[0.125,0.0,0.25,0.875,0.75,0.75],[0.3125,0.1875,0.125,0.6875,0.5625,0.25],[0.3125,0.1875,0.75,0.6875,0.5625,0.875],[0.375,0.5625,0.125,0.625,1.0,0.25],[0.375,0.5625,0.75,0.625,1.0,0.875]],"3409":[[0.125,0.0,0.25,0.875,0.75,0.75],[0.3125,0.1875,0.125,0.6875,0.5625,0.25],[0.3125,0.1875,0.75,0.6875,0.5625,0.875],[0.375,0.5625,0.125,0.625,1.0,0.25],[0.375,0.5625,0.75,0.625,1.0,0.875]],"3410":[[0.0,0.0,0.0,1.0,0.125,1.0],[0.25,0.125,0.25,0.75,0.875,0.75]],"3411":[[0.0,0.0,0.0,1.0,0.5625,1.0]],"3412":[[0.0,0.0,0.25,1.0,1.0,0.75]],"3413":[[0.25,0.0,0.0,0.75,1.0,1.0]],"3414":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.4375,0.8125,0.4375,0.5625,1.0,0.5625]],"3415":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.4375,0.8125,0.0,0.5625,0.9375,0.8125]],"3416":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.4375,0.8125,0.1875,0.5625,0.9375,1.0]],"3417":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.0,0.8125,0.4375,0.8125,0.9375,0.5625]],"3418":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.1875,0.8125,0.4375,1.0,0.9375,0.5625]],"3419":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.4375,0.8125,0.0,0.5625,0.9375,1.0]],"3420":[[0.25,0.25,0.25,0.75,0.375,0.75],[0.3125,0.375,0.3125,0.6875,0.8125,0.6875],[0.0,0.8125,0.4375,1.0,0.9375,0.5625]],"3421":[[0.3125,0.0625,0.3125,0.6875,0.5,0.6875],[0.375,0.5,0.375,0.625,0.625,0.625]],"3422":[[0.3125,0.0,0.3125,0.6875,0.4375,0.6875],[0.375,0.4375,0.375,0.625,0.5625,0.625]],"3423":[[0.0,0.0,0.0,1.0,0.4375,1.0]],"3424":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"3425":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"3426":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"3427":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"3428":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"3429":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"3430":[[0.0,0.0,0.375,1.0,1.5,0.625]],"3431":[[0.375,0.0,0.375,1.0,1.5,0.625]],"3432":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"3433":[[0.375,0.0,0.0,0.625,1.5,1.0]],"3434":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"3435":[[0.375,0.0,0.0,0.625,1.5,0.625]],"3436":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"3437":[[0.375,0.0,0.375,0.625,1.5,1.0]],"3438":[[0.0,0.0,0.375,0.625,1.5,0.625]],"3439":[[0.375,0.0,0.375,0.625,1.5,0.625]],"3440":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"3441":[[0.375,0.0,0.0,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"3442":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"3443":[[0.375,0.0,0.0,0.625,1.5,0.625],[0.625,0.0,0.375,1.0,1.5,0.625]],"3444":[[0.0,0.0,0.375,1.0,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"3445":[[0.375,0.0,0.375,0.625,1.5,1.0],[0.625,0.0,0.375,1.0,1.5,0.625]],"3446":[[0.0,0.0,0.375,1.0,1.5,0.625]],"3447":[[0.375,0.0,0.375,1.0,1.5,0.625]],"3448":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375],[0.375,0.0,0.625,0.625,1.5,1.0]],"3449":[[0.375,0.0,0.0,0.625,1.5,1.0]],"3450":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.0,0.625,1.5,0.375]],"3451":[[0.375,0.0,0.0,0.625,1.5,0.625]],"3452":[[0.0,0.0,0.375,0.625,1.5,0.625],[0.375,0.0,0.625,0.625,1.5,1.0]],"3453":[[0.375,0.0,0.375,0.625,1.5,1.0]],"3454":[[0.0,0.0,0.375,0.625,1.5,0.625]],"3455":[[0.375,0.0,0.375,0.625,1.5,0.625]],"3456":[[0.0,0.0,0.0,1.0,0.125,1.0],[0.0,0.125,0.0,0.125,1.0,1.0],[0.125,0.125,0.0,1.0,1.0,0.125],[0.125,0.125,0.875,1.0,1.0,1.0],[0.875,0.125,0.125,1.0,1.0,0.875]],"3457":[[0.0625,0.0,0.0625,0.9375,0.9375,0.9375]],"3458":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3459":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3460":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3461":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3462":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3463":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3464":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3465":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3466":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3467":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3468":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3469":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3470":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3471":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3472":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3473":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3474":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3475":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3476":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3477":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3478":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3479":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3480":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3481":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3482":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3483":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3484":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3485":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3486":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3487":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3488":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3489":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3490":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3491":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3492":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3493":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3494":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3495":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3496":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3497":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3498":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3499":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3500":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3501":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3502":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3503":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3504":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3505":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3506":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3507":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3508":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3509":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3510":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3511":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3512":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3513":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3514":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3515":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3516":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3517":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3518":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3519":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3520":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3521":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3522":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3523":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3524":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3525":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3526":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3527":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3528":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3529":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3530":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3531":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3532":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3533":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3534":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3535":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3536":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3537":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3538":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3539":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3540":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3541":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3542":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3543":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3544":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3545":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3546":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3547":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3548":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3549":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3550":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3551":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3552":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3553":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3554":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3555":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3556":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3557":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3558":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3559":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3560":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3561":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3562":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3563":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3564":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3565":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3566":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3567":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3568":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3569":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3570":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3571":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3572":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3573":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3574":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3575":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3576":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3577":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3578":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3579":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3580":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3581":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3582":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3583":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3584":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3585":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3586":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3587":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3588":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3589":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3590":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3591":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3592":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3593":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3594":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3595":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3596":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3597":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3598":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3599":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3600":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3601":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3602":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3603":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3604":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3605":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3606":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3607":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3608":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3609":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3610":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3611":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3612":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3613":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3614":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3615":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3616":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3617":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3618":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3619":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3620":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3621":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3622":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3623":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3624":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3625":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3626":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3627":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3628":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3629":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3630":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3631":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3632":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3633":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3634":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3635":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3636":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3637":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3638":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3639":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3640":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3641":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3642":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3643":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3644":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3645":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3646":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3647":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3648":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3649":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3650":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3651":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3652":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3653":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3654":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3655":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3656":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3657":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3658":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3659":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3660":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3661":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3662":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3663":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3664":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3665":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3666":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3667":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3668":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3669":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3670":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3671":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3672":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3673":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3674":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3675":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3676":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3677":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3678":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3679":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3680":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3681":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3682":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3683":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3684":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3685":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3686":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3687":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3688":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3689":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3690":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3691":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3692":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3693":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3694":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3695":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3696":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3697":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3698":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3699":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3700":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3701":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3702":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3703":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3704":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3705":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3706":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3707":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3708":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3709":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3710":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3711":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3712":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3713":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3714":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3715":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3716":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3717":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3718":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3719":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3720":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3721":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3722":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3723":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3724":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3725":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3726":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3727":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3728":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3729":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3730":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3731":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3732":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3733":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3734":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3735":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3736":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3737":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3738":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3739":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3740":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3741":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3742":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3743":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3744":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3745":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3746":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3747":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3748":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3749":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3750":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3751":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3752":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3753":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3754":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3755":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3756":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3757":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3758":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3759":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3760":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3761":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3762":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3763":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3764":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3765":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3766":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3767":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3768":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3769":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3770":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3771":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3772":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3773":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3774":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3775":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3776":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3777":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3778":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3779":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3780":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3781":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3782":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3783":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3784":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3785":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3786":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3787":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3788":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3789":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3790":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3791":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3792":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3793":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3794":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3795":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3796":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3797":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3798":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3799":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3800":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3801":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3802":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3803":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3804":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3805":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3806":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3807":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3808":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3809":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3810":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3811":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3812":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3813":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3814":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3815":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3816":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3817":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3818":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3819":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3820":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3821":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3822":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3823":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3824":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3825":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3826":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3827":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3828":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3829":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3830":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3831":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3832":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3833":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3834":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3835":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3836":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3837":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3838":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3839":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3840":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3841":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3842":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3843":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3844":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3845":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3846":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3847":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3848":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3849":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3850":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3851":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3852":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3853":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3854":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3855":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3856":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3857":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3858":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3859":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3860":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3861":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3862":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3863":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3864":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3865":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3866":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3867":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3868":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3869":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3870":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3871":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3872":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3873":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3874":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3875":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3876":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3877":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3878":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3879":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3880":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3881":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3882":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3883":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3884":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3885":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3886":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3887":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3888":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3889":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3890":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"3891":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3892":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"3893":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3894":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3895":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3896":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3897":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3898":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3899":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3900":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3901":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3902":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3903":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3904":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3905":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3906":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3907":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3908":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3909":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3910":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3911":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3912":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3913":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3914":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3915":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3916":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3917":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3918":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3919":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3920":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3921":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3922":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3923":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3924":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3925":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3926":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3927":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3928":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3929":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3930":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3931":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3932":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3933":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3934":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3935":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"3936":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3937":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3938":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"3939":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3940":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3941":[[0.4375,0.0,0.4375,0.5625,0.375,0.5625]],"3942":[[0.3125,0.0,0.375,0.6875,0.375,0.5625]],"3943":[[0.3125,0.0,0.375,0.625,0.375,0.6875]],"3944":[[0.3125,0.0,0.3125,0.6875,0.375,0.625]],"3945":[[0.0625,0.0,0.0625,0.9375,0.5,0.9375],[0.4375,0.5,0.4375,0.5625,0.875,0.5625]],"3946":[[0.1875,0.1875,0.5625,0.8125,0.8125,1.0]],"3947":[[0.0,0.1875,0.1875,0.4375,0.8125,0.8125]],"3948":[[0.1875,0.1875,0.0,0.8125,0.8125,0.4375]],"3949":[[0.5625,0.1875,0.1875,1.0,0.8125,0.8125]],"3950":[[0.1875,0.0,0.1875,0.8125,0.4375,0.8125]],"3951":[[0.1875,0.5625,0.1875,0.8125,1.0,0.8125]],"3952":[[0.1875,0.1875,0.6875,0.8125,0.8125,1.0]],"3953":[[0.0,0.1875,0.1875,0.3125,0.8125,0.8125]],"3954":[[0.1875,0.1875,0.0,0.8125,0.8125,0.3125]],"3955":[[0.6875,0.1875,0.1875,1.0,0.8125,0.8125]],"3956":[[0.1875,0.0,0.1875,0.8125,0.3125,0.8125]],"3957":[[0.1875,0.6875,0.1875,0.8125,1.0,0.8125]],"3958":[[0.1875,0.1875,0.75,0.8125,0.8125,1.0]],"3959":[[0.0,0.1875,0.1875,0.25,0.8125,0.8125]],"3960":[[0.1875,0.1875,0.0,0.8125,0.8125,0.25]],"3961":[[0.75,0.1875,0.1875,1.0,0.8125,0.8125]],"3962":[[0.1875,0.0,0.1875,0.8125,0.25,0.8125]],"3963":[[0.1875,0.75,0.1875,0.8125,1.0,0.8125]],"3964":[[0.25,0.25,0.8125,0.75,0.75,1.0]],"3965":[[0.0,0.25,0.25,0.1875,0.75,0.75]],"3966":[[0.25,0.25,0.0,0.75,0.75,0.1875]],"3967":[[0.8125,0.25,0.25,1.0,0.75,0.75]],"3968":[[0.25,0.0,0.25,0.75,0.1875,0.75]],"3969":[[0.25,0.8125,0.25,0.75,1.0,0.75]],"3970":[[0.25,0.0,0.25,0.75,1.5,0.75]],"3971":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3972":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"3973":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3974":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"3975":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3976":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3977":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3978":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3979":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3980":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3981":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3982":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3983":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3984":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"3985":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3986":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3987":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3988":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3989":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"3990":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"3991":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3992":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"3993":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3994":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3995":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"3996":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"3997":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3998":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"3999":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4000":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4001":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4002":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4003":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4004":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4005":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4006":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4007":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4008":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4009":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4010":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4011":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4012":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4013":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4014":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4015":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4016":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4017":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4018":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4019":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4020":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4021":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4022":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4023":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4024":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4025":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4026":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4027":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4028":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4029":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4030":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4031":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4032":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4033":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4034":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4035":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4036":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4037":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4038":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4039":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4040":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4041":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4042":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4043":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4044":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4045":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4046":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4047":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4048":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4049":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4050":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4051":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4052":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4053":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4054":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4055":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4056":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4057":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4058":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4059":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4060":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4061":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4062":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4063":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4064":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4065":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4066":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4067":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4068":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4069":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4070":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4071":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4072":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4073":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4074":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4075":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4076":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4077":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4078":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4079":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4080":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4081":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4082":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4083":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4084":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4085":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4086":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4087":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4088":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4089":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4090":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4091":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4092":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4093":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4094":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4095":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4096":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4097":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4098":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4099":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4100":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4101":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4102":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4103":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4104":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4105":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4106":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4107":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4108":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4109":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4110":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4111":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4112":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4113":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4114":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4115":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4116":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4117":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4118":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4119":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4120":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4121":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4122":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4123":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4124":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4125":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4126":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4127":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4128":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4129":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4130":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4131":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4132":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4133":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4134":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4135":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4136":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4137":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4138":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4139":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4140":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4141":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4142":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4143":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4144":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4145":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4146":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4147":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4148":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4149":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4150":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4151":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4152":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4153":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4154":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4155":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4156":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4157":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4158":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4159":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4160":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4161":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4162":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4163":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4164":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4165":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4166":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4167":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4168":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4169":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4170":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4171":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4172":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4173":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4174":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4175":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4176":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4177":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4178":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4179":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4180":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4181":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4182":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4183":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4184":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4185":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4186":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4187":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4188":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4189":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4190":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4191":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4192":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4193":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4194":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4195":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4196":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4197":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4198":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4199":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4200":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4201":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4202":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4203":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4204":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4205":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4206":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4207":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4208":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4209":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4210":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4211":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4212":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4213":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4214":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4215":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4216":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4217":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4218":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4219":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4220":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4221":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4222":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4223":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4224":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4225":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4226":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4227":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4228":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4229":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4230":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4231":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4232":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4233":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4234":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4235":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4236":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4237":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4238":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4239":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4240":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4241":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4242":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4243":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4244":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4245":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4246":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4247":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4248":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4249":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4250":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4251":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4252":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4253":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4254":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4255":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4256":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4257":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4258":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4259":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4260":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4261":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4262":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4263":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4264":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4265":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4266":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4267":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4268":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4269":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4270":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4271":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4272":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4273":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4274":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4275":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4276":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4277":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4278":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4279":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4280":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4281":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4282":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4283":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4284":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4285":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4286":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4287":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4288":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4289":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4290":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4291":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4292":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4293":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4294":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4295":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4296":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4297":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4298":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4299":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4300":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4301":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4302":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4303":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4304":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4305":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4306":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4307":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4308":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4309":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4310":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4311":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4312":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4313":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4314":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4315":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4316":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4317":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4318":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4319":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4320":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4321":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4322":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4323":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4324":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4325":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4326":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4327":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4328":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4329":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4330":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4331":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4332":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4333":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4334":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4335":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4336":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4337":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4338":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4339":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4340":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4341":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4342":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4343":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4344":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4345":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4346":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4347":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4348":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4349":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4350":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4351":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4352":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4353":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4354":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4355":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4356":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4357":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4358":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4359":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4360":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4361":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4362":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4363":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4364":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4365":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4366":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4367":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4368":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4369":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4370":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4371":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4372":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4373":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4374":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4375":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4376":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4377":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4378":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4379":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4380":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4381":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4382":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4383":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4384":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4385":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4386":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4387":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4388":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4389":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4390":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4391":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4392":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4393":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4394":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4395":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4396":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4397":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4398":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4399":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4400":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4401":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4402":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4403":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4404":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4405":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4406":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4407":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4408":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4409":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4410":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4411":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4412":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4413":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4414":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4415":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4416":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4417":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4418":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4419":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4420":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4421":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4422":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4423":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4424":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4425":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4426":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4427":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4428":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4429":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4430":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4431":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4432":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4433":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4434":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4435":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4436":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4437":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4438":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4439":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4440":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4441":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4442":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4443":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4444":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4445":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4446":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4447":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4448":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4449":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4450":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4451":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4452":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4453":[[0.0,0.0,0.0,1.0,0.5,1.0]],"4454":[[0.0,0.0,0.0,1.0,0.5,1.0]],"4455":[[0.1875,0.0,0.1875,0.8125,0.875,0.8125]],"4456":[[0.1875,0.0,0.1875,0.5625,1.0,0.5625]],"4457":[[0.1875,0.0,0.1875,0.5625,1.0,0.5625]],"4458":[[0.1875,0.0,0.1875,0.5625,1.0,0.5625]],"4459":[[0.1875,0.0,0.1875,0.5625,1.0,0.5625]],"4460":[[0.1875,0.0,0.1875,0.5625,0.6875,0.5625]],"4461":[[0.1875,0.0,0.1875,0.5625,0.6875,0.5625]],"4462":[[0.1875,0.3125,0.1875,0.5625,1.0,0.5625]],"4463":[[0.1875,0.3125,0.1875,0.5625,1.0,0.5625]],"4464":[[0.125,0.0,0.125,0.625,1.0,0.625]],"4465":[[0.125,0.0,0.125,0.625,1.0,0.625]],"4466":[[0.125,0.0,0.125,0.625,1.0,0.625]],"4467":[[0.125,0.0,0.125,0.625,1.0,0.625]],"4468":[[0.0625,0.0,0.0625,0.6875,1.0,0.6875]],"4469":[[0.0625,0.0,0.0625,0.6875,1.0,0.6875]],"4470":[[0.0625,0.0,0.0625,0.6875,1.0,0.6875]],"4471":[[0.0625,0.0,0.0625,0.6875,1.0,0.6875]],"4472":[[0.0,0.0,0.0,0.75,1.0,0.75]],"4473":[[0.0,0.0,0.0,0.75,1.0,0.75]],"4474":[[0.0,0.0,0.0,0.75,1.0,0.75]],"4475":[[0.0,0.0,0.0,0.75,1.0,0.75]],"4476":[[0.375,0.0,0.375,0.625,1.0,0.625],[0.0,0.5,0.0,0.375,1.0,1.0],[0.375,0.5,0.0,1.0,1.0,0.375],[0.375,0.5,0.625,1.0,1.0,1.0],[0.625,0.5,0.375,1.0,1.0,0.625]],"4477":[[0.0,0.6875,0.0,1.0,0.9375,1.0]],"4478":[[0.0,0.6875,0.0,1.0,0.9375,1.0]],"4479":[[0.0,0.6875,0.0,1.0,0.8125,1.0]],"4480":[[0.0,0.0,0.0,1.0,0.875,1.0]],"4481":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4482":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4483":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4484":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4485":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4486":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4487":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4488":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4489":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4490":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4491":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4492":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4493":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4494":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4495":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4496":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4497":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4498":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4499":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4500":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4501":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4502":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4503":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4504":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4505":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4506":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4507":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4508":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4509":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4510":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4511":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4512":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4513":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4514":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4515":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4516":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4517":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4518":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4519":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4520":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4521":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4522":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4523":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4524":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4525":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4526":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4527":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4528":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4529":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4530":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4531":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4532":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4533":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4534":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4535":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4536":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4537":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4538":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4539":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4540":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4541":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4542":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4543":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4544":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4545":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4546":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4547":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4548":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4549":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4550":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4551":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4552":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4553":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4554":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4555":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4556":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4557":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4558":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4559":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4560":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4561":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4562":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4563":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4564":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4565":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4566":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4567":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4568":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4569":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4570":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4571":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4572":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4573":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4574":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4575":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4576":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4577":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4578":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4579":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4580":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4581":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4582":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4583":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4584":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4585":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4586":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4587":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4588":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4589":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4590":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4591":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4592":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4593":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4594":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4595":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4596":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4597":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4598":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4599":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4600":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4601":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4602":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4603":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4604":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4605":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4606":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4607":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4608":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4609":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4610":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4611":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4612":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4613":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4614":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4615":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4616":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4617":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4618":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4619":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4620":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4621":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4622":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4623":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4624":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4625":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4626":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4627":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4628":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4629":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4630":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4631":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4632":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4633":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4634":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4635":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4636":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4637":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4638":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4639":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4640":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4641":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4642":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4643":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4644":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4645":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4646":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4647":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4648":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4649":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4650":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4651":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4652":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4653":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4654":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4655":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4656":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4657":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4658":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4659":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4660":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4661":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4662":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4663":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4664":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4665":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4666":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4667":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4668":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4669":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4670":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4671":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4672":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4673":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4674":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4675":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4676":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4677":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4678":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4679":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4680":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4681":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4682":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4683":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4684":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4685":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4686":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4687":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4688":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4689":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4690":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4691":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4692":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4693":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4694":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4695":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4696":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4697":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4698":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4699":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4700":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4701":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4702":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4703":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4704":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4705":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4706":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4707":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4708":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4709":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4710":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4711":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4712":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4713":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4714":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4715":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4716":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4717":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4718":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4719":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4720":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4721":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4722":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4723":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4724":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4725":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4726":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4727":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4728":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4729":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4730":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4731":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4732":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4733":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4734":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4735":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4736":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4737":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4738":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4739":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4740":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4741":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4742":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4743":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4744":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4745":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4746":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4747":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4748":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4749":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4750":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4751":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4752":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4753":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4754":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4755":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4756":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4757":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4758":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4759":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4760":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4761":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4762":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4763":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4764":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4765":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4766":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4767":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4768":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4769":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4770":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4771":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4772":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4773":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4774":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4775":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4776":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4777":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4778":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4779":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4780":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4781":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4782":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4783":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4784":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4785":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4786":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4787":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4788":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4789":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4790":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4791":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4792":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4793":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4794":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4795":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4796":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4797":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4798":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4799":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4800":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4801":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4802":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4803":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4804":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4805":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4806":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4807":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4808":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4809":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4810":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4811":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4812":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4813":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4814":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4815":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4816":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4817":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4818":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4819":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4820":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4821":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4822":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4823":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4824":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4825":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4826":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4827":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4828":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4829":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4830":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4831":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4832":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4833":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4834":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4835":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4836":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4837":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4838":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4839":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4840":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4841":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4842":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4843":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4844":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4845":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4846":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4847":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4848":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4849":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4850":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4851":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4852":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4853":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4854":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4855":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4856":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4857":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4858":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4859":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4860":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4861":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4862":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4863":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4864":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4865":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4866":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4867":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4868":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4869":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4870":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4871":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4872":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4873":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4874":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4875":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4876":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4877":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4878":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4879":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4880":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4881":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4882":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4883":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4884":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4885":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4886":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4887":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4888":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4889":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4890":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4891":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4892":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4893":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4894":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4895":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4896":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4897":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4898":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4899":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4900":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4901":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4902":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4903":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4904":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4905":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4906":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4907":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4908":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4909":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4910":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4911":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4912":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4913":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"4914":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4915":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"4916":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4917":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4918":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4919":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4920":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4921":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4922":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4923":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4924":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4925":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4926":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4927":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4928":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4929":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4930":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4931":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4932":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4933":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4934":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4935":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4936":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4937":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4938":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4939":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4940":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4941":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4942":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4943":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4944":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4945":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4946":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4947":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4948":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4949":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4950":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4951":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4952":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4953":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4954":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4955":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4956":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4957":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4958":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"4959":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4960":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4961":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"4962":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4963":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4964":[[0.25,0.0,0.25,0.75,1.5,0.75]],"4965":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4966":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"4967":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4968":[[0.0,0.0,0.3125,0.6875,1.5,0.6875]],"4969":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4970":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4971":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4972":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4973":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4974":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4975":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4976":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4977":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4978":[[0.3125,0.0,0.3125,0.6875,1.5,1.0]],"4979":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4980":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4981":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4982":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4983":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"4984":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"4985":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4986":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"4987":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4988":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4989":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4990":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4991":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4992":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4993":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4994":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4995":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"4996":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"4997":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4998":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"4999":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5000":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5001":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5002":[[0.3125,0.0,0.0,0.6875,1.5,0.6875]],"5003":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5004":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5005":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5006":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5007":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5008":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"5009":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5010":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5011":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5012":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5013":[[0.0,0.0,0.3125,0.75,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5014":[[0.3125,0.0,0.0,0.6875,1.5,1.0]],"5015":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5016":[[0.0,0.0,0.3125,0.6875,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5017":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5018":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"5019":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"5020":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"5021":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"5022":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"5023":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5024":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5025":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5026":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5027":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5028":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5029":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5030":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5031":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5032":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5033":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5034":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5035":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5036":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5037":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5038":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5039":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5040":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5041":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5042":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5043":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5044":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5045":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5046":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5047":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5048":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5049":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5050":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5051":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5052":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5053":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5054":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5055":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5056":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5057":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5058":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5059":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5060":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5061":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5062":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5063":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5064":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5065":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5066":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5067":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5068":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5069":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5070":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5071":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5072":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"5073":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75]],"5074":[[0.3125,0.0,0.3125,1.0,1.5,0.6875]],"5075":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"5076":[[0.0,0.0,0.3125,1.0,1.5,0.6875]],"5077":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5078":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5079":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5080":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5081":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5082":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5083":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5084":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5085":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5086":[[0.3125,0.0,0.3125,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5087":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5088":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5089":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5090":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5091":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5092":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5093":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5094":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5095":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5096":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5097":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5098":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5099":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5100":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5101":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5102":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5103":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5104":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5105":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5106":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5107":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5108":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5109":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25]],"5110":[[0.3125,0.0,0.0,0.6875,1.5,0.6875],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5111":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5112":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125]],"5113":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5114":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5115":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5116":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5117":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5118":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5119":[[0.25,0.0,0.25,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0],[0.75,0.0,0.3125,1.0,1.5,0.6875]],"5120":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5121":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.25,0.0,0.25,0.75,1.5,0.3125],[0.25,0.0,0.6875,0.75,1.5,0.75],[0.3125,0.0,0.0,0.6875,1.5,0.25],[0.3125,0.0,0.75,0.6875,1.5,1.0]],"5122":[[0.3125,0.0,0.0,0.6875,1.5,1.0],[0.6875,0.0,0.3125,1.0,1.5,0.6875]],"5123":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5124":[[0.0,0.0,0.3125,1.0,1.5,0.6875],[0.3125,0.0,0.0,0.6875,1.5,0.3125],[0.3125,0.0,0.6875,0.6875,1.5,1.0]],"5125":[[0.0625,0.0,0.0625,0.9375,1.0,0.9375]],"5126":[[0.25,0.0,0.25,0.75,0.5,0.75]],"5127":[[0.0,0.0,0.0,1.0,0.0625,1.0]]},"blocks":{"air":0,"stone":1,"granite":1,"polished_granite":1,"diorite":1,"polished_diorite":1,"andesite":1,"polished_andesite":1,"grass_block":1,"dirt":1,"coarse_dirt":1,"podzol":1,"cobblestone":1,"oak_planks":1,"spruce_planks":1,"birch_planks":1,"jungle_planks":1,"acacia_planks":1,"cherry_planks":1,"dark_oak_planks":1,"pale_oak_wood":1,"pale_oak_planks":1,"mangrove_planks":1,"bamboo_planks":1,"bamboo_mosaic":1,"oak_sapling":0,"spruce_sapling":0,"birch_sapling":0,"jungle_sapling":0,"acacia_sapling":0,"cherry_sapling":0,"dark_oak_sapling":0,"pale_oak_sapling":0,"mangrove_propagule":0,"bedrock":1,"water":0,"lava":0,"sand":1,"suspicious_sand":1,"red_sand":1,"gravel":1,"suspicious_gravel":1,"gold_ore":1,"deepslate_gold_ore":1,"iron_ore":1,"deepslate_iron_ore":1,"coal_ore":1,"deepslate_coal_ore":1,"nether_gold_ore":1,"oak_log":1,"spruce_log":1,"birch_log":1,"jungle_log":1,"acacia_log":1,"cherry_log":1,"dark_oak_log":1,"pale_oak_log":1,"mangrove_log":1,"mangrove_roots":1,"muddy_mangrove_roots":1,"bamboo_block":1,"stripped_spruce_log":1,"stripped_birch_log":1,"stripped_jungle_log":1,"stripped_acacia_log":1,"stripped_cherry_log":1,"stripped_dark_oak_log":1,"stripped_pale_oak_log":1,"stripped_oak_log":1,"stripped_mangrove_log":1,"stripped_bamboo_block":1,"oak_wood":1,"spruce_wood":1,"birch_wood":1,"jungle_wood":1,"acacia_wood":1,"cherry_wood":1,"dark_oak_wood":1,"mangrove_wood":1,"stripped_oak_wood":1,"stripped_spruce_wood":1,"stripped_birch_wood":1,"stripped_jungle_wood":1,"stripped_acacia_wood":1,"stripped_cherry_wood":1,"stripped_dark_oak_wood":1,"stripped_pale_oak_wood":1,"stripped_mangrove_wood":1,"oak_leaves":1,"spruce_leaves":1,"birch_leaves":1,"jungle_leaves":1,"acacia_leaves":1,"cherry_leaves":1,"dark_oak_leaves":1,"pale_oak_leaves":1,"mangrove_leaves":1,"azalea_leaves":1,"flowering_azalea_leaves":1,"sponge":1,"wet_sponge":1,"glass":1,"lapis_ore":1,"deepslate_lapis_ore":1,"lapis_block":1,"dispenser":1,"sandstone":1,"chiseled_sandstone":1,"cut_sandstone":1,"note_block":1,"white_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"orange_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"magenta_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"light_blue_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"yellow_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"lime_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"pink_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"gray_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"light_gray_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"cyan_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"purple_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"blue_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"brown_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"green_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"red_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"black_bed":[2,3,2,3,3,2,3,2,4,5,4,5,5,4,5,4],"powered_rail":0,"detector_rail":0,"sticky_piston":[6,7,8,9,10,11,1,1,1,1,1,1],"cobweb":0,"short_grass":0,"fern":0,"dead_bush":0,"bush":0,"short_dry_grass":0,"tall_dry_grass":0,"seagrass":0,"tall_seagrass":0,"piston":[6,7,8,9,10,11,1,1,1,1,1,1],"piston_head":[12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23],"white_wool":1,"orange_wool":1,"magenta_wool":1,"light_blue_wool":1,"yellow_wool":1,"lime_wool":1,"pink_wool":1,"gray_wool":1,"light_gray_wool":1,"cyan_wool":1,"purple_wool":1,"blue_wool":1,"brown_wool":1,"green_wool":1,"red_wool":1,"black_wool":1,"moving_piston":0,"dandelion":0,"torchflower":0,"poppy":0,"blue_orchid":0,"allium":0,"azure_bluet":0,"red_tulip":0,"orange_tulip":0,"white_tulip":0,"pink_tulip":0,"oxeye_daisy":0,"cornflower":0,"wither_rose":0,"lily_of_the_valley":0,"brown_mushroom":0,"red_mushroom":0,"gold_block":1,"iron_block":1,"bricks":1,"tnt":1,"bookshelf":1,"chiseled_bookshelf":1,"acacia_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"bamboo_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"birch_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"cherry_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"crimson_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"dark_oak_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"jungle_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"mangrove_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"oak_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"pale_oak_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"spruce_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"warped_shelf":[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27],"mossy_cobblestone":1,"obsidian":1,"torch":0,"wall_torch":0,"fire":0,"soul_fire":0,"spawner":1,"creaking_heart":1,"oak_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"redstone_wire":0,"diamond_ore":1,"deepslate_diamond_ore":1,"diamond_block":1,"crafting_table":1,"wheat":0,"farmland":57,"furnace":1,"oak_sign":0,"spruce_sign":0,"birch_sign":0,"acacia_sign":0,"cherry_sign":0,"jungle_sign":0,"dark_oak_sign":0,"pale_oak_sign":0,"mangrove_sign":0,"bamboo_sign":0,"oak_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"ladder":[62,62,63,63,64,64,65,65],"rail":0,"cobblestone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"oak_wall_sign":0,"spruce_wall_sign":0,"birch_wall_sign":0,"acacia_wall_sign":0,"cherry_wall_sign":0,"jungle_wall_sign":0,"dark_oak_wall_sign":0,"pale_oak_wall_sign":0,"mangrove_wall_sign":0,"bamboo_wall_sign":0,"oak_hanging_sign":0,"spruce_hanging_sign":0,"birch_hanging_sign":0,"acacia_hanging_sign":0,"cherry_hanging_sign":0,"jungle_hanging_sign":0,"dark_oak_hanging_sign":0,"pale_oak_hanging_sign":0,"crimson_hanging_sign":0,"warped_hanging_sign":0,"mangrove_hanging_sign":0,"bamboo_hanging_sign":0,"oak_wall_hanging_sign":[66,66,66,66,67,67,67,67],"spruce_wall_hanging_sign":[66,66,66,66,67,67,67,67],"birch_wall_hanging_sign":[66,66,66,66,67,67,67,67],"acacia_wall_hanging_sign":[66,66,66,66,67,67,67,67],"cherry_wall_hanging_sign":[66,66,66,66,67,67,67,67],"jungle_wall_hanging_sign":[66,66,66,66,67,67,67,67],"dark_oak_wall_hanging_sign":[66,66,66,66,67,67,67,67],"pale_oak_wall_hanging_sign":[66,66,66,66,67,67,67,67],"mangrove_wall_hanging_sign":[66,66,66,66,67,67,67,67],"crimson_wall_hanging_sign":[66,66,66,66,67,67,67,67],"warped_wall_hanging_sign":[66,66,66,66,67,67,67,67],"bamboo_wall_hanging_sign":[66,66,66,66,67,67,67,67],"lever":0,"stone_pressure_plate":0,"iron_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"oak_pressure_plate":0,"spruce_pressure_plate":0,"birch_pressure_plate":0,"jungle_pressure_plate":0,"acacia_pressure_plate":0,"cherry_pressure_plate":0,"dark_oak_pressure_plate":0,"pale_oak_pressure_plate":0,"mangrove_pressure_plate":0,"bamboo_pressure_plate":0,"redstone_ore":1,"deepslate_redstone_ore":1,"redstone_torch":0,"redstone_wall_torch":0,"stone_button":0,"snow":[0,68,69,70,71,72,73,74],"ice":1,"snow_block":1,"cactus":75,"cactus_flower":0,"clay":1,"sugar_cane":0,"jukebox":1,"oak_fence":[76,77,76,77,78,79,78,79,80,81,80,81,82,83,82,83,84,85,84,85,86,87,86,87,88,89,88,89,90,91,90,91],"netherrack":1,"soul_sand":92,"soul_soil":1,"basalt":1,"polished_basalt":1,"soul_torch":0,"soul_wall_torch":0,"copper_torch":0,"copper_wall_torch":0,"glowstone":1,"nether_portal":0,"carved_pumpkin":1,"jack_o_lantern":1,"cake":[93,94,95,96,97,98,99],"repeater":100,"white_stained_glass":1,"orange_stained_glass":1,"magenta_stained_glass":1,"light_blue_stained_glass":1,"yellow_stained_glass":1,"lime_stained_glass":1,"pink_stained_glass":1,"gray_stained_glass":1,"light_gray_stained_glass":1,"cyan_stained_glass":1,"purple_stained_glass":1,"blue_stained_glass":1,"brown_stained_glass":1,"green_stained_glass":1,"red_stained_glass":1,"black_stained_glass":1,"oak_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"spruce_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"birch_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"jungle_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"acacia_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"cherry_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"dark_oak_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"pale_oak_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"mangrove_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"bamboo_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"stone_bricks":1,"mossy_stone_bricks":1,"cracked_stone_bricks":1,"chiseled_stone_bricks":1,"packed_mud":1,"mud_bricks":1,"infested_stone":1,"infested_cobblestone":1,"infested_stone_bricks":1,"infested_mossy_stone_bricks":1,"infested_cracked_stone_bricks":1,"infested_chiseled_stone_bricks":1,"brown_mushroom_block":1,"red_mushroom_block":1,"mushroom_stem":1,"iron_bars":[107,108,107,108,109,110,109,110,111,112,111,112,113,114,113,114,115,116,115,116,117,118,117,118,119,120,119,120,121,122,121,122],"copper_bars":[123,124,123,124,125,126,125,126,127,128,127,128,129,130,129,130,131,132,131,132,133,134,133,134,135,136,135,136,137,138,137,138],"exposed_copper_bars":[139,140,139,140,141,142,141,142,143,144,143,144,145,146,145,146,147,148,147,148,149,150,149,150,151,152,151,152,153,154,153,154],"weathered_copper_bars":[155,156,155,156,157,158,157,158,159,160,159,160,161,162,161,162,163,164,163,164,165,166,165,166,167,168,167,168,169,170,169,170],"oxidized_copper_bars":[171,172,171,172,173,174,173,174,175,176,175,176,177,178,177,178,179,180,179,180,181,182,181,182,183,184,183,184,185,186,185,186],"waxed_copper_bars":[187,188,187,188,189,190,189,190,191,192,191,192,193,194,193,194,195,196,195,196,197,198,197,198,199,200,199,200,201,202,201,202],"waxed_exposed_copper_bars":[203,204,203,204,205,206,205,206,207,208,207,208,209,210,209,210,211,212,211,212,213,214,213,214,215,216,215,216,217,218,217,218],"waxed_weathered_copper_bars":[219,220,219,220,221,222,221,222,223,224,223,224,225,226,225,226,227,228,227,228,229,230,229,230,231,232,231,232,233,234,233,234],"waxed_oxidized_copper_bars":[235,236,235,236,237,238,237,238,239,240,239,240,241,242,241,242,243,244,243,244,245,246,245,246,247,248,247,248,249,250,249,250],"iron_chain":[251,251,252,252,253,253],"copper_chain":[251,251,252,252,253,253],"exposed_copper_chain":[251,251,252,252,253,253],"weathered_copper_chain":[251,251,252,252,253,253],"oxidized_copper_chain":[251,251,252,252,253,253],"waxed_copper_chain":[251,251,252,252,253,253],"waxed_exposed_copper_chain":[251,251,252,252,253,253],"waxed_weathered_copper_chain":[251,251,252,252,253,253],"waxed_oxidized_copper_chain":[251,251,252,252,253,253],"glass_pane":[254,255,254,255,256,257,256,257,258,259,258,259,260,261,260,261,262,263,262,263,264,265,264,265,266,267,266,267,268,269,268,269],"pumpkin":1,"melon":1,"attached_pumpkin_stem":0,"attached_melon_stem":0,"pumpkin_stem":0,"melon_stem":0,"vine":0,"glow_lichen":0,"resin_clump":0,"oak_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"stone_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"mud_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"mycelium":1,"lily_pad":272,"resin_block":1,"resin_bricks":1,"resin_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"resin_brick_slab":[273,273,274,274,1,1],"resin_brick_wall":[275,276,277,275,276,277,0,278,279,0,278,279,280,281,282,280,281,282,283,284,285,283,284,285,286,287,288,286,287,288,289,290,291,289,290,291,292,293,294,292,293,294,295,296,297,295,296,297,298,299,300,298,299,300,301,302,303,301,302,303,304,305,306,304,305,306,307,308,309,307,308,309,310,311,312,310,311,312,313,314,315,313,314,315,316,317,318,316,317,318,319,320,321,319,320,321,322,323,324,322,323,324,325,326,327,325,326,327,328,329,330,328,329,330,331,332,333,331,332,333,334,335,336,334,335,336,337,338,339,337,338,339,340,341,342,340,341,342,343,344,345,343,344,345,346,347,348,346,347,348,349,350,351,349,350,351,352,353,354,352,353,354,355,356,357,355,356,357,358,359,360,358,359,360,361,362,363,361,362,363,364,365,366,364,365,366,367,368,369,367,368,369,370,371,372,370,371,372,373,374,375,373,374,375,376,377,378,376,377,378,379,380,381,379,380,381,382,383,384,382,383,384,385,386,387,385,386,387,388,389,390,388,389,390,391,392,393,391,392,393,394,395,396,394,395,396,397,398,399,397,398,399,400,401,402,400,401,402,403,404,405,403,404,405,406,407,408,406,407,408,409,410,411,409,410,411,412,413,414,412,413,414,415,416,417,415,416,417,418,419,420,418,419,420,421,422,423,421,422,423,424,425,426,424,425,426,427,428,429,427,428,429,430,431,432,430,431,432,433,434,435,433,434,435],"chiseled_resin_bricks":1,"nether_bricks":1,"nether_brick_fence":[436,437,436,437,438,439,438,439,440,441,440,441,442,443,442,443,444,445,444,445,446,447,446,447,448,449,448,449,450,451,450,451],"nether_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"nether_wart":0,"enchanting_table":452,"brewing_stand":453,"cauldron":454,"water_cauldron":454,"lava_cauldron":454,"powder_snow_cauldron":454,"end_portal":0,"end_portal_frame":[455,455,455,455,456,456,456,456],"end_stone":1,"dragon_egg":457,"redstone_lamp":1,"cocoa":[458,459,460,461,462,463,464,465,466,467,468,469],"sandstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"emerald_ore":1,"deepslate_emerald_ore":1,"ender_chest":470,"tripwire_hook":0,"tripwire":0,"emerald_block":1,"spruce_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"birch_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"jungle_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"command_block":1,"beacon":1,"cobblestone_wall":[471,472,473,471,472,473,0,474,475,0,474,475,476,477,478,476,477,478,479,480,481,479,480,481,482,483,484,482,483,484,485,486,487,485,486,487,488,489,490,488,489,490,491,492,493,491,492,493,494,495,496,494,495,496,497,498,499,497,498,499,500,501,502,500,501,502,503,504,505,503,504,505,506,507,508,506,507,508,509,510,511,509,510,511,512,513,514,512,513,514,515,516,517,515,516,517,518,519,520,518,519,520,521,522,523,521,522,523,524,525,526,524,525,526,527,528,529,527,528,529,530,531,532,530,531,532,533,534,535,533,534,535,536,537,538,536,537,538,539,540,541,539,540,541,542,543,544,542,543,544,545,546,547,545,546,547,548,549,550,548,549,550,551,552,553,551,552,553,554,555,556,554,555,556,557,558,559,557,558,559,560,561,562,560,561,562,563,564,565,563,564,565,566,567,568,566,567,568,569,570,571,569,570,571,572,573,574,572,573,574,575,576,577,575,576,577,578,579,580,578,579,580,581,582,583,581,582,583,584,585,586,584,585,586,587,588,589,587,588,589,590,591,592,590,591,592,593,594,595,593,594,595,596,597,598,596,597,598,599,600,601,599,600,601,602,603,604,602,603,604,605,606,607,605,606,607,608,609,610,608,609,610,611,612,613,611,612,613,614,615,616,614,615,616,617,618,619,617,618,619,620,621,622,620,621,622,623,624,625,623,624,625,626,627,628,626,627,628,629,630,631,629,630,631],"mossy_cobblestone_wall":[632,633,634,632,633,634,0,635,636,0,635,636,637,638,639,637,638,639,640,641,642,640,641,642,643,644,645,643,644,645,646,647,648,646,647,648,649,650,651,649,650,651,652,653,654,652,653,654,655,656,657,655,656,657,658,659,660,658,659,660,661,662,663,661,662,663,664,665,666,664,665,666,667,668,669,667,668,669,670,671,672,670,671,672,673,674,675,673,674,675,676,677,678,676,677,678,679,680,681,679,680,681,682,683,684,682,683,684,685,686,687,685,686,687,688,689,690,688,689,690,691,692,693,691,692,693,694,695,696,694,695,696,697,698,699,697,698,699,700,701,702,700,701,702,703,704,705,703,704,705,706,707,708,706,707,708,709,710,711,709,710,711,712,713,714,712,713,714,715,716,717,715,716,717,718,719,720,718,719,720,721,722,723,721,722,723,724,725,726,724,725,726,727,728,729,727,728,729,730,731,732,730,731,732,733,734,735,733,734,735,736,737,738,736,737,738,739,740,741,739,740,741,742,743,744,742,743,744,745,746,747,745,746,747,748,749,750,748,749,750,751,752,753,751,752,753,754,755,756,754,755,756,757,758,759,757,758,759,760,761,762,760,761,762,763,764,765,763,764,765,766,767,768,766,767,768,769,770,771,769,770,771,772,773,774,772,773,774,775,776,777,775,776,777,778,779,780,778,779,780,781,782,783,781,782,783,784,785,786,784,785,786,787,788,789,787,788,789,790,791,792,790,791,792],"flower_pot":793,"potted_torchflower":793,"potted_oak_sapling":793,"potted_spruce_sapling":793,"potted_birch_sapling":793,"potted_jungle_sapling":793,"potted_acacia_sapling":793,"potted_cherry_sapling":793,"potted_dark_oak_sapling":793,"potted_pale_oak_sapling":793,"potted_mangrove_propagule":793,"potted_fern":793,"potted_dandelion":793,"potted_poppy":793,"potted_blue_orchid":793,"potted_allium":793,"potted_azure_bluet":793,"potted_red_tulip":793,"potted_orange_tulip":793,"potted_white_tulip":793,"potted_pink_tulip":793,"potted_oxeye_daisy":793,"potted_cornflower":793,"potted_lily_of_the_valley":793,"potted_wither_rose":793,"potted_red_mushroom":793,"potted_brown_mushroom":793,"potted_dead_bush":793,"potted_cactus":793,"carrots":0,"potatoes":0,"oak_button":0,"spruce_button":0,"birch_button":0,"jungle_button":0,"acacia_button":0,"cherry_button":0,"dark_oak_button":0,"pale_oak_button":0,"mangrove_button":0,"bamboo_button":0,"skeleton_skull":794,"skeleton_wall_skull":[795,795,796,796,797,797,798,798],"wither_skeleton_skull":794,"wither_skeleton_wall_skull":[795,795,796,796,797,797,798,798],"zombie_head":794,"zombie_wall_head":[795,795,796,796,797,797,798,798],"player_head":794,"player_wall_head":[795,795,796,796,797,797,798,798],"creeper_head":794,"creeper_wall_head":[795,795,796,796,797,797,798,798],"dragon_head":794,"dragon_wall_head":[795,795,796,796,797,797,798,798],"piglin_head":799,"piglin_wall_head":[800,800,801,801,802,802,803,803],"anvil":[804,804,805,805],"chipped_anvil":[804,804,805,805],"damaged_anvil":[804,804,805,805],"trapped_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"light_weighted_pressure_plate":0,"heavy_weighted_pressure_plate":0,"comparator":100,"daylight_detector":806,"redstone_block":1,"nether_quartz_ore":1,"hopper":[807,808,809,810,811,807,808,809,810,811],"quartz_block":1,"chiseled_quartz_block":1,"quartz_pillar":1,"quartz_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"activator_rail":0,"dropper":1,"white_terracotta":1,"orange_terracotta":1,"magenta_terracotta":1,"light_blue_terracotta":1,"yellow_terracotta":1,"lime_terracotta":1,"pink_terracotta":1,"gray_terracotta":1,"light_gray_terracotta":1,"cyan_terracotta":1,"purple_terracotta":1,"blue_terracotta":1,"brown_terracotta":1,"green_terracotta":1,"red_terracotta":1,"black_terracotta":1,"white_stained_glass_pane":[812,813,812,813,814,815,814,815,816,817,816,817,818,819,818,819,820,821,820,821,822,823,822,823,824,825,824,825,826,827,826,827],"orange_stained_glass_pane":[828,829,828,829,830,831,830,831,832,833,832,833,834,835,834,835,836,837,836,837,838,839,838,839,840,841,840,841,842,843,842,843],"magenta_stained_glass_pane":[844,845,844,845,846,847,846,847,848,849,848,849,850,851,850,851,852,853,852,853,854,855,854,855,856,857,856,857,858,859,858,859],"light_blue_stained_glass_pane":[860,861,860,861,862,863,862,863,864,865,864,865,866,867,866,867,868,869,868,869,870,871,870,871,872,873,872,873,874,875,874,875],"yellow_stained_glass_pane":[876,877,876,877,878,879,878,879,880,881,880,881,882,883,882,883,884,885,884,885,886,887,886,887,888,889,888,889,890,891,890,891],"lime_stained_glass_pane":[892,893,892,893,894,895,894,895,896,897,896,897,898,899,898,899,900,901,900,901,902,903,902,903,904,905,904,905,906,907,906,907],"pink_stained_glass_pane":[908,909,908,909,910,911,910,911,912,913,912,913,914,915,914,915,916,917,916,917,918,919,918,919,920,921,920,921,922,923,922,923],"gray_stained_glass_pane":[924,925,924,925,926,927,926,927,928,929,928,929,930,931,930,931,932,933,932,933,934,935,934,935,936,937,936,937,938,939,938,939],"light_gray_stained_glass_pane":[940,941,940,941,942,943,942,943,944,945,944,945,946,947,946,947,948,949,948,949,950,951,950,951,952,953,952,953,954,955,954,955],"cyan_stained_glass_pane":[956,957,956,957,958,959,958,959,960,961,960,961,962,963,962,963,964,965,964,965,966,967,966,967,968,969,968,969,970,971,970,971],"purple_stained_glass_pane":[972,973,972,973,974,975,974,975,976,977,976,977,978,979,978,979,980,981,980,981,982,983,982,983,984,985,984,985,986,987,986,987],"blue_stained_glass_pane":[988,989,988,989,990,991,990,991,992,993,992,993,994,995,994,995,996,997,996,997,998,999,998,999,1000,1001,1000,1001,1002,1003,1002,1003],"brown_stained_glass_pane":[1004,1005,1004,1005,1006,1007,1006,1007,1008,1009,1008,1009,1010,1011,1010,1011,1012,1013,1012,1013,1014,1015,1014,1015,1016,1017,1016,1017,1018,1019,1018,1019],"green_stained_glass_pane":[1020,1021,1020,1021,1022,1023,1022,1023,1024,1025,1024,1025,1026,1027,1026,1027,1028,1029,1028,1029,1030,1031,1030,1031,1032,1033,1032,1033,1034,1035,1034,1035],"red_stained_glass_pane":[1036,1037,1036,1037,1038,1039,1038,1039,1040,1041,1040,1041,1042,1043,1042,1043,1044,1045,1044,1045,1046,1047,1046,1047,1048,1049,1048,1049,1050,1051,1050,1051],"black_stained_glass_pane":[1052,1053,1052,1053,1054,1055,1054,1055,1056,1057,1056,1057,1058,1059,1058,1059,1060,1061,1060,1061,1062,1063,1062,1063,1064,1065,1064,1065,1066,1067,1066,1067],"acacia_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"cherry_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"dark_oak_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"pale_oak_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"mangrove_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"bamboo_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"bamboo_mosaic_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"slime_block":1,"barrier":1,"light":0,"iron_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"prismarine":1,"prismarine_bricks":1,"dark_prismarine":1,"prismarine_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"prismarine_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"dark_prismarine_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"prismarine_slab":[273,273,274,274,1,1],"prismarine_brick_slab":[273,273,274,274,1,1],"dark_prismarine_slab":[273,273,274,274,1,1],"sea_lantern":1,"hay_block":1,"white_carpet":1068,"orange_carpet":1068,"magenta_carpet":1068,"light_blue_carpet":1068,"yellow_carpet":1068,"lime_carpet":1068,"pink_carpet":1068,"gray_carpet":1068,"light_gray_carpet":1068,"cyan_carpet":1068,"purple_carpet":1068,"blue_carpet":1068,"brown_carpet":1068,"green_carpet":1068,"red_carpet":1068,"black_carpet":1068,"terracotta":1,"coal_block":1,"packed_ice":1,"sunflower":0,"lilac":0,"rose_bush":0,"peony":0,"tall_grass":0,"large_fern":0,"white_banner":0,"orange_banner":0,"magenta_banner":0,"light_blue_banner":0,"yellow_banner":0,"lime_banner":0,"pink_banner":0,"gray_banner":0,"light_gray_banner":0,"cyan_banner":0,"purple_banner":0,"blue_banner":0,"brown_banner":0,"green_banner":0,"red_banner":0,"black_banner":0,"white_wall_banner":0,"orange_wall_banner":0,"magenta_wall_banner":0,"light_blue_wall_banner":0,"yellow_wall_banner":0,"lime_wall_banner":0,"pink_wall_banner":0,"gray_wall_banner":0,"light_gray_wall_banner":0,"cyan_wall_banner":0,"purple_wall_banner":0,"blue_wall_banner":0,"brown_wall_banner":0,"green_wall_banner":0,"red_wall_banner":0,"black_wall_banner":0,"red_sandstone":1,"chiseled_red_sandstone":1,"cut_red_sandstone":1,"red_sandstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"oak_slab":[273,273,274,274,1,1],"spruce_slab":[273,273,274,274,1,1],"birch_slab":[273,273,274,274,1,1],"jungle_slab":[273,273,274,274,1,1],"acacia_slab":[273,273,274,274,1,1],"cherry_slab":[273,273,274,274,1,1],"dark_oak_slab":[273,273,274,274,1,1],"pale_oak_slab":[273,273,274,274,1,1],"mangrove_slab":[273,273,274,274,1,1],"bamboo_slab":[273,273,274,274,1,1],"bamboo_mosaic_slab":[273,273,274,274,1,1],"stone_slab":[273,273,274,274,1,1],"smooth_stone_slab":[273,273,274,274,1,1],"sandstone_slab":[273,273,274,274,1,1],"cut_sandstone_slab":[273,273,274,274,1,1],"petrified_oak_slab":[273,273,274,274,1,1],"cobblestone_slab":[273,273,274,274,1,1],"brick_slab":[273,273,274,274,1,1],"stone_brick_slab":[273,273,274,274,1,1],"mud_brick_slab":[273,273,274,274,1,1],"nether_brick_slab":[273,273,274,274,1,1],"quartz_slab":[273,273,274,274,1,1],"red_sandstone_slab":[273,273,274,274,1,1],"cut_red_sandstone_slab":[273,273,274,274,1,1],"purpur_slab":[273,273,274,274,1,1],"smooth_stone":1,"smooth_sandstone":1,"smooth_quartz":1,"smooth_red_sandstone":1,"spruce_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"birch_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"jungle_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"acacia_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"cherry_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"dark_oak_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"pale_oak_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"mangrove_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"bamboo_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"spruce_fence":[1069,1070,1069,1070,1071,1072,1071,1072,1073,1074,1073,1074,1075,1076,1075,1076,1077,1078,1077,1078,1079,1080,1079,1080,1081,1082,1081,1082,1083,1084,1083,1084],"birch_fence":[1085,1086,1085,1086,1087,1088,1087,1088,1089,1090,1089,1090,1091,1092,1091,1092,1093,1094,1093,1094,1095,1096,1095,1096,1097,1098,1097,1098,1099,1100,1099,1100],"jungle_fence":[1101,1102,1101,1102,1103,1104,1103,1104,1105,1106,1105,1106,1107,1108,1107,1108,1109,1110,1109,1110,1111,1112,1111,1112,1113,1114,1113,1114,1115,1116,1115,1116],"acacia_fence":[1117,1118,1117,1118,1119,1120,1119,1120,1121,1122,1121,1122,1123,1124,1123,1124,1125,1126,1125,1126,1127,1128,1127,1128,1129,1130,1129,1130,1131,1132,1131,1132],"cherry_fence":[1133,1134,1133,1134,1135,1136,1135,1136,1137,1138,1137,1138,1139,1140,1139,1140,1141,1142,1141,1142,1143,1144,1143,1144,1145,1146,1145,1146,1147,1148,1147,1148],"dark_oak_fence":[1149,1150,1149,1150,1151,1152,1151,1152,1153,1154,1153,1154,1155,1156,1155,1156,1157,1158,1157,1158,1159,1160,1159,1160,1161,1162,1161,1162,1163,1164,1163,1164],"pale_oak_fence":[1165,1166,1165,1166,1167,1168,1167,1168,1169,1170,1169,1170,1171,1172,1171,1172,1173,1174,1173,1174,1175,1176,1175,1176,1177,1178,1177,1178,1179,1180,1179,1180],"mangrove_fence":[1181,1182,1181,1182,1183,1184,1183,1184,1185,1186,1185,1186,1187,1188,1187,1188,1189,1190,1189,1190,1191,1192,1191,1192,1193,1194,1193,1194,1195,1196,1195,1196],"bamboo_fence":[1197,1198,1197,1198,1199,1200,1199,1200,1201,1202,1201,1202,1203,1204,1203,1204,1205,1206,1205,1206,1207,1208,1207,1208,1209,1210,1209,1210,1211,1212,1211,1212],"spruce_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"birch_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"jungle_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"acacia_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"cherry_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"dark_oak_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"pale_oak_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"mangrove_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"bamboo_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"end_rod":[1213,1214,1213,1214,1215,1215],"chorus_plant":[1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279],"chorus_flower":1,"purpur_block":1,"purpur_pillar":1,"purpur_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"end_stone_bricks":1,"torchflower_crop":0,"pitcher_crop":[0,1280,0,1281,0,1281,0,1281,0,1281],"pitcher_plant":0,"beetroots":0,"dirt_path":1282,"end_gateway":0,"repeating_command_block":1,"chain_command_block":1,"frosted_ice":1,"magma_block":1,"nether_wart_block":1,"red_nether_bricks":1,"bone_block":1,"structure_void":0,"observer":1,"shulker_box":1,"white_shulker_box":1,"orange_shulker_box":1,"magenta_shulker_box":1,"light_blue_shulker_box":1,"yellow_shulker_box":1,"lime_shulker_box":1,"pink_shulker_box":1,"gray_shulker_box":1,"light_gray_shulker_box":1,"cyan_shulker_box":1,"purple_shulker_box":1,"blue_shulker_box":1,"brown_shulker_box":1,"green_shulker_box":1,"red_shulker_box":1,"black_shulker_box":1,"white_glazed_terracotta":1,"orange_glazed_terracotta":1,"magenta_glazed_terracotta":1,"light_blue_glazed_terracotta":1,"yellow_glazed_terracotta":1,"lime_glazed_terracotta":1,"pink_glazed_terracotta":1,"gray_glazed_terracotta":1,"light_gray_glazed_terracotta":1,"cyan_glazed_terracotta":1,"purple_glazed_terracotta":1,"blue_glazed_terracotta":1,"brown_glazed_terracotta":1,"green_glazed_terracotta":1,"red_glazed_terracotta":1,"black_glazed_terracotta":1,"white_concrete":1,"orange_concrete":1,"magenta_concrete":1,"light_blue_concrete":1,"yellow_concrete":1,"lime_concrete":1,"pink_concrete":1,"gray_concrete":1,"light_gray_concrete":1,"cyan_concrete":1,"purple_concrete":1,"blue_concrete":1,"brown_concrete":1,"green_concrete":1,"red_concrete":1,"black_concrete":1,"white_concrete_powder":1,"orange_concrete_powder":1,"magenta_concrete_powder":1,"light_blue_concrete_powder":1,"yellow_concrete_powder":1,"lime_concrete_powder":1,"pink_concrete_powder":1,"gray_concrete_powder":1,"light_gray_concrete_powder":1,"cyan_concrete_powder":1,"purple_concrete_powder":1,"blue_concrete_powder":1,"brown_concrete_powder":1,"green_concrete_powder":1,"red_concrete_powder":1,"black_concrete_powder":1,"kelp":0,"kelp_plant":0,"dried_kelp_block":1,"turtle_egg":[1283,1283,1283,1284,1284,1284,1284,1284,1284,1284,1284,1284],"sniffer_egg":1285,"dried_ghast":1286,"dead_tube_coral_block":1,"dead_brain_coral_block":1,"dead_bubble_coral_block":1,"dead_fire_coral_block":1,"dead_horn_coral_block":1,"tube_coral_block":1,"brain_coral_block":1,"bubble_coral_block":1,"fire_coral_block":1,"horn_coral_block":1,"dead_tube_coral":0,"dead_brain_coral":0,"dead_bubble_coral":0,"dead_fire_coral":0,"dead_horn_coral":0,"tube_coral":0,"brain_coral":0,"bubble_coral":0,"fire_coral":0,"horn_coral":0,"dead_tube_coral_fan":0,"dead_brain_coral_fan":0,"dead_bubble_coral_fan":0,"dead_fire_coral_fan":0,"dead_horn_coral_fan":0,"tube_coral_fan":0,"brain_coral_fan":0,"bubble_coral_fan":0,"fire_coral_fan":0,"horn_coral_fan":0,"dead_tube_coral_wall_fan":0,"dead_brain_coral_wall_fan":0,"dead_bubble_coral_wall_fan":0,"dead_fire_coral_wall_fan":0,"dead_horn_coral_wall_fan":0,"tube_coral_wall_fan":0,"brain_coral_wall_fan":0,"bubble_coral_wall_fan":0,"fire_coral_wall_fan":0,"horn_coral_wall_fan":0,"sea_pickle":[1287,1287,1288,1288,1289,1289,1290,1290],"blue_ice":1,"conduit":1291,"bamboo_sapling":0,"bamboo":[1292,1293,1294,1295,1296,1297,1298,1299,1300,1301,1302,1303],"potted_bamboo":793,"void_air":0,"cave_air":0,"bubble_column":0,"polished_granite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"smooth_red_sandstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"mossy_stone_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_diorite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"mossy_cobblestone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"end_stone_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"stone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"smooth_sandstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"smooth_quartz_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"granite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"andesite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"red_nether_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_andesite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"diorite_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_granite_slab":[273,273,274,274,1,1],"smooth_red_sandstone_slab":[273,273,274,274,1,1],"mossy_stone_brick_slab":[273,273,274,274,1,1],"polished_diorite_slab":[273,273,274,274,1,1],"mossy_cobblestone_slab":[273,273,274,274,1,1],"end_stone_brick_slab":[273,273,274,274,1,1],"smooth_sandstone_slab":[273,273,274,274,1,1],"smooth_quartz_slab":[273,273,274,274,1,1],"granite_slab":[273,273,274,274,1,1],"andesite_slab":[273,273,274,274,1,1],"red_nether_brick_slab":[273,273,274,274,1,1],"polished_andesite_slab":[273,273,274,274,1,1],"diorite_slab":[273,273,274,274,1,1],"brick_wall":[1304,1305,1306,1304,1305,1306,0,1307,1308,0,1307,1308,1309,1310,1311,1309,1310,1311,1312,1313,1314,1312,1313,1314,1315,1316,1317,1315,1316,1317,1318,1319,1320,1318,1319,1320,1321,1322,1323,1321,1322,1323,1324,1325,1326,1324,1325,1326,1327,1328,1329,1327,1328,1329,1330,1331,1332,1330,1331,1332,1333,1334,1335,1333,1334,1335,1336,1337,1338,1336,1337,1338,1339,1340,1341,1339,1340,1341,1342,1343,1344,1342,1343,1344,1345,1346,1347,1345,1346,1347,1348,1349,1350,1348,1349,1350,1351,1352,1353,1351,1352,1353,1354,1355,1356,1354,1355,1356,1357,1358,1359,1357,1358,1359,1360,1361,1362,1360,1361,1362,1363,1364,1365,1363,1364,1365,1366,1367,1368,1366,1367,1368,1369,1370,1371,1369,1370,1371,1372,1373,1374,1372,1373,1374,1375,1376,1377,1375,1376,1377,1378,1379,1380,1378,1379,1380,1381,1382,1383,1381,1382,1383,1384,1385,1386,1384,1385,1386,1387,1388,1389,1387,1388,1389,1390,1391,1392,1390,1391,1392,1393,1394,1395,1393,1394,1395,1396,1397,1398,1396,1397,1398,1399,1400,1401,1399,1400,1401,1402,1403,1404,1402,1403,1404,1405,1406,1407,1405,1406,1407,1408,1409,1410,1408,1409,1410,1411,1412,1413,1411,1412,1413,1414,1415,1416,1414,1415,1416,1417,1418,1419,1417,1418,1419,1420,1421,1422,1420,1421,1422,1423,1424,1425,1423,1424,1425,1426,1427,1428,1426,1427,1428,1429,1430,1431,1429,1430,1431,1432,1433,1434,1432,1433,1434,1435,1436,1437,1435,1436,1437,1438,1439,1440,1438,1439,1440,1441,1442,1443,1441,1442,1443,1444,1445,1446,1444,1445,1446,1447,1448,1449,1447,1448,1449,1450,1451,1452,1450,1451,1452,1453,1454,1455,1453,1454,1455,1456,1457,1458,1456,1457,1458,1459,1460,1461,1459,1460,1461,1462,1463,1464,1462,1463,1464],"prismarine_wall":[1465,1466,1467,1465,1466,1467,0,1468,1469,0,1468,1469,1470,1471,1472,1470,1471,1472,1473,1474,1475,1473,1474,1475,1476,1477,1478,1476,1477,1478,1479,1480,1481,1479,1480,1481,1482,1483,1484,1482,1483,1484,1485,1486,1487,1485,1486,1487,1488,1489,1490,1488,1489,1490,1491,1492,1493,1491,1492,1493,1494,1495,1496,1494,1495,1496,1497,1498,1499,1497,1498,1499,1500,1501,1502,1500,1501,1502,1503,1504,1505,1503,1504,1505,1506,1507,1508,1506,1507,1508,1509,1510,1511,1509,1510,1511,1512,1513,1514,1512,1513,1514,1515,1516,1517,1515,1516,1517,1518,1519,1520,1518,1519,1520,1521,1522,1523,1521,1522,1523,1524,1525,1526,1524,1525,1526,1527,1528,1529,1527,1528,1529,1530,1531,1532,1530,1531,1532,1533,1534,1535,1533,1534,1535,1536,1537,1538,1536,1537,1538,1539,1540,1541,1539,1540,1541,1542,1543,1544,1542,1543,1544,1545,1546,1547,1545,1546,1547,1548,1549,1550,1548,1549,1550,1551,1552,1553,1551,1552,1553,1554,1555,1556,1554,1555,1556,1557,1558,1559,1557,1558,1559,1560,1561,1562,1560,1561,1562,1563,1564,1565,1563,1564,1565,1566,1567,1568,1566,1567,1568,1569,1570,1571,1569,1570,1571,1572,1573,1574,1572,1573,1574,1575,1576,1577,1575,1576,1577,1578,1579,1580,1578,1579,1580,1581,1582,1583,1581,1582,1583,1584,1585,1586,1584,1585,1586,1587,1588,1589,1587,1588,1589,1590,1591,1592,1590,1591,1592,1593,1594,1595,1593,1594,1595,1596,1597,1598,1596,1597,1598,1599,1600,1601,1599,1600,1601,1602,1603,1604,1602,1603,1604,1605,1606,1607,1605,1606,1607,1608,1609,1610,1608,1609,1610,1611,1612,1613,1611,1612,1613,1614,1615,1616,1614,1615,1616,1617,1618,1619,1617,1618,1619,1620,1621,1622,1620,1621,1622,1623,1624,1625,1623,1624,1625],"red_sandstone_wall":[1626,1627,1628,1626,1627,1628,0,1629,1630,0,1629,1630,1631,1632,1633,1631,1632,1633,1634,1635,1636,1634,1635,1636,1637,1638,1639,1637,1638,1639,1640,1641,1642,1640,1641,1642,1643,1644,1645,1643,1644,1645,1646,1647,1648,1646,1647,1648,1649,1650,1651,1649,1650,1651,1652,1653,1654,1652,1653,1654,1655,1656,1657,1655,1656,1657,1658,1659,1660,1658,1659,1660,1661,1662,1663,1661,1662,1663,1664,1665,1666,1664,1665,1666,1667,1668,1669,1667,1668,1669,1670,1671,1672,1670,1671,1672,1673,1674,1675,1673,1674,1675,1676,1677,1678,1676,1677,1678,1679,1680,1681,1679,1680,1681,1682,1683,1684,1682,1683,1684,1685,1686,1687,1685,1686,1687,1688,1689,1690,1688,1689,1690,1691,1692,1693,1691,1692,1693,1694,1695,1696,1694,1695,1696,1697,1698,1699,1697,1698,1699,1700,1701,1702,1700,1701,1702,1703,1704,1705,1703,1704,1705,1706,1707,1708,1706,1707,1708,1709,1710,1711,1709,1710,1711,1712,1713,1714,1712,1713,1714,1715,1716,1717,1715,1716,1717,1718,1719,1720,1718,1719,1720,1721,1722,1723,1721,1722,1723,1724,1725,1726,1724,1725,1726,1727,1728,1729,1727,1728,1729,1730,1731,1732,1730,1731,1732,1733,1734,1735,1733,1734,1735,1736,1737,1738,1736,1737,1738,1739,1740,1741,1739,1740,1741,1742,1743,1744,1742,1743,1744,1745,1746,1747,1745,1746,1747,1748,1749,1750,1748,1749,1750,1751,1752,1753,1751,1752,1753,1754,1755,1756,1754,1755,1756,1757,1758,1759,1757,1758,1759,1760,1761,1762,1760,1761,1762,1763,1764,1765,1763,1764,1765,1766,1767,1768,1766,1767,1768,1769,1770,1771,1769,1770,1771,1772,1773,1774,1772,1773,1774,1775,1776,1777,1775,1776,1777,1778,1779,1780,1778,1779,1780,1781,1782,1783,1781,1782,1783,1784,1785,1786,1784,1785,1786],"mossy_stone_brick_wall":[1787,1788,1789,1787,1788,1789,0,1790,1791,0,1790,1791,1792,1793,1794,1792,1793,1794,1795,1796,1797,1795,1796,1797,1798,1799,1800,1798,1799,1800,1801,1802,1803,1801,1802,1803,1804,1805,1806,1804,1805,1806,1807,1808,1809,1807,1808,1809,1810,1811,1812,1810,1811,1812,1813,1814,1815,1813,1814,1815,1816,1817,1818,1816,1817,1818,1819,1820,1821,1819,1820,1821,1822,1823,1824,1822,1823,1824,1825,1826,1827,1825,1826,1827,1828,1829,1830,1828,1829,1830,1831,1832,1833,1831,1832,1833,1834,1835,1836,1834,1835,1836,1837,1838,1839,1837,1838,1839,1840,1841,1842,1840,1841,1842,1843,1844,1845,1843,1844,1845,1846,1847,1848,1846,1847,1848,1849,1850,1851,1849,1850,1851,1852,1853,1854,1852,1853,1854,1855,1856,1857,1855,1856,1857,1858,1859,1860,1858,1859,1860,1861,1862,1863,1861,1862,1863,1864,1865,1866,1864,1865,1866,1867,1868,1869,1867,1868,1869,1870,1871,1872,1870,1871,1872,1873,1874,1875,1873,1874,1875,1876,1877,1878,1876,1877,1878,1879,1880,1881,1879,1880,1881,1882,1883,1884,1882,1883,1884,1885,1886,1887,1885,1886,1887,1888,1889,1890,1888,1889,1890,1891,1892,1893,1891,1892,1893,1894,1895,1896,1894,1895,1896,1897,1898,1899,1897,1898,1899,1900,1901,1902,1900,1901,1902,1903,1904,1905,1903,1904,1905,1906,1907,1908,1906,1907,1908,1909,1910,1911,1909,1910,1911,1912,1913,1914,1912,1913,1914,1915,1916,1917,1915,1916,1917,1918,1919,1920,1918,1919,1920,1921,1922,1923,1921,1922,1923,1924,1925,1926,1924,1925,1926,1927,1928,1929,1927,1928,1929,1930,1931,1932,1930,1931,1932,1933,1934,1935,1933,1934,1935,1936,1937,1938,1936,1937,1938,1939,1940,1941,1939,1940,1941,1942,1943,1944,1942,1943,1944,1945,1946,1947,1945,1946,1947],"granite_wall":[1948,1949,1950,1948,1949,1950,0,1951,1952,0,1951,1952,1953,1954,1955,1953,1954,1955,1956,1957,1958,1956,1957,1958,1959,1960,1961,1959,1960,1961,1962,1963,1964,1962,1963,1964,1965,1966,1967,1965,1966,1967,1968,1969,1970,1968,1969,1970,1971,1972,1973,1971,1972,1973,1974,1975,1976,1974,1975,1976,1977,1978,1979,1977,1978,1979,1980,1981,1982,1980,1981,1982,1983,1984,1985,1983,1984,1985,1986,1987,1988,1986,1987,1988,1989,1990,1991,1989,1990,1991,1992,1993,1994,1992,1993,1994,1995,1996,1997,1995,1996,1997,1998,1999,2000,1998,1999,2000,2001,2002,2003,2001,2002,2003,2004,2005,2006,2004,2005,2006,2007,2008,2009,2007,2008,2009,2010,2011,2012,2010,2011,2012,2013,2014,2015,2013,2014,2015,2016,2017,2018,2016,2017,2018,2019,2020,2021,2019,2020,2021,2022,2023,2024,2022,2023,2024,2025,2026,2027,2025,2026,2027,2028,2029,2030,2028,2029,2030,2031,2032,2033,2031,2032,2033,2034,2035,2036,2034,2035,2036,2037,2038,2039,2037,2038,2039,2040,2041,2042,2040,2041,2042,2043,2044,2045,2043,2044,2045,2046,2047,2048,2046,2047,2048,2049,2050,2051,2049,2050,2051,2052,2053,2054,2052,2053,2054,2055,2056,2057,2055,2056,2057,2058,2059,2060,2058,2059,2060,2061,2062,2063,2061,2062,2063,2064,2065,2066,2064,2065,2066,2067,2068,2069,2067,2068,2069,2070,2071,2072,2070,2071,2072,2073,2074,2075,2073,2074,2075,2076,2077,2078,2076,2077,2078,2079,2080,2081,2079,2080,2081,2082,2083,2084,2082,2083,2084,2085,2086,2087,2085,2086,2087,2088,2089,2090,2088,2089,2090,2091,2092,2093,2091,2092,2093,2094,2095,2096,2094,2095,2096,2097,2098,2099,2097,2098,2099,2100,2101,2102,2100,2101,2102,2103,2104,2105,2103,2104,2105,2106,2107,2108,2106,2107,2108],"stone_brick_wall":[2109,2110,2111,2109,2110,2111,0,2112,2113,0,2112,2113,2114,2115,2116,2114,2115,2116,2117,2118,2119,2117,2118,2119,2120,2121,2122,2120,2121,2122,2123,2124,2125,2123,2124,2125,2126,2127,2128,2126,2127,2128,2129,2130,2131,2129,2130,2131,2132,2133,2134,2132,2133,2134,2135,2136,2137,2135,2136,2137,2138,2139,2140,2138,2139,2140,2141,2142,2143,2141,2142,2143,2144,2145,2146,2144,2145,2146,2147,2148,2149,2147,2148,2149,2150,2151,2152,2150,2151,2152,2153,2154,2155,2153,2154,2155,2156,2157,2158,2156,2157,2158,2159,2160,2161,2159,2160,2161,2162,2163,2164,2162,2163,2164,2165,2166,2167,2165,2166,2167,2168,2169,2170,2168,2169,2170,2171,2172,2173,2171,2172,2173,2174,2175,2176,2174,2175,2176,2177,2178,2179,2177,2178,2179,2180,2181,2182,2180,2181,2182,2183,2184,2185,2183,2184,2185,2186,2187,2188,2186,2187,2188,2189,2190,2191,2189,2190,2191,2192,2193,2194,2192,2193,2194,2195,2196,2197,2195,2196,2197,2198,2199,2200,2198,2199,2200,2201,2202,2203,2201,2202,2203,2204,2205,2206,2204,2205,2206,2207,2208,2209,2207,2208,2209,2210,2211,2212,2210,2211,2212,2213,2214,2215,2213,2214,2215,2216,2217,2218,2216,2217,2218,2219,2220,2221,2219,2220,2221,2222,2223,2224,2222,2223,2224,2225,2226,2227,2225,2226,2227,2228,2229,2230,2228,2229,2230,2231,2232,2233,2231,2232,2233,2234,2235,2236,2234,2235,2236,2237,2238,2239,2237,2238,2239,2240,2241,2242,2240,2241,2242,2243,2244,2245,2243,2244,2245,2246,2247,2248,2246,2247,2248,2249,2250,2251,2249,2250,2251,2252,2253,2254,2252,2253,2254,2255,2256,2257,2255,2256,2257,2258,2259,2260,2258,2259,2260,2261,2262,2263,2261,2262,2263,2264,2265,2266,2264,2265,2266,2267,2268,2269,2267,2268,2269],"mud_brick_wall":[2270,2271,2272,2270,2271,2272,0,2273,2274,0,2273,2274,2275,2276,2277,2275,2276,2277,2278,2279,2280,2278,2279,2280,2281,2282,2283,2281,2282,2283,2284,2285,2286,2284,2285,2286,2287,2288,2289,2287,2288,2289,2290,2291,2292,2290,2291,2292,2293,2294,2295,2293,2294,2295,2296,2297,2298,2296,2297,2298,2299,2300,2301,2299,2300,2301,2302,2303,2304,2302,2303,2304,2305,2306,2307,2305,2306,2307,2308,2309,2310,2308,2309,2310,2311,2312,2313,2311,2312,2313,2314,2315,2316,2314,2315,2316,2317,2318,2319,2317,2318,2319,2320,2321,2322,2320,2321,2322,2323,2324,2325,2323,2324,2325,2326,2327,2328,2326,2327,2328,2329,2330,2331,2329,2330,2331,2332,2333,2334,2332,2333,2334,2335,2336,2337,2335,2336,2337,2338,2339,2340,2338,2339,2340,2341,2342,2343,2341,2342,2343,2344,2345,2346,2344,2345,2346,2347,2348,2349,2347,2348,2349,2350,2351,2352,2350,2351,2352,2353,2354,2355,2353,2354,2355,2356,2357,2358,2356,2357,2358,2359,2360,2361,2359,2360,2361,2362,2363,2364,2362,2363,2364,2365,2366,2367,2365,2366,2367,2368,2369,2370,2368,2369,2370,2371,2372,2373,2371,2372,2373,2374,2375,2376,2374,2375,2376,2377,2378,2379,2377,2378,2379,2380,2381,2382,2380,2381,2382,2383,2384,2385,2383,2384,2385,2386,2387,2388,2386,2387,2388,2389,2390,2391,2389,2390,2391,2392,2393,2394,2392,2393,2394,2395,2396,2397,2395,2396,2397,2398,2399,2400,2398,2399,2400,2401,2402,2403,2401,2402,2403,2404,2405,2406,2404,2405,2406,2407,2408,2409,2407,2408,2409,2410,2411,2412,2410,2411,2412,2413,2414,2415,2413,2414,2415,2416,2417,2418,2416,2417,2418,2419,2420,2421,2419,2420,2421,2422,2423,2424,2422,2423,2424,2425,2426,2427,2425,2426,2427,2428,2429,2430,2428,2429,2430],"nether_brick_wall":[2431,2432,2433,2431,2432,2433,0,2434,2435,0,2434,2435,2436,2437,2438,2436,2437,2438,2439,2440,2441,2439,2440,2441,2442,2443,2444,2442,2443,2444,2445,2446,2447,2445,2446,2447,2448,2449,2450,2448,2449,2450,2451,2452,2453,2451,2452,2453,2454,2455,2456,2454,2455,2456,2457,2458,2459,2457,2458,2459,2460,2461,2462,2460,2461,2462,2463,2464,2465,2463,2464,2465,2466,2467,2468,2466,2467,2468,2469,2470,2471,2469,2470,2471,2472,2473,2474,2472,2473,2474,2475,2476,2477,2475,2476,2477,2478,2479,2480,2478,2479,2480,2481,2482,2483,2481,2482,2483,2484,2485,2486,2484,2485,2486,2487,2488,2489,2487,2488,2489,2490,2491,2492,2490,2491,2492,2493,2494,2495,2493,2494,2495,2496,2497,2498,2496,2497,2498,2499,2500,2501,2499,2500,2501,2502,2503,2504,2502,2503,2504,2505,2506,2507,2505,2506,2507,2508,2509,2510,2508,2509,2510,2511,2512,2513,2511,2512,2513,2514,2515,2516,2514,2515,2516,2517,2518,2519,2517,2518,2519,2520,2521,2522,2520,2521,2522,2523,2524,2525,2523,2524,2525,2526,2527,2528,2526,2527,2528,2529,2530,2531,2529,2530,2531,2532,2533,2534,2532,2533,2534,2535,2536,2537,2535,2536,2537,2538,2539,2540,2538,2539,2540,2541,2542,2543,2541,2542,2543,2544,2545,2546,2544,2545,2546,2547,2548,2549,2547,2548,2549,2550,2551,2552,2550,2551,2552,2553,2554,2555,2553,2554,2555,2556,2557,2558,2556,2557,2558,2559,2560,2561,2559,2560,2561,2562,2563,2564,2562,2563,2564,2565,2566,2567,2565,2566,2567,2568,2569,2570,2568,2569,2570,2571,2572,2573,2571,2572,2573,2574,2575,2576,2574,2575,2576,2577,2578,2579,2577,2578,2579,2580,2581,2582,2580,2581,2582,2583,2584,2585,2583,2584,2585,2586,2587,2588,2586,2587,2588,2589,2590,2591,2589,2590,2591],"andesite_wall":[2592,2593,2594,2592,2593,2594,0,2595,2596,0,2595,2596,2597,2598,2599,2597,2598,2599,2600,2601,2602,2600,2601,2602,2603,2604,2605,2603,2604,2605,2606,2607,2608,2606,2607,2608,2609,2610,2611,2609,2610,2611,2612,2613,2614,2612,2613,2614,2615,2616,2617,2615,2616,2617,2618,2619,2620,2618,2619,2620,2621,2622,2623,2621,2622,2623,2624,2625,2626,2624,2625,2626,2627,2628,2629,2627,2628,2629,2630,2631,2632,2630,2631,2632,2633,2634,2635,2633,2634,2635,2636,2637,2638,2636,2637,2638,2639,2640,2641,2639,2640,2641,2642,2643,2644,2642,2643,2644,2645,2646,2647,2645,2646,2647,2648,2649,2650,2648,2649,2650,2651,2652,2653,2651,2652,2653,2654,2655,2656,2654,2655,2656,2657,2658,2659,2657,2658,2659,2660,2661,2662,2660,2661,2662,2663,2664,2665,2663,2664,2665,2666,2667,2668,2666,2667,2668,2669,2670,2671,2669,2670,2671,2672,2673,2674,2672,2673,2674,2675,2676,2677,2675,2676,2677,2678,2679,2680,2678,2679,2680,2681,2682,2683,2681,2682,2683,2684,2685,2686,2684,2685,2686,2687,2688,2689,2687,2688,2689,2690,2691,2692,2690,2691,2692,2693,2694,2695,2693,2694,2695,2696,2697,2698,2696,2697,2698,2699,2700,2701,2699,2700,2701,2702,2703,2704,2702,2703,2704,2705,2706,2707,2705,2706,2707,2708,2709,2710,2708,2709,2710,2711,2712,2713,2711,2712,2713,2714,2715,2716,2714,2715,2716,2717,2718,2719,2717,2718,2719,2720,2721,2722,2720,2721,2722,2723,2724,2725,2723,2724,2725,2726,2727,2728,2726,2727,2728,2729,2730,2731,2729,2730,2731,2732,2733,2734,2732,2733,2734,2735,2736,2737,2735,2736,2737,2738,2739,2740,2738,2739,2740,2741,2742,2743,2741,2742,2743,2744,2745,2746,2744,2745,2746,2747,2748,2749,2747,2748,2749,2750,2751,2752,2750,2751,2752],"red_nether_brick_wall":[2753,2754,2755,2753,2754,2755,0,2756,2757,0,2756,2757,2758,2759,2760,2758,2759,2760,2761,2762,2763,2761,2762,2763,2764,2765,2766,2764,2765,2766,2767,2768,2769,2767,2768,2769,2770,2771,2772,2770,2771,2772,2773,2774,2775,2773,2774,2775,2776,2777,2778,2776,2777,2778,2779,2780,2781,2779,2780,2781,2782,2783,2784,2782,2783,2784,2785,2786,2787,2785,2786,2787,2788,2789,2790,2788,2789,2790,2791,2792,2793,2791,2792,2793,2794,2795,2796,2794,2795,2796,2797,2798,2799,2797,2798,2799,2800,2801,2802,2800,2801,2802,2803,2804,2805,2803,2804,2805,2806,2807,2808,2806,2807,2808,2809,2810,2811,2809,2810,2811,2812,2813,2814,2812,2813,2814,2815,2816,2817,2815,2816,2817,2818,2819,2820,2818,2819,2820,2821,2822,2823,2821,2822,2823,2824,2825,2826,2824,2825,2826,2827,2828,2829,2827,2828,2829,2830,2831,2832,2830,2831,2832,2833,2834,2835,2833,2834,2835,2836,2837,2838,2836,2837,2838,2839,2840,2841,2839,2840,2841,2842,2843,2844,2842,2843,2844,2845,2846,2847,2845,2846,2847,2848,2849,2850,2848,2849,2850,2851,2852,2853,2851,2852,2853,2854,2855,2856,2854,2855,2856,2857,2858,2859,2857,2858,2859,2860,2861,2862,2860,2861,2862,2863,2864,2865,2863,2864,2865,2866,2867,2868,2866,2867,2868,2869,2870,2871,2869,2870,2871,2872,2873,2874,2872,2873,2874,2875,2876,2877,2875,2876,2877,2878,2879,2880,2878,2879,2880,2881,2882,2883,2881,2882,2883,2884,2885,2886,2884,2885,2886,2887,2888,2889,2887,2888,2889,2890,2891,2892,2890,2891,2892,2893,2894,2895,2893,2894,2895,2896,2897,2898,2896,2897,2898,2899,2900,2901,2899,2900,2901,2902,2903,2904,2902,2903,2904,2905,2906,2907,2905,2906,2907,2908,2909,2910,2908,2909,2910,2911,2912,2913,2911,2912,2913],"sandstone_wall":[2914,2915,2916,2914,2915,2916,0,2917,2918,0,2917,2918,2919,2920,2921,2919,2920,2921,2922,2923,2924,2922,2923,2924,2925,2926,2927,2925,2926,2927,2928,2929,2930,2928,2929,2930,2931,2932,2933,2931,2932,2933,2934,2935,2936,2934,2935,2936,2937,2938,2939,2937,2938,2939,2940,2941,2942,2940,2941,2942,2943,2944,2945,2943,2944,2945,2946,2947,2948,2946,2947,2948,2949,2950,2951,2949,2950,2951,2952,2953,2954,2952,2953,2954,2955,2956,2957,2955,2956,2957,2958,2959,2960,2958,2959,2960,2961,2962,2963,2961,2962,2963,2964,2965,2966,2964,2965,2966,2967,2968,2969,2967,2968,2969,2970,2971,2972,2970,2971,2972,2973,2974,2975,2973,2974,2975,2976,2977,2978,2976,2977,2978,2979,2980,2981,2979,2980,2981,2982,2983,2984,2982,2983,2984,2985,2986,2987,2985,2986,2987,2988,2989,2990,2988,2989,2990,2991,2992,2993,2991,2992,2993,2994,2995,2996,2994,2995,2996,2997,2998,2999,2997,2998,2999,3000,3001,3002,3000,3001,3002,3003,3004,3005,3003,3004,3005,3006,3007,3008,3006,3007,3008,3009,3010,3011,3009,3010,3011,3012,3013,3014,3012,3013,3014,3015,3016,3017,3015,3016,3017,3018,3019,3020,3018,3019,3020,3021,3022,3023,3021,3022,3023,3024,3025,3026,3024,3025,3026,3027,3028,3029,3027,3028,3029,3030,3031,3032,3030,3031,3032,3033,3034,3035,3033,3034,3035,3036,3037,3038,3036,3037,3038,3039,3040,3041,3039,3040,3041,3042,3043,3044,3042,3043,3044,3045,3046,3047,3045,3046,3047,3048,3049,3050,3048,3049,3050,3051,3052,3053,3051,3052,3053,3054,3055,3056,3054,3055,3056,3057,3058,3059,3057,3058,3059,3060,3061,3062,3060,3061,3062,3063,3064,3065,3063,3064,3065,3066,3067,3068,3066,3067,3068,3069,3070,3071,3069,3070,3071,3072,3073,3074,3072,3073,3074],"end_stone_brick_wall":[3075,3076,3077,3075,3076,3077,0,3078,3079,0,3078,3079,3080,3081,3082,3080,3081,3082,3083,3084,3085,3083,3084,3085,3086,3087,3088,3086,3087,3088,3089,3090,3091,3089,3090,3091,3092,3093,3094,3092,3093,3094,3095,3096,3097,3095,3096,3097,3098,3099,3100,3098,3099,3100,3101,3102,3103,3101,3102,3103,3104,3105,3106,3104,3105,3106,3107,3108,3109,3107,3108,3109,3110,3111,3112,3110,3111,3112,3113,3114,3115,3113,3114,3115,3116,3117,3118,3116,3117,3118,3119,3120,3121,3119,3120,3121,3122,3123,3124,3122,3123,3124,3125,3126,3127,3125,3126,3127,3128,3129,3130,3128,3129,3130,3131,3132,3133,3131,3132,3133,3134,3135,3136,3134,3135,3136,3137,3138,3139,3137,3138,3139,3140,3141,3142,3140,3141,3142,3143,3144,3145,3143,3144,3145,3146,3147,3148,3146,3147,3148,3149,3150,3151,3149,3150,3151,3152,3153,3154,3152,3153,3154,3155,3156,3157,3155,3156,3157,3158,3159,3160,3158,3159,3160,3161,3162,3163,3161,3162,3163,3164,3165,3166,3164,3165,3166,3167,3168,3169,3167,3168,3169,3170,3171,3172,3170,3171,3172,3173,3174,3175,3173,3174,3175,3176,3177,3178,3176,3177,3178,3179,3180,3181,3179,3180,3181,3182,3183,3184,3182,3183,3184,3185,3186,3187,3185,3186,3187,3188,3189,3190,3188,3189,3190,3191,3192,3193,3191,3192,3193,3194,3195,3196,3194,3195,3196,3197,3198,3199,3197,3198,3199,3200,3201,3202,3200,3201,3202,3203,3204,3205,3203,3204,3205,3206,3207,3208,3206,3207,3208,3209,3210,3211,3209,3210,3211,3212,3213,3214,3212,3213,3214,3215,3216,3217,3215,3216,3217,3218,3219,3220,3218,3219,3220,3221,3222,3223,3221,3222,3223,3224,3225,3226,3224,3225,3226,3227,3228,3229,3227,3228,3229,3230,3231,3232,3230,3231,3232,3233,3234,3235,3233,3234,3235],"diorite_wall":[3236,3237,3238,3236,3237,3238,0,3239,3240,0,3239,3240,3241,3242,3243,3241,3242,3243,3244,3245,3246,3244,3245,3246,3247,3248,3249,3247,3248,3249,3250,3251,3252,3250,3251,3252,3253,3254,3255,3253,3254,3255,3256,3257,3258,3256,3257,3258,3259,3260,3261,3259,3260,3261,3262,3263,3264,3262,3263,3264,3265,3266,3267,3265,3266,3267,3268,3269,3270,3268,3269,3270,3271,3272,3273,3271,3272,3273,3274,3275,3276,3274,3275,3276,3277,3278,3279,3277,3278,3279,3280,3281,3282,3280,3281,3282,3283,3284,3285,3283,3284,3285,3286,3287,3288,3286,3287,3288,3289,3290,3291,3289,3290,3291,3292,3293,3294,3292,3293,3294,3295,3296,3297,3295,3296,3297,3298,3299,3300,3298,3299,3300,3301,3302,3303,3301,3302,3303,3304,3305,3306,3304,3305,3306,3307,3308,3309,3307,3308,3309,3310,3311,3312,3310,3311,3312,3313,3314,3315,3313,3314,3315,3316,3317,3318,3316,3317,3318,3319,3320,3321,3319,3320,3321,3322,3323,3324,3322,3323,3324,3325,3326,3327,3325,3326,3327,3328,3329,3330,3328,3329,3330,3331,3332,3333,3331,3332,3333,3334,3335,3336,3334,3335,3336,3337,3338,3339,3337,3338,3339,3340,3341,3342,3340,3341,3342,3343,3344,3345,3343,3344,3345,3346,3347,3348,3346,3347,3348,3349,3350,3351,3349,3350,3351,3352,3353,3354,3352,3353,3354,3355,3356,3357,3355,3356,3357,3358,3359,3360,3358,3359,3360,3361,3362,3363,3361,3362,3363,3364,3365,3366,3364,3365,3366,3367,3368,3369,3367,3368,3369,3370,3371,3372,3370,3371,3372,3373,3374,3375,3373,3374,3375,3376,3377,3378,3376,3377,3378,3379,3380,3381,3379,3380,3381,3382,3383,3384,3382,3383,3384,3385,3386,3387,3385,3386,3387,3388,3389,3390,3388,3389,3390,3391,3392,3393,3391,3392,3393,3394,3395,3396,3394,3395,3396],"scaffolding":3397,"loom":1,"barrel":1,"smoker":1,"blast_furnace":1,"cartography_table":1,"fletching_table":1,"grindstone":[3398,3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409],"lectern":3410,"smithing_table":1,"stonecutter":3411,"bell":[3412,3412,3412,3412,3413,3413,3413,3413,3414,3414,3414,3414,3414,3414,3414,3414,3415,3415,3416,3416,3417,3417,3418,3418,3419,3419,3419,3419,3420,3420,3420,3420],"lantern":[3421,3421,3422,3422],"soul_lantern":[3421,3421,3422,3422],"copper_lantern":[3421,3421,3422,3422],"exposed_copper_lantern":[3421,3421,3422,3422],"weathered_copper_lantern":[3421,3421,3422,3422],"oxidized_copper_lantern":[3421,3421,3422,3422],"waxed_copper_lantern":[3421,3421,3422,3422],"waxed_exposed_copper_lantern":[3421,3421,3422,3422],"waxed_weathered_copper_lantern":[3421,3421,3422,3422],"waxed_oxidized_copper_lantern":[3421,3421,3422,3422],"campfire":3423,"soul_campfire":3423,"sweet_berry_bush":0,"warped_stem":1,"stripped_warped_stem":1,"warped_hyphae":1,"stripped_warped_hyphae":1,"warped_nylium":1,"warped_fungus":0,"warped_wart_block":1,"warped_roots":0,"nether_sprouts":0,"crimson_stem":1,"stripped_crimson_stem":1,"crimson_hyphae":1,"stripped_crimson_hyphae":1,"crimson_nylium":1,"crimson_fungus":0,"shroomlight":1,"weeping_vines":0,"weeping_vines_plant":0,"twisting_vines":0,"twisting_vines_plant":0,"crimson_roots":0,"crimson_planks":1,"warped_planks":1,"crimson_slab":[273,273,274,274,1,1],"warped_slab":[273,273,274,274,1,1],"crimson_pressure_plate":0,"warped_pressure_plate":0,"crimson_fence":[3424,3425,3424,3425,3426,3427,3426,3427,3428,3429,3428,3429,3430,3431,3430,3431,3432,3433,3432,3433,3434,3435,3434,3435,3436,3437,3436,3437,3438,3439,3438,3439],"warped_fence":[3440,3441,3440,3441,3442,3443,3442,3443,3444,3445,3444,3445,3446,3447,3446,3447,3448,3449,3448,3449,3450,3451,3450,3451,3452,3453,3452,3453,3454,3455,3454,3455],"crimson_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"warped_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"crimson_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"warped_fence_gate":[0,0,270,270,0,0,270,270,0,0,270,270,0,0,270,270,0,0,271,271,0,0,271,271,0,0,271,271,0,0,271,271],"crimson_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"warped_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"crimson_button":0,"warped_button":0,"crimson_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"warped_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"crimson_sign":0,"warped_sign":0,"crimson_wall_sign":0,"warped_wall_sign":0,"structure_block":1,"jigsaw":1,"test_block":1,"test_instance_block":1,"composter":3456,"target":1,"bee_nest":1,"beehive":1,"honey_block":3457,"honeycomb_block":1,"netherite_block":1,"ancient_debris":1,"crying_obsidian":1,"respawn_anchor":1,"potted_crimson_fungus":793,"potted_warped_fungus":793,"potted_crimson_roots":793,"potted_warped_roots":793,"lodestone":1,"blackstone":1,"blackstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"blackstone_wall":[3458,3459,3460,3458,3459,3460,0,3461,3462,0,3461,3462,3463,3464,3465,3463,3464,3465,3466,3467,3468,3466,3467,3468,3469,3470,3471,3469,3470,3471,3472,3473,3474,3472,3473,3474,3475,3476,3477,3475,3476,3477,3478,3479,3480,3478,3479,3480,3481,3482,3483,3481,3482,3483,3484,3485,3486,3484,3485,3486,3487,3488,3489,3487,3488,3489,3490,3491,3492,3490,3491,3492,3493,3494,3495,3493,3494,3495,3496,3497,3498,3496,3497,3498,3499,3500,3501,3499,3500,3501,3502,3503,3504,3502,3503,3504,3505,3506,3507,3505,3506,3507,3508,3509,3510,3508,3509,3510,3511,3512,3513,3511,3512,3513,3514,3515,3516,3514,3515,3516,3517,3518,3519,3517,3518,3519,3520,3521,3522,3520,3521,3522,3523,3524,3525,3523,3524,3525,3526,3527,3528,3526,3527,3528,3529,3530,3531,3529,3530,3531,3532,3533,3534,3532,3533,3534,3535,3536,3537,3535,3536,3537,3538,3539,3540,3538,3539,3540,3541,3542,3543,3541,3542,3543,3544,3545,3546,3544,3545,3546,3547,3548,3549,3547,3548,3549,3550,3551,3552,3550,3551,3552,3553,3554,3555,3553,3554,3555,3556,3557,3558,3556,3557,3558,3559,3560,3561,3559,3560,3561,3562,3563,3564,3562,3563,3564,3565,3566,3567,3565,3566,3567,3568,3569,3570,3568,3569,3570,3571,3572,3573,3571,3572,3573,3574,3575,3576,3574,3575,3576,3577,3578,3579,3577,3578,3579,3580,3581,3582,3580,3581,3582,3583,3584,3585,3583,3584,3585,3586,3587,3588,3586,3587,3588,3589,3590,3591,3589,3590,3591,3592,3593,3594,3592,3593,3594,3595,3596,3597,3595,3596,3597,3598,3599,3600,3598,3599,3600,3601,3602,3603,3601,3602,3603,3604,3605,3606,3604,3605,3606,3607,3608,3609,3607,3608,3609,3610,3611,3612,3610,3611,3612,3613,3614,3615,3613,3614,3615,3616,3617,3618,3616,3617,3618],"blackstone_slab":[273,273,274,274,1,1],"polished_blackstone":1,"polished_blackstone_bricks":1,"cracked_polished_blackstone_bricks":1,"chiseled_polished_blackstone":1,"polished_blackstone_brick_slab":[273,273,274,274,1,1],"polished_blackstone_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_blackstone_brick_wall":[3619,3620,3621,3619,3620,3621,0,3622,3623,0,3622,3623,3624,3625,3626,3624,3625,3626,3627,3628,3629,3627,3628,3629,3630,3631,3632,3630,3631,3632,3633,3634,3635,3633,3634,3635,3636,3637,3638,3636,3637,3638,3639,3640,3641,3639,3640,3641,3642,3643,3644,3642,3643,3644,3645,3646,3647,3645,3646,3647,3648,3649,3650,3648,3649,3650,3651,3652,3653,3651,3652,3653,3654,3655,3656,3654,3655,3656,3657,3658,3659,3657,3658,3659,3660,3661,3662,3660,3661,3662,3663,3664,3665,3663,3664,3665,3666,3667,3668,3666,3667,3668,3669,3670,3671,3669,3670,3671,3672,3673,3674,3672,3673,3674,3675,3676,3677,3675,3676,3677,3678,3679,3680,3678,3679,3680,3681,3682,3683,3681,3682,3683,3684,3685,3686,3684,3685,3686,3687,3688,3689,3687,3688,3689,3690,3691,3692,3690,3691,3692,3693,3694,3695,3693,3694,3695,3696,3697,3698,3696,3697,3698,3699,3700,3701,3699,3700,3701,3702,3703,3704,3702,3703,3704,3705,3706,3707,3705,3706,3707,3708,3709,3710,3708,3709,3710,3711,3712,3713,3711,3712,3713,3714,3715,3716,3714,3715,3716,3717,3718,3719,3717,3718,3719,3720,3721,3722,3720,3721,3722,3723,3724,3725,3723,3724,3725,3726,3727,3728,3726,3727,3728,3729,3730,3731,3729,3730,3731,3732,3733,3734,3732,3733,3734,3735,3736,3737,3735,3736,3737,3738,3739,3740,3738,3739,3740,3741,3742,3743,3741,3742,3743,3744,3745,3746,3744,3745,3746,3747,3748,3749,3747,3748,3749,3750,3751,3752,3750,3751,3752,3753,3754,3755,3753,3754,3755,3756,3757,3758,3756,3757,3758,3759,3760,3761,3759,3760,3761,3762,3763,3764,3762,3763,3764,3765,3766,3767,3765,3766,3767,3768,3769,3770,3768,3769,3770,3771,3772,3773,3771,3772,3773,3774,3775,3776,3774,3775,3776,3777,3778,3779,3777,3778,3779],"gilded_blackstone":1,"polished_blackstone_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_blackstone_slab":[273,273,274,274,1,1],"polished_blackstone_pressure_plate":0,"polished_blackstone_button":0,"polished_blackstone_wall":[3780,3781,3782,3780,3781,3782,0,3783,3784,0,3783,3784,3785,3786,3787,3785,3786,3787,3788,3789,3790,3788,3789,3790,3791,3792,3793,3791,3792,3793,3794,3795,3796,3794,3795,3796,3797,3798,3799,3797,3798,3799,3800,3801,3802,3800,3801,3802,3803,3804,3805,3803,3804,3805,3806,3807,3808,3806,3807,3808,3809,3810,3811,3809,3810,3811,3812,3813,3814,3812,3813,3814,3815,3816,3817,3815,3816,3817,3818,3819,3820,3818,3819,3820,3821,3822,3823,3821,3822,3823,3824,3825,3826,3824,3825,3826,3827,3828,3829,3827,3828,3829,3830,3831,3832,3830,3831,3832,3833,3834,3835,3833,3834,3835,3836,3837,3838,3836,3837,3838,3839,3840,3841,3839,3840,3841,3842,3843,3844,3842,3843,3844,3845,3846,3847,3845,3846,3847,3848,3849,3850,3848,3849,3850,3851,3852,3853,3851,3852,3853,3854,3855,3856,3854,3855,3856,3857,3858,3859,3857,3858,3859,3860,3861,3862,3860,3861,3862,3863,3864,3865,3863,3864,3865,3866,3867,3868,3866,3867,3868,3869,3870,3871,3869,3870,3871,3872,3873,3874,3872,3873,3874,3875,3876,3877,3875,3876,3877,3878,3879,3880,3878,3879,3880,3881,3882,3883,3881,3882,3883,3884,3885,3886,3884,3885,3886,3887,3888,3889,3887,3888,3889,3890,3891,3892,3890,3891,3892,3893,3894,3895,3893,3894,3895,3896,3897,3898,3896,3897,3898,3899,3900,3901,3899,3900,3901,3902,3903,3904,3902,3903,3904,3905,3906,3907,3905,3906,3907,3908,3909,3910,3908,3909,3910,3911,3912,3913,3911,3912,3913,3914,3915,3916,3914,3915,3916,3917,3918,3919,3917,3918,3919,3920,3921,3922,3920,3921,3922,3923,3924,3925,3923,3924,3925,3926,3927,3928,3926,3927,3928,3929,3930,3931,3929,3930,3931,3932,3933,3934,3932,3933,3934,3935,3936,3937,3935,3936,3937,3938,3939,3940,3938,3939,3940],"chiseled_nether_bricks":1,"cracked_nether_bricks":1,"quartz_bricks":1,"candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"white_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"orange_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"magenta_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"light_blue_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"yellow_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"lime_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"pink_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"gray_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"light_gray_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"cyan_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"purple_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"blue_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"brown_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"green_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"red_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"black_candle":[3941,3941,3941,3941,3942,3942,3942,3942,3943,3943,3943,3943,3944,3944,3944,3944],"candle_cake":3945,"white_candle_cake":3945,"orange_candle_cake":3945,"magenta_candle_cake":3945,"light_blue_candle_cake":3945,"yellow_candle_cake":3945,"lime_candle_cake":3945,"pink_candle_cake":3945,"gray_candle_cake":3945,"light_gray_candle_cake":3945,"cyan_candle_cake":3945,"purple_candle_cake":3945,"blue_candle_cake":3945,"brown_candle_cake":3945,"green_candle_cake":3945,"red_candle_cake":3945,"black_candle_cake":3945,"amethyst_block":1,"budding_amethyst":1,"amethyst_cluster":[3946,3946,3947,3947,3948,3948,3949,3949,3950,3950,3951,3951],"large_amethyst_bud":[3952,3952,3953,3953,3954,3954,3955,3955,3956,3956,3957,3957],"medium_amethyst_bud":[3958,3958,3959,3959,3960,3960,3961,3961,3962,3962,3963,3963],"small_amethyst_bud":[3964,3964,3965,3965,3966,3966,3967,3967,3968,3968,3969,3969],"tuff":1,"tuff_slab":[273,273,274,274,1,1],"tuff_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"tuff_wall":[3970,3971,3972,3970,3971,3972,0,3973,3974,0,3973,3974,3975,3976,3977,3975,3976,3977,3978,3979,3980,3978,3979,3980,3981,3982,3983,3981,3982,3983,3984,3985,3986,3984,3985,3986,3987,3988,3989,3987,3988,3989,3990,3991,3992,3990,3991,3992,3993,3994,3995,3993,3994,3995,3996,3997,3998,3996,3997,3998,3999,4000,4001,3999,4000,4001,4002,4003,4004,4002,4003,4004,4005,4006,4007,4005,4006,4007,4008,4009,4010,4008,4009,4010,4011,4012,4013,4011,4012,4013,4014,4015,4016,4014,4015,4016,4017,4018,4019,4017,4018,4019,4020,4021,4022,4020,4021,4022,4023,4024,4025,4023,4024,4025,4026,4027,4028,4026,4027,4028,4029,4030,4031,4029,4030,4031,4032,4033,4034,4032,4033,4034,4035,4036,4037,4035,4036,4037,4038,4039,4040,4038,4039,4040,4041,4042,4043,4041,4042,4043,4044,4045,4046,4044,4045,4046,4047,4048,4049,4047,4048,4049,4050,4051,4052,4050,4051,4052,4053,4054,4055,4053,4054,4055,4056,4057,4058,4056,4057,4058,4059,4060,4061,4059,4060,4061,4062,4063,4064,4062,4063,4064,4065,4066,4067,4065,4066,4067,4068,4069,4070,4068,4069,4070,4071,4072,4073,4071,4072,4073,4074,4075,4076,4074,4075,4076,4077,4078,4079,4077,4078,4079,4080,4081,4082,4080,4081,4082,4083,4084,4085,4083,4084,4085,4086,4087,4088,4086,4087,4088,4089,4090,4091,4089,4090,4091,4092,4093,4094,4092,4093,4094,4095,4096,4097,4095,4096,4097,4098,4099,4100,4098,4099,4100,4101,4102,4103,4101,4102,4103,4104,4105,4106,4104,4105,4106,4107,4108,4109,4107,4108,4109,4110,4111,4112,4110,4111,4112,4113,4114,4115,4113,4114,4115,4116,4117,4118,4116,4117,4118,4119,4120,4121,4119,4120,4121,4122,4123,4124,4122,4123,4124,4125,4126,4127,4125,4126,4127,4128,4129,4130,4128,4129,4130],"polished_tuff":1,"polished_tuff_slab":[273,273,274,274,1,1],"polished_tuff_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_tuff_wall":[4131,4132,4133,4131,4132,4133,0,4134,4135,0,4134,4135,4136,4137,4138,4136,4137,4138,4139,4140,4141,4139,4140,4141,4142,4143,4144,4142,4143,4144,4145,4146,4147,4145,4146,4147,4148,4149,4150,4148,4149,4150,4151,4152,4153,4151,4152,4153,4154,4155,4156,4154,4155,4156,4157,4158,4159,4157,4158,4159,4160,4161,4162,4160,4161,4162,4163,4164,4165,4163,4164,4165,4166,4167,4168,4166,4167,4168,4169,4170,4171,4169,4170,4171,4172,4173,4174,4172,4173,4174,4175,4176,4177,4175,4176,4177,4178,4179,4180,4178,4179,4180,4181,4182,4183,4181,4182,4183,4184,4185,4186,4184,4185,4186,4187,4188,4189,4187,4188,4189,4190,4191,4192,4190,4191,4192,4193,4194,4195,4193,4194,4195,4196,4197,4198,4196,4197,4198,4199,4200,4201,4199,4200,4201,4202,4203,4204,4202,4203,4204,4205,4206,4207,4205,4206,4207,4208,4209,4210,4208,4209,4210,4211,4212,4213,4211,4212,4213,4214,4215,4216,4214,4215,4216,4217,4218,4219,4217,4218,4219,4220,4221,4222,4220,4221,4222,4223,4224,4225,4223,4224,4225,4226,4227,4228,4226,4227,4228,4229,4230,4231,4229,4230,4231,4232,4233,4234,4232,4233,4234,4235,4236,4237,4235,4236,4237,4238,4239,4240,4238,4239,4240,4241,4242,4243,4241,4242,4243,4244,4245,4246,4244,4245,4246,4247,4248,4249,4247,4248,4249,4250,4251,4252,4250,4251,4252,4253,4254,4255,4253,4254,4255,4256,4257,4258,4256,4257,4258,4259,4260,4261,4259,4260,4261,4262,4263,4264,4262,4263,4264,4265,4266,4267,4265,4266,4267,4268,4269,4270,4268,4269,4270,4271,4272,4273,4271,4272,4273,4274,4275,4276,4274,4275,4276,4277,4278,4279,4277,4278,4279,4280,4281,4282,4280,4281,4282,4283,4284,4285,4283,4284,4285,4286,4287,4288,4286,4287,4288,4289,4290,4291,4289,4290,4291],"chiseled_tuff":1,"tuff_bricks":1,"tuff_brick_slab":[273,273,274,274,1,1],"tuff_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"tuff_brick_wall":[4292,4293,4294,4292,4293,4294,0,4295,4296,0,4295,4296,4297,4298,4299,4297,4298,4299,4300,4301,4302,4300,4301,4302,4303,4304,4305,4303,4304,4305,4306,4307,4308,4306,4307,4308,4309,4310,4311,4309,4310,4311,4312,4313,4314,4312,4313,4314,4315,4316,4317,4315,4316,4317,4318,4319,4320,4318,4319,4320,4321,4322,4323,4321,4322,4323,4324,4325,4326,4324,4325,4326,4327,4328,4329,4327,4328,4329,4330,4331,4332,4330,4331,4332,4333,4334,4335,4333,4334,4335,4336,4337,4338,4336,4337,4338,4339,4340,4341,4339,4340,4341,4342,4343,4344,4342,4343,4344,4345,4346,4347,4345,4346,4347,4348,4349,4350,4348,4349,4350,4351,4352,4353,4351,4352,4353,4354,4355,4356,4354,4355,4356,4357,4358,4359,4357,4358,4359,4360,4361,4362,4360,4361,4362,4363,4364,4365,4363,4364,4365,4366,4367,4368,4366,4367,4368,4369,4370,4371,4369,4370,4371,4372,4373,4374,4372,4373,4374,4375,4376,4377,4375,4376,4377,4378,4379,4380,4378,4379,4380,4381,4382,4383,4381,4382,4383,4384,4385,4386,4384,4385,4386,4387,4388,4389,4387,4388,4389,4390,4391,4392,4390,4391,4392,4393,4394,4395,4393,4394,4395,4396,4397,4398,4396,4397,4398,4399,4400,4401,4399,4400,4401,4402,4403,4404,4402,4403,4404,4405,4406,4407,4405,4406,4407,4408,4409,4410,4408,4409,4410,4411,4412,4413,4411,4412,4413,4414,4415,4416,4414,4415,4416,4417,4418,4419,4417,4418,4419,4420,4421,4422,4420,4421,4422,4423,4424,4425,4423,4424,4425,4426,4427,4428,4426,4427,4428,4429,4430,4431,4429,4430,4431,4432,4433,4434,4432,4433,4434,4435,4436,4437,4435,4436,4437,4438,4439,4440,4438,4439,4440,4441,4442,4443,4441,4442,4443,4444,4445,4446,4444,4445,4446,4447,4448,4449,4447,4448,4449,4450,4451,4452,4450,4451,4452],"chiseled_tuff_bricks":1,"calcite":1,"tinted_glass":1,"powder_snow":0,"sculk_sensor":4453,"calibrated_sculk_sensor":4453,"sculk":1,"sculk_vein":0,"sculk_catalyst":1,"sculk_shrieker":4454,"copper_block":1,"exposed_copper":1,"weathered_copper":1,"oxidized_copper":1,"copper_ore":1,"deepslate_copper_ore":1,"oxidized_cut_copper":1,"weathered_cut_copper":1,"exposed_cut_copper":1,"cut_copper":1,"oxidized_chiseled_copper":1,"weathered_chiseled_copper":1,"exposed_chiseled_copper":1,"chiseled_copper":1,"waxed_oxidized_chiseled_copper":1,"waxed_weathered_chiseled_copper":1,"waxed_exposed_chiseled_copper":1,"waxed_chiseled_copper":1,"oxidized_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"weathered_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"exposed_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"oxidized_cut_copper_slab":[273,273,274,274,1,1],"weathered_cut_copper_slab":[273,273,274,274,1,1],"exposed_cut_copper_slab":[273,273,274,274,1,1],"cut_copper_slab":[273,273,274,274,1,1],"waxed_copper_block":1,"waxed_weathered_copper":1,"waxed_exposed_copper":1,"waxed_oxidized_copper":1,"waxed_oxidized_cut_copper":1,"waxed_weathered_cut_copper":1,"waxed_exposed_cut_copper":1,"waxed_cut_copper":1,"waxed_oxidized_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"waxed_weathered_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"waxed_exposed_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"waxed_cut_copper_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"waxed_oxidized_cut_copper_slab":[273,273,274,274,1,1],"waxed_weathered_cut_copper_slab":[273,273,274,274,1,1],"waxed_exposed_cut_copper_slab":[273,273,274,274,1,1],"waxed_cut_copper_slab":[273,273,274,274,1,1],"copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"exposed_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"oxidized_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"weathered_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"waxed_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"waxed_exposed_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"waxed_oxidized_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"waxed_weathered_copper_door":[58,58,59,59,60,60,59,59,58,58,59,59,60,60,59,59,60,60,61,61,58,58,61,61,60,60,61,61,58,58,61,61,59,59,60,60,61,61,60,60,59,59,60,60,61,61,60,60,61,61,58,58,59,59,58,58,61,61,58,58,59,59,58,58],"copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"exposed_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"oxidized_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"weathered_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"waxed_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"waxed_exposed_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"waxed_oxidized_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"waxed_weathered_copper_trapdoor":[101,101,101,101,102,102,102,102,101,101,101,101,103,103,103,103,104,104,104,104,102,102,102,102,104,104,104,104,103,103,103,103,105,105,105,105,102,102,102,102,105,105,105,105,103,103,103,103,106,106,106,106,102,102,102,102,106,106,106,106,103,103,103,103],"copper_grate":1,"exposed_copper_grate":1,"weathered_copper_grate":1,"oxidized_copper_grate":1,"waxed_copper_grate":1,"waxed_exposed_copper_grate":1,"waxed_weathered_copper_grate":1,"waxed_oxidized_copper_grate":1,"copper_bulb":1,"exposed_copper_bulb":1,"weathered_copper_bulb":1,"oxidized_copper_bulb":1,"waxed_copper_bulb":1,"waxed_exposed_copper_bulb":1,"waxed_weathered_copper_bulb":1,"waxed_oxidized_copper_bulb":1,"copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"exposed_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"weathered_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"oxidized_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"waxed_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"waxed_exposed_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"waxed_weathered_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"waxed_oxidized_copper_chest":[52,52,53,53,54,54,52,52,54,54,53,53,52,52,55,55,56,56,52,52,56,56,55,55],"copper_golem_statue":4455,"exposed_copper_golem_statue":4455,"weathered_copper_golem_statue":4455,"oxidized_copper_golem_statue":4455,"waxed_copper_golem_statue":4455,"waxed_exposed_copper_golem_statue":4455,"waxed_weathered_copper_golem_statue":4455,"waxed_oxidized_copper_golem_statue":4455,"lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"exposed_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"weathered_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"oxidized_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"waxed_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"waxed_exposed_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"waxed_weathered_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"waxed_oxidized_lightning_rod":[1213,1213,1213,1213,1214,1214,1214,1214,1213,1213,1213,1213,1214,1214,1214,1214,1215,1215,1215,1215,1215,1215,1215,1215],"pointed_dripstone":[4456,4457,4458,4459,4460,4461,4462,4463,4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475],"dripstone_block":1,"cave_vines":0,"cave_vines_plant":0,"spore_blossom":0,"azalea":4476,"flowering_azalea":4476,"moss_carpet":1068,"pink_petals":0,"wildflowers":0,"leaf_litter":0,"moss_block":1,"big_dripleaf":[4477,4477,4478,4478,4479,4479,0,0,4477,4477,4478,4478,4479,4479,0,0,4477,4477,4478,4478,4479,4479,0,0,4477,4477,4478,4478,4479,4479,0,0],"big_dripleaf_stem":0,"small_dripleaf":0,"hanging_roots":0,"rooted_dirt":1,"mud":4480,"deepslate":1,"cobbled_deepslate":1,"cobbled_deepslate_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"cobbled_deepslate_slab":[273,273,274,274,1,1],"cobbled_deepslate_wall":[4481,4482,4483,4481,4482,4483,0,4484,4485,0,4484,4485,4486,4487,4488,4486,4487,4488,4489,4490,4491,4489,4490,4491,4492,4493,4494,4492,4493,4494,4495,4496,4497,4495,4496,4497,4498,4499,4500,4498,4499,4500,4501,4502,4503,4501,4502,4503,4504,4505,4506,4504,4505,4506,4507,4508,4509,4507,4508,4509,4510,4511,4512,4510,4511,4512,4513,4514,4515,4513,4514,4515,4516,4517,4518,4516,4517,4518,4519,4520,4521,4519,4520,4521,4522,4523,4524,4522,4523,4524,4525,4526,4527,4525,4526,4527,4528,4529,4530,4528,4529,4530,4531,4532,4533,4531,4532,4533,4534,4535,4536,4534,4535,4536,4537,4538,4539,4537,4538,4539,4540,4541,4542,4540,4541,4542,4543,4544,4545,4543,4544,4545,4546,4547,4548,4546,4547,4548,4549,4550,4551,4549,4550,4551,4552,4553,4554,4552,4553,4554,4555,4556,4557,4555,4556,4557,4558,4559,4560,4558,4559,4560,4561,4562,4563,4561,4562,4563,4564,4565,4566,4564,4565,4566,4567,4568,4569,4567,4568,4569,4570,4571,4572,4570,4571,4572,4573,4574,4575,4573,4574,4575,4576,4577,4578,4576,4577,4578,4579,4580,4581,4579,4580,4581,4582,4583,4584,4582,4583,4584,4585,4586,4587,4585,4586,4587,4588,4589,4590,4588,4589,4590,4591,4592,4593,4591,4592,4593,4594,4595,4596,4594,4595,4596,4597,4598,4599,4597,4598,4599,4600,4601,4602,4600,4601,4602,4603,4604,4605,4603,4604,4605,4606,4607,4608,4606,4607,4608,4609,4610,4611,4609,4610,4611,4612,4613,4614,4612,4613,4614,4615,4616,4617,4615,4616,4617,4618,4619,4620,4618,4619,4620,4621,4622,4623,4621,4622,4623,4624,4625,4626,4624,4625,4626,4627,4628,4629,4627,4628,4629,4630,4631,4632,4630,4631,4632,4633,4634,4635,4633,4634,4635,4636,4637,4638,4636,4637,4638,4639,4640,4641,4639,4640,4641],"polished_deepslate":1,"polished_deepslate_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"polished_deepslate_slab":[273,273,274,274,1,1],"polished_deepslate_wall":[4642,4643,4644,4642,4643,4644,0,4645,4646,0,4645,4646,4647,4648,4649,4647,4648,4649,4650,4651,4652,4650,4651,4652,4653,4654,4655,4653,4654,4655,4656,4657,4658,4656,4657,4658,4659,4660,4661,4659,4660,4661,4662,4663,4664,4662,4663,4664,4665,4666,4667,4665,4666,4667,4668,4669,4670,4668,4669,4670,4671,4672,4673,4671,4672,4673,4674,4675,4676,4674,4675,4676,4677,4678,4679,4677,4678,4679,4680,4681,4682,4680,4681,4682,4683,4684,4685,4683,4684,4685,4686,4687,4688,4686,4687,4688,4689,4690,4691,4689,4690,4691,4692,4693,4694,4692,4693,4694,4695,4696,4697,4695,4696,4697,4698,4699,4700,4698,4699,4700,4701,4702,4703,4701,4702,4703,4704,4705,4706,4704,4705,4706,4707,4708,4709,4707,4708,4709,4710,4711,4712,4710,4711,4712,4713,4714,4715,4713,4714,4715,4716,4717,4718,4716,4717,4718,4719,4720,4721,4719,4720,4721,4722,4723,4724,4722,4723,4724,4725,4726,4727,4725,4726,4727,4728,4729,4730,4728,4729,4730,4731,4732,4733,4731,4732,4733,4734,4735,4736,4734,4735,4736,4737,4738,4739,4737,4738,4739,4740,4741,4742,4740,4741,4742,4743,4744,4745,4743,4744,4745,4746,4747,4748,4746,4747,4748,4749,4750,4751,4749,4750,4751,4752,4753,4754,4752,4753,4754,4755,4756,4757,4755,4756,4757,4758,4759,4760,4758,4759,4760,4761,4762,4763,4761,4762,4763,4764,4765,4766,4764,4765,4766,4767,4768,4769,4767,4768,4769,4770,4771,4772,4770,4771,4772,4773,4774,4775,4773,4774,4775,4776,4777,4778,4776,4777,4778,4779,4780,4781,4779,4780,4781,4782,4783,4784,4782,4783,4784,4785,4786,4787,4785,4786,4787,4788,4789,4790,4788,4789,4790,4791,4792,4793,4791,4792,4793,4794,4795,4796,4794,4795,4796,4797,4798,4799,4797,4798,4799,4800,4801,4802,4800,4801,4802],"deepslate_tiles":1,"deepslate_tile_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"deepslate_tile_slab":[273,273,274,274,1,1],"deepslate_tile_wall":[4803,4804,4805,4803,4804,4805,0,4806,4807,0,4806,4807,4808,4809,4810,4808,4809,4810,4811,4812,4813,4811,4812,4813,4814,4815,4816,4814,4815,4816,4817,4818,4819,4817,4818,4819,4820,4821,4822,4820,4821,4822,4823,4824,4825,4823,4824,4825,4826,4827,4828,4826,4827,4828,4829,4830,4831,4829,4830,4831,4832,4833,4834,4832,4833,4834,4835,4836,4837,4835,4836,4837,4838,4839,4840,4838,4839,4840,4841,4842,4843,4841,4842,4843,4844,4845,4846,4844,4845,4846,4847,4848,4849,4847,4848,4849,4850,4851,4852,4850,4851,4852,4853,4854,4855,4853,4854,4855,4856,4857,4858,4856,4857,4858,4859,4860,4861,4859,4860,4861,4862,4863,4864,4862,4863,4864,4865,4866,4867,4865,4866,4867,4868,4869,4870,4868,4869,4870,4871,4872,4873,4871,4872,4873,4874,4875,4876,4874,4875,4876,4877,4878,4879,4877,4878,4879,4880,4881,4882,4880,4881,4882,4883,4884,4885,4883,4884,4885,4886,4887,4888,4886,4887,4888,4889,4890,4891,4889,4890,4891,4892,4893,4894,4892,4893,4894,4895,4896,4897,4895,4896,4897,4898,4899,4900,4898,4899,4900,4901,4902,4903,4901,4902,4903,4904,4905,4906,4904,4905,4906,4907,4908,4909,4907,4908,4909,4910,4911,4912,4910,4911,4912,4913,4914,4915,4913,4914,4915,4916,4917,4918,4916,4917,4918,4919,4920,4921,4919,4920,4921,4922,4923,4924,4922,4923,4924,4925,4926,4927,4925,4926,4927,4928,4929,4930,4928,4929,4930,4931,4932,4933,4931,4932,4933,4934,4935,4936,4934,4935,4936,4937,4938,4939,4937,4938,4939,4940,4941,4942,4940,4941,4942,4943,4944,4945,4943,4944,4945,4946,4947,4948,4946,4947,4948,4949,4950,4951,4949,4950,4951,4952,4953,4954,4952,4953,4954,4955,4956,4957,4955,4956,4957,4958,4959,4960,4958,4959,4960,4961,4962,4963,4961,4962,4963],"deepslate_bricks":1,"deepslate_brick_stairs":[28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,40,40,29,29,42,42,31,31,49,49,45,45,34,34,47,47,36,36,50,50,30,30,39,39,32,32,41,41,51,51,35,35,44,44,37,37,46,46],"deepslate_brick_slab":[273,273,274,274,1,1],"deepslate_brick_wall":[4964,4965,4966,4964,4965,4966,0,4967,4968,0,4967,4968,4969,4970,4971,4969,4970,4971,4972,4973,4974,4972,4973,4974,4975,4976,4977,4975,4976,4977,4978,4979,4980,4978,4979,4980,4981,4982,4983,4981,4982,4983,4984,4985,4986,4984,4985,4986,4987,4988,4989,4987,4988,4989,4990,4991,4992,4990,4991,4992,4993,4994,4995,4993,4994,4995,4996,4997,4998,4996,4997,4998,4999,5000,5001,4999,5000,5001,5002,5003,5004,5002,5003,5004,5005,5006,5007,5005,5006,5007,5008,5009,5010,5008,5009,5010,5011,5012,5013,5011,5012,5013,5014,5015,5016,5014,5015,5016,5017,5018,5019,5017,5018,5019,5020,5021,5022,5020,5021,5022,5023,5024,5025,5023,5024,5025,5026,5027,5028,5026,5027,5028,5029,5030,5031,5029,5030,5031,5032,5033,5034,5032,5033,5034,5035,5036,5037,5035,5036,5037,5038,5039,5040,5038,5039,5040,5041,5042,5043,5041,5042,5043,5044,5045,5046,5044,5045,5046,5047,5048,5049,5047,5048,5049,5050,5051,5052,5050,5051,5052,5053,5054,5055,5053,5054,5055,5056,5057,5058,5056,5057,5058,5059,5060,5061,5059,5060,5061,5062,5063,5064,5062,5063,5064,5065,5066,5067,5065,5066,5067,5068,5069,5070,5068,5069,5070,5071,5072,5073,5071,5072,5073,5074,5075,5076,5074,5075,5076,5077,5078,5079,5077,5078,5079,5080,5081,5082,5080,5081,5082,5083,5084,5085,5083,5084,5085,5086,5087,5088,5086,5087,5088,5089,5090,5091,5089,5090,5091,5092,5093,5094,5092,5093,5094,5095,5096,5097,5095,5096,5097,5098,5099,5100,5098,5099,5100,5101,5102,5103,5101,5102,5103,5104,5105,5106,5104,5105,5106,5107,5108,5109,5107,5108,5109,5110,5111,5112,5110,5111,5112,5113,5114,5115,5113,5114,5115,5116,5117,5118,5116,5117,5118,5119,5120,5121,5119,5120,5121,5122,5123,5124,5122,5123,5124],"chiseled_deepslate":1,"cracked_deepslate_bricks":1,"cracked_deepslate_tiles":1,"infested_deepslate":1,"smooth_basalt":1,"raw_iron_block":1,"raw_copper_block":1,"raw_gold_block":1,"potted_azalea_bush":793,"potted_flowering_azalea_bush":793,"ochre_froglight":1,"verdant_froglight":1,"pearlescent_froglight":1,"frogspawn":0,"reinforced_deepslate":1,"decorated_pot":5125,"crafter":1,"trial_spawner":1,"vault":1,"heavy_core":5126,"pale_moss_block":1,"pale_moss_carpet":[5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,5127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"pale_hanging_moss":0,"open_eyeblossom":0,"closed_eyeblossom":0,"potted_open_eyeblossom":793,"potted_closed_eyeblossom":793,"firefly_bush":0}} \ No newline at end of file diff --git a/MinecraftClient/Physics/BlockShapes.cs b/MinecraftClient/Physics/BlockShapes.cs new file mode 100644 index 00000000..c960913c --- /dev/null +++ b/MinecraftClient/Physics/BlockShapes.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using MinecraftClient.Mapping; +using MinecraftClient.Mapping.BlockPalettes; + +namespace MinecraftClient.Physics +{ + /// + /// Registry of block collision shapes. Maps block state IDs to collision AABBs. + /// Data sourced from PrismarineJS/minecraft-data blockCollisionShapes.json. + /// + public static class BlockShapes + { + private static readonly Aabb FullBlock = new(0, 0, 0, 1, 1, 1); + private static readonly Aabb[] FullBlockArray = { FullBlock }; + private static readonly Aabb[] EmptyArray = Array.Empty(); + + private static FrozenDictionary? stateToShape; + private static Dictionary? prismarineBlocks; + private static Dictionary? prismarineShapes; + + /// + /// Initialize the shape registry from embedded data + current palette. + /// Call once after the block palette is set. + /// + public static void Initialize() + { + LoadPrismarineData(); + BuildStateMap(); + } + + /// + /// Get collision shapes for a block state ID. + /// Returns empty array for air/passable blocks, single full-block for solid cubes, etc. + /// + public static Aabb[] GetShapes(int blockStateId) + { + if (stateToShape is not null && stateToShape.TryGetValue(blockStateId, out var shapes)) + return shapes; + return FallbackShape(blockStateId); + } + + /// + /// Get collision shapes for a Block at a specific position (state-aware) + /// + public static Aabb[] GetShapes(Block block) => GetShapes(block.BlockId); + + /// + /// Check if a block state is effectively empty (no collision) + /// + public static bool IsEmpty(int blockStateId) + { + var shapes = GetShapes(blockStateId); + return shapes.Length == 0; + } + + private static Aabb[] FallbackShape(int blockStateId) + { + Material mat = Block.Palette.FromId(blockStateId); + if (mat == Material.Air) return EmptyArray; + if (mat.IsLiquid()) return EmptyArray; + if (mat.IsSolid()) return FullBlockArray; + return EmptyArray; + } + + private static void LoadPrismarineData() + { + prismarineBlocks = new Dictionary(); + prismarineShapes = new Dictionary(); + + try + { + var assembly = Assembly.GetExecutingAssembly(); + using var stream = assembly.GetManifestResourceStream("BlockShapeData.json"); + if (stream is null) + { + ConsoleIO.WriteLineFormatted("§e[Physics] BlockShapeData.json not found as embedded resource"); + return; + } + using var doc = JsonDocument.Parse(stream); + var root = doc.RootElement; + + // Parse shapes: shapeId -> list of AABB boxes + if (root.TryGetProperty("shapes", out var shapesEl)) + { + foreach (var prop in shapesEl.EnumerateObject()) + { + if (int.TryParse(prop.Name, out int shapeId)) + { + var boxes = new List(); + foreach (var boxEl in prop.Value.EnumerateArray()) + { + var coords = new double[6]; + int idx = 0; + foreach (var c in boxEl.EnumerateArray()) + { + if (idx < 6) coords[idx++] = c.GetDouble(); + } + if (idx == 6) + boxes.Add(new Aabb(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])); + } + prismarineShapes[shapeId] = boxes.ToArray(); + } + } + } + + // Parse blocks: blockName -> shapeId (int) or list of shapeIds + if (root.TryGetProperty("blocks", out var blocksEl)) + { + foreach (var prop in blocksEl.EnumerateObject()) + { + string blockName = prop.Name; + if (prop.Value.ValueKind == JsonValueKind.Number) + { + prismarineBlocks[blockName] = prop.Value.GetInt32(); + } + else if (prop.Value.ValueKind == JsonValueKind.Array) + { + var ids = new List(); + foreach (var el in prop.Value.EnumerateArray()) + ids.Add(el.GetInt32()); + prismarineBlocks[blockName] = ids; + } + } + } + } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"§e[Physics] Failed to load BlockShapeData.json: {ex.Message}"); + } + } + + private static void BuildStateMap() + { + var builder = new Dictionary(); + + if (prismarineBlocks is null || prismarineShapes is null) + { + stateToShape = builder.ToFrozenDictionary(); + return; + } + + var palette = Block.Palette; + var dict = GetPaletteDict(palette); + if (dict is null) + { + stateToShape = builder.ToFrozenDictionary(); + return; + } + + // Group consecutive state IDs by Material to find state ranges per block + var materialRanges = new Dictionary>(); + int? rangeStart = null; + Material? currentMat = null; + + foreach (var kvp in dict.OrderBy(k => k.Key)) + { + if (currentMat == kvp.Value && rangeStart.HasValue && kvp.Key == (materialRanges[currentMat.Value].Last().end + 1)) + { + var ranges = materialRanges[currentMat.Value]; + ranges[ranges.Count - 1] = (ranges.Last().start, kvp.Key); + } + else + { + currentMat = kvp.Value; + if (!materialRanges.ContainsKey(currentMat.Value)) + materialRanges[currentMat.Value] = new List<(int, int)>(); + materialRanges[currentMat.Value].Add((kvp.Key, kvp.Key)); + } + } + + // Map each Material to PrismarineJS block name + foreach (var kvp in materialRanges) + { + string snakeName = MaterialToSnakeCase(kvp.Key); + if (!prismarineBlocks.TryGetValue(snakeName, out var blockShapeData)) + continue; + + int globalStateOffset = 0; + foreach (var (start, end) in kvp.Value) + { + int stateCount = end - start + 1; + + if (blockShapeData is int singleShapeId) + { + var shapes = prismarineShapes.GetValueOrDefault(singleShapeId, EmptyArray); + for (int sid = start; sid <= end; sid++) + builder[sid] = shapes; + } + else if (blockShapeData is List shapeIdList) + { + for (int i = 0; i < stateCount && (globalStateOffset + i) < shapeIdList.Count; i++) + { + int shapeId = shapeIdList[globalStateOffset + i]; + builder[start + i] = prismarineShapes.GetValueOrDefault(shapeId, EmptyArray); + } + } + globalStateOffset += stateCount; + } + } + stateToShape = builder.ToFrozenDictionary(); + } + + /// + /// Convert Material enum name (PascalCase) to snake_case block name + /// + private static string MaterialToSnakeCase(Material mat) + { + string name = mat.ToString(); + var sb = new System.Text.StringBuilder(name.Length + 5); + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + if (char.IsUpper(c) && i > 0) + sb.Append('_'); + sb.Append(char.ToLowerInvariant(c)); + } + return sb.ToString(); + } + + /// + /// Access the internal dictionary of a palette via reflection (all palettes store it the same way) + /// + private static Dictionary? GetPaletteDict(BlockPalette palette) + { + try + { + var method = palette.GetType().GetMethod("GetDict", + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + return method?.Invoke(palette, null) as Dictionary; + } + catch + { + return null; + } + } + } +} diff --git a/MinecraftClient/Physics/CollisionDetector.cs b/MinecraftClient/Physics/CollisionDetector.cs new file mode 100644 index 00000000..0788e93b --- /dev/null +++ b/MinecraftClient/Physics/CollisionDetector.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Physics +{ + /// + /// Performs AABB collision detection against the block world. + /// Mirrors Entity.collide(), collideBoundingBox(), collideWithShapes() from vanilla MC. + /// + public static class CollisionDetector + { + /// + /// Resolve movement with full collision detection including step-up. + /// This is the main entry point, equivalent to Entity.collide(Vec3). + /// + public static Vec3d Collide(World world, Aabb entityBox, Vec3d movement, bool onGround, float maxUpStep) + { + if (movement.LengthSqr() == 0.0) + return movement; + + // Collect block collision shapes in the movement path + var colliders = CollectBlockColliders(world, entityBox.ExpandTowards(movement)); + Vec3d resolved = CollideWithShapes(movement, entityBox, colliders); + + bool blockedX = movement.X != resolved.X; + bool blockedZ = movement.Z != resolved.Z; + bool blockedY = movement.Y != resolved.Y; + bool hitGroundDuringMove = blockedY && movement.Y < 0.0; + + // Step-up logic: if blocked horizontally and on ground or just landed + if (maxUpStep > 0.0f && (hitGroundDuringMove || onGround) && (blockedX || blockedZ)) + { + // Try stepping up + Aabb stepBase = hitGroundDuringMove ? entityBox.Move(0, resolved.Y, 0) : entityBox; + Aabb expanded = stepBase.ExpandTowards(movement.X, maxUpStep, movement.Z) + .ExpandTowards(0, hitGroundDuringMove ? 0 : -1.0E-5, 0); + + var stepColliders = CollectBlockColliders(world, expanded); + + // Try various step heights + float[] candidateHeights = CollectCandidateStepHeights(stepBase, stepColliders, maxUpStep, (float)resolved.Y); + + foreach (float stepY in candidateHeights) + { + Vec3d stepMovement = new Vec3d(movement.X, stepY, movement.Z); + Vec3d stepResolved = CollideWithShapes(stepMovement, stepBase, stepColliders); + + if (stepResolved.HorizontalDistanceSqr() > resolved.HorizontalDistanceSqr()) + { + double yOffset = entityBox.MinY - stepBase.MinY; + return stepResolved.Subtract(0, yOffset, 0); + } + } + } + + return resolved; + } + + /// + /// Collide movement against a list of shapes using axis-separated resolution. + /// Matches Entity.collideWithShapes() — processes axes in order of smallest movement first. + /// + private static Vec3d CollideWithShapes(Vec3d movement, Aabb entityBox, List colliders) + { + if (colliders.Count == 0) + return movement; + + Vec3d accumulated = Vec3d.Zero; + int[] axisOrder = GetAxisStepOrder(movement); + + foreach (int axis in axisOrder) + { + double dist = movement.Get(axis); + if (dist == 0.0) continue; + + double resolved = CollideAxis(axis, entityBox.Move(accumulated), colliders, dist); + accumulated = accumulated.With(axis, resolved); + } + + return accumulated; + } + + /// + /// Get axis processing order: Y first if moving down, otherwise smallest absolute movement first. + /// Vanilla uses Direction.axisStepOrder(Vec3) which returns axes sorted by absolute movement. + /// + private static int[] GetAxisStepOrder(Vec3d movement) + { + double absX = Math.Abs(movement.X); + double absY = Math.Abs(movement.Y); + double absZ = Math.Abs(movement.Z); + + if (absX > absZ) + { + if (absZ > absY) + return new[] { 1, 2, 0 }; // Y Z X + if (absX > absY) + return new[] { 1, 0, 2 }; // Y X Z + return new[] { 0, 1, 2 }; // X Y Z + } + else + { + if (absX > absY) + return new[] { 1, 0, 2 }; // Y X Z + if (absZ > absY) + return new[] { 1, 2, 0 }; // Y Z X + return new[] { 2, 1, 0 }; // Z Y X + } + } + + /// + /// Collide along a single axis against all block shapes. + /// Equivalent to Shapes.collide(axis, box, shapes, distance). + /// + private static double CollideAxis(int axis, Aabb entityBox, List colliders, double movement) + { + foreach (var collider in colliders) + { + if (Math.Abs(movement) < PhysicsConsts.CollisionEpsilon) + return 0.0; + movement = entityBox.Collide(axis, collider, movement); + } + return movement; + } + + /// + /// Collect all block collision AABBs that overlap the given search area. + /// Equivalent to BlockCollisions iterator in vanilla. + /// + public static List CollectBlockColliders(World world, Aabb searchBox) + { + var result = new List(); + + int minBX = (int)Math.Floor(searchBox.MinX - PhysicsConsts.CollisionEpsilon) - 1; + int maxBX = (int)Math.Floor(searchBox.MaxX + PhysicsConsts.CollisionEpsilon) + 1; + int minBY = (int)Math.Floor(searchBox.MinY - PhysicsConsts.CollisionEpsilon) - 1; + int maxBY = (int)Math.Floor(searchBox.MaxY + PhysicsConsts.CollisionEpsilon) + 1; + int minBZ = (int)Math.Floor(searchBox.MinZ - PhysicsConsts.CollisionEpsilon) - 1; + int maxBZ = (int)Math.Floor(searchBox.MaxZ + PhysicsConsts.CollisionEpsilon) + 1; + + for (int bx = minBX; bx <= maxBX; bx++) + { + for (int bz = minBZ; bz <= maxBZ; bz++) + { + for (int by = minBY; by <= maxBY; by++) + { + Block block = world.GetBlock(new Location(bx, by, bz)); + Aabb[] shapes = BlockShapes.GetShapes(block); + + foreach (var shape in shapes) + { + Aabb worldShape = shape.Move(bx, by, bz); + if (worldShape.Intersects(searchBox)) + result.Add(worldShape); + } + } + } + } + + return result; + } + + /// + /// Collect candidate step-up heights, matching Entity.collectCandidateStepUpHeights(). + /// Returns sorted distinct step heights between current resolved Y and maxUpStep. + /// + private static float[] CollectCandidateStepHeights(Aabb stepBase, List colliders, float maxUpStep, float currentY) + { + var heights = new SortedSet(); + + foreach (var collider in colliders) + { + float h = (float)(collider.MaxY - stepBase.MinY); + if (h > currentY && h <= maxUpStep) + heights.Add(h); + } + + if (heights.Count == 0) + return new[] { maxUpStep }; + + var result = new float[heights.Count]; + heights.CopyTo(result); + return result; + } + + /// + /// Check if a position is on ground by testing for vertical collision below. + /// + public static bool IsOnGround(World world, Aabb entityBox) + { + Aabb testBox = entityBox.ExpandTowards(0, -0.06, 0); + return CollectBlockColliders(world, testBox).Count > 0; + } + + /// + /// Check if a given position has no collision (for checking if player fits somewhere). + /// + public static bool NoCollision(World world, Aabb entityBox) + { + return CollectBlockColliders(world, entityBox).Count == 0; + } + } +} diff --git a/MinecraftClient/Physics/MovementInput.cs b/MinecraftClient/Physics/MovementInput.cs new file mode 100644 index 00000000..f1899d57 --- /dev/null +++ b/MinecraftClient/Physics/MovementInput.cs @@ -0,0 +1,55 @@ +using System; + +namespace MinecraftClient.Physics +{ + /// + /// Represents movement input state, equivalent to vanilla ClientInput / KeyboardInput. + /// + public class MovementInput + { + public bool Forward; + public bool Back; + public bool Left; + public bool Right; + public bool Jump; + public bool Sneak; + public bool Sprint; + + /// + /// Get the raw input vector (xxa, zza) before rotation. + /// Forward = +zza, Back = -zza, Left = +xxa, Right = -xxa. + /// Then normalized if magnitude > 1. + /// + public (float xxa, float zza) GetMoveVector() + { + float xxa = 0; + float zza = 0; + + if (Forward) zza += 1.0f; + if (Back) zza -= 1.0f; + if (Left) xxa += 1.0f; + if (Right) xxa -= 1.0f; + + float lenSqr = xxa * xxa + zza * zza; + if (lenSqr > 1.0f) + { + float len = MathF.Sqrt(lenSqr); + xxa /= len; + zza /= len; + } + + return (xxa, zza); + } + + public void Reset() + { + Forward = false; + Back = false; + Left = false; + Right = false; + Jump = false; + Sneak = false; + Sprint = false; + } + } +} diff --git a/MinecraftClient/Physics/PhysicsConsts.cs b/MinecraftClient/Physics/PhysicsConsts.cs new file mode 100644 index 00000000..95cb02c1 --- /dev/null +++ b/MinecraftClient/Physics/PhysicsConsts.cs @@ -0,0 +1,87 @@ +namespace MinecraftClient.Physics +{ + /// + /// All physics constants matching vanilla Minecraft 1.21.11. + /// Values sourced from Entity.java, LivingEntity.java, Player.java, LocalPlayer.java. + /// + public static class PhysicsConsts + { + // --- Player dimensions --- + public const double PlayerWidth = 0.6; + public const double PlayerHeight = 1.8; + public const double PlayerSneakHeight = 1.5; + public const double PlayerSwimHeight = 0.6; + public const double PlayerEyeHeight = 1.62; + + // --- Gravity --- + public const double DefaultGravity = 0.08; + public const double SlowFallingCap = 0.01; + + // --- Step height --- + public const float StepHeight = 0.6f; + + // --- Friction / drag --- + public const float FrictionMultiplier = 0.91f; + public const float DragY = 0.98f; + public const float InputFriction = 0.98f; + public const float GroundAccelerationFactor = 0.21600002f; // 0.216 / (f^3) + public const float AirAcceleration = 0.02f; + + // --- Default block friction --- + public const float DefaultBlockFriction = 0.6f; + public const float IceFriction = 0.98f; + public const float PackedIceFriction = 0.98f; + public const float BlueIceFriction = 0.989f; + public const float SlimeBlockFriction = 0.8f; + + // --- Speed factors --- + public const float DefaultSpeedFactor = 1.0f; + public const float SoulSandSpeedFactor = 0.4f; + public const float HoneySpeedFactor = 0.4f; + + // --- Water --- + public const float WaterSlowDown = 0.8f; + public const float WaterSprintSlowDown = 0.9f; + public const float DolphinsGraceSlowDown = 0.96f; + public const float WaterBaseSpeed = 0.02f; + public const float WaterYDamping = 0.8f; + public const float WaterFloatImpulse = 0.04f; + + // --- Lava --- + public const float LavaSpeed = 0.02f; + public const double LavaHorizontalDamping = 0.5; + public const double LavaVerticalDamping = 0.8; + + // --- Jump --- + public const float BaseJumpPower = 0.42f; + public const double SprintJumpHorizontalBoost = 0.2; + + // --- Climb --- + public const float ClimbMaxSpeed = 0.15f; + public const double ClimbWallBump = 0.2; + + // --- Velocity zeroing thresholds (from LivingEntity.aiStep) --- + public const double PlayerHorizontalVelocityThresholdSqr = 9.0E-6; // < 0.003 length + public const double NonPlayerVelocityThreshold = 0.003; + public const double VerticalVelocityThreshold = 0.003; + + // --- Collision epsilon --- + public const double CollisionEpsilon = 1.0E-7; + + // --- Position packet sending (from LocalPlayer.sendPosition) --- + public const double PositionSendThresholdSqr = 4.0E-8; // (2e-4)^2 + public const int PositionReminderInterval = 20; + + // --- Flying detection --- + public const double FloatingYThreshold = -0.03125; + + // --- Elytra --- + public const double ElytraXZDrag = 0.99; + public const double ElytraYDrag = 0.98; + + // --- Creative/spectator fly --- + public const float DefaultFlySpeed = 0.05f; + public const double FlyVerticalDamping = 0.6; + public const double FlyVerticalBoostScale = 3.0; + } +} diff --git a/MinecraftClient/Physics/PlayerPhysics.cs b/MinecraftClient/Physics/PlayerPhysics.cs new file mode 100644 index 00000000..bf18429f --- /dev/null +++ b/MinecraftClient/Physics/PlayerPhysics.cs @@ -0,0 +1,579 @@ +using System; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Physics +{ + /// + /// Core physics tick engine for the player, faithfully replicating vanilla 1.21.11 physics. + /// Mirrors the combined logic of Entity.move(), LivingEntity.aiStep()/travel()/travelInAir(), + /// Player.travel(), and LocalPlayer.aiStep(). + /// + public class PlayerPhysics + { + // --- State --- + public Vec3d Position; + public Vec3d DeltaMovement; + public float Yaw; + public float Pitch; + public bool OnGround; + public bool HorizontalCollision; + public bool VerticalCollision; + public bool VerticalCollisionBelow; + public double FallDistance; + public Vec3d StuckSpeedMultiplier = Vec3d.Zero; + + // Movement input + public float Xxa; // strafe + public float Zza; // forward + public float Yya; // vertical (creative fly) + public bool Jumping; + + // Movement mode flags + public bool Sprinting; + public bool Sneaking; + public bool CreativeFlying; + public bool InWater; + public bool InLava; + public bool OnClimbable; + public bool HasSlowFalling; + public bool HasLevitation; + public int LevitationAmplifier; + + // Player dimensions + public double PlayerWidth = PhysicsConsts.PlayerWidth; + public double PlayerHeight = PhysicsConsts.PlayerHeight; + + // Anti-jump-spam + private int noJumpDelay; + + // Tick counter for position packet timing + public int TickCount; + + // Movement speed attribute (base = 0.1 for players) + public float MovementSpeed = 0.1f; + + /// + /// Get the player's bounding box at current position + /// + public Aabb GetBoundingBox() + { + return Aabb.OfSize(Position.X, Position.Y, Position.Z, PlayerWidth, PlayerHeight); + } + + /// + /// Run one physics tick. Call at 20 TPS. + /// + public void Tick(World world) + { + TickCount++; + + // Velocity threshold zeroing (LivingEntity.aiStep) + ZeroTinyVelocity(); + + // Jump handling + HandleJumping(world); + + // Build travel input + Vec3d travelInput = new(Xxa, Yya, Zza); + + // Travel (dispatches to air/water/lava/fly) + Travel(world, travelInput); + + if (noJumpDelay > 0) + noJumpDelay--; + } + + /// + /// Apply the movement input from MovementInput to xxa/zza. + /// Call before Tick() each frame. + /// + public void ApplyInput(MovementInput input) + { + var (rawXxa, rawZza) = input.GetMoveVector(); + + // Scale by INPUT_FRICTION (0.98) — this matches LocalPlayer.modifyInput + rawXxa *= PhysicsConsts.InputFriction; + rawZza *= PhysicsConsts.InputFriction; + + // Sneak slowdown + if (input.Sneak) + { + rawXxa *= 0.3f; + rawZza *= 0.3f; + } + + Xxa = rawXxa; + Zza = rawZza; + Yya = 0; + Jumping = input.Jump; + Sneaking = input.Sneak; + Sprinting = input.Sprint; + + // Creative/spectator fly vertical + if (CreativeFlying) + { + if (input.Jump) + Yya += (float)(PhysicsConsts.DefaultFlySpeed * PhysicsConsts.FlyVerticalBoostScale); + if (input.Sneak) + Yya -= (float)(PhysicsConsts.DefaultFlySpeed * PhysicsConsts.FlyVerticalBoostScale); + } + } + + private void ZeroTinyVelocity() + { + double dx = DeltaMovement.X; + double dy = DeltaMovement.Y; + double dz = DeltaMovement.Z; + + // Player-specific: zero horizontal if combined length < 0.003 + if (dx * dx + dz * dz < PhysicsConsts.PlayerHorizontalVelocityThresholdSqr) + { + dx = 0; + dz = 0; + } + if (Math.Abs(dy) < PhysicsConsts.VerticalVelocityThreshold) + dy = 0; + + DeltaMovement = new Vec3d(dx, dy, dz); + } + + private void HandleJumping(World world) + { + if (!Jumping) { noJumpDelay = 0; return; } + + if (InWater || InLava) + { + // Jump in fluid: add upward impulse + DeltaMovement = DeltaMovement.Add(0, PhysicsConsts.WaterFloatImpulse, 0); + } + else if (OnGround && noJumpDelay == 0) + { + JumpFromGround(); + noJumpDelay = 10; + } + } + + private void JumpFromGround() + { + float jumpPower = PhysicsConsts.BaseJumpPower; + if (jumpPower <= 1.0E-5f) return; + + DeltaMovement = new Vec3d( + DeltaMovement.X, + Math.Max(jumpPower, DeltaMovement.Y), + DeltaMovement.Z); + + if (Sprinting) + { + float yawRad = Yaw * (MathF.PI / 180.0f); + DeltaMovement = DeltaMovement.Add( + -MathF.Sin(yawRad) * PhysicsConsts.SprintJumpHorizontalBoost, + 0, + MathF.Cos(yawRad) * PhysicsConsts.SprintJumpHorizontalBoost); + } + } + + private void Travel(World world, Vec3d input) + { + if (InWater && !CreativeFlying) + { + TravelInWater(world, input); + } + else if (InLava && !CreativeFlying) + { + TravelInLava(world, input); + } + else + { + TravelInAir(world, input); + } + } + + /// + /// Ground/air travel — LivingEntity.travelInAir(Vec3) + /// + private void TravelInAir(World world, Vec3d input) + { + // Get block friction at feet + float blockFriction = OnGround ? GetBlockFriction(world) : 1.0f; + float f = blockFriction * PhysicsConsts.FrictionMultiplier; + + // Apply input → velocity (handleRelativeFrictionAndCalculateMovement) + float speed = GetFrictionInfluencedSpeed(blockFriction); + MoveRelative(speed, input); + + // Handle climbable + HandleOnClimbable(); + + // Execute collision + Move(world, DeltaMovement); + + Vec3d postMoveVel = DeltaMovement; + double vy = postMoveVel.Y; + + // Climbing wall bump + if ((HorizontalCollision || Jumping) && OnClimbable) + { + vy = PhysicsConsts.ClimbWallBump; + } + + // Apply gravity + if (HasLevitation) + { + vy += (0.05 * (LevitationAmplifier + 1) - vy) * 0.2; + } + else + { + vy -= GetEffectiveGravity(); + } + + // Apply drag/friction + if (CreativeFlying) + { + // Player.travel override: creative fly preserves horizontal from parent, damps Y + DeltaMovement = new Vec3d(postMoveVel.X * f, vy * PhysicsConsts.FlyVerticalDamping, postMoveVel.Z * f); + } + else + { + DeltaMovement = new Vec3d(postMoveVel.X * f, vy * PhysicsConsts.DragY, postMoveVel.Z * f); + } + + // Block speed factor (soul sand, honey, etc.) + ApplyBlockSpeedFactor(world); + } + + /// + /// Water travel — LivingEntity.travelInWater(Vec3, ...) + /// + private void TravelInWater(World world, Vec3d input) + { + float slowDown = Sprinting ? PhysicsConsts.WaterSprintSlowDown : PhysicsConsts.WaterSlowDown; + float speed = PhysicsConsts.WaterBaseSpeed; + + MoveRelative(speed, input); + Move(world, DeltaMovement); + + Vec3d vel = DeltaMovement; + + // Climbing bump in water + if (HorizontalCollision && OnClimbable) + vel = new Vec3d(vel.X, PhysicsConsts.ClimbWallBump, vel.Z); + + vel = vel.Multiply(slowDown, PhysicsConsts.WaterYDamping, slowDown); + + // Gravity adjustment in water + double gravity = GetEffectiveGravity(); + if (gravity != 0.0) + { + double adjustedY = vel.Y; + bool falling = vel.Y <= 0.0; + if (falling && Math.Abs(vel.Y - 0.005) >= PhysicsConsts.VerticalVelocityThreshold) + { + adjustedY -= gravity / 16.0; + } + + if (!OnGround) + adjustedY -= gravity / 16.0; + + vel = new Vec3d(vel.X, adjustedY, vel.Z); + } + + DeltaMovement = vel; + } + + /// + /// Lava travel — LivingEntity.travelInLava(Vec3, ...) + /// + private void TravelInLava(World world, Vec3d input) + { + MoveRelative(PhysicsConsts.LavaSpeed, input); + Move(world, DeltaMovement); + + double gravity = GetEffectiveGravity(); + Vec3d vel = DeltaMovement; + vel = vel.Multiply(PhysicsConsts.LavaHorizontalDamping, PhysicsConsts.LavaVerticalDamping, PhysicsConsts.LavaHorizontalDamping); + + if (gravity != 0.0) + { + vel = vel.Add(0, -gravity / 4.0, 0); + } + + DeltaMovement = vel; + } + + /// + /// Add input vector rotated by yaw to deltaMovement. + /// Equivalent to Entity.moveRelative(float, Vec3) + getInputVector(). + /// + private void MoveRelative(float speed, Vec3d input) + { + Vec3d rotated = GetInputVector(input, speed, Yaw); + DeltaMovement = DeltaMovement.Add(rotated); + } + + /// + /// Rotate input by yaw and scale by speed. Equivalent to Entity.getInputVector(). + /// + private static Vec3d GetInputVector(Vec3d input, float speed, float yaw) + { + double lenSqr = input.LengthSqr(); + if (lenSqr < 1.0E-7) + return Vec3d.Zero; + + Vec3d scaled = (lenSqr > 1.0 ? input.Normalize() : input).Scale(speed); + float sinYaw = MathF.Sin(yaw * (MathF.PI / 180.0f)); + float cosYaw = MathF.Cos(yaw * (MathF.PI / 180.0f)); + + return new Vec3d( + scaled.X * cosYaw - scaled.Z * sinYaw, + scaled.Y, + scaled.Z * cosYaw + scaled.X * sinYaw); + } + + /// + /// Execute movement with collision detection. + /// Equivalent to Entity.move(MoverType.SELF, delta). + /// + private void Move(World world, Vec3d movement) + { + if (StuckSpeedMultiplier.LengthSqr() > 1.0E-7) + { + movement = movement.Multiply(StuckSpeedMultiplier); + StuckSpeedMultiplier = Vec3d.Zero; + DeltaMovement = Vec3d.Zero; + } + + // Sneak edge back-off + if (Sneaking && OnGround) + movement = MaybeBackOffFromEdge(world, movement); + + Aabb box = GetBoundingBox(); + Vec3d resolved = CollisionDetector.Collide(world, box, movement, OnGround, PhysicsConsts.StepHeight); + + double resolvedLenSqr = resolved.LengthSqr(); + if (resolvedLenSqr > 1.0E-7 || movement.LengthSqr() - resolvedLenSqr < 1.0E-7) + { + // Fall distance reset via trace (simplified: reset on hitting ground) + if (FallDistance != 0.0 && resolvedLenSqr >= 1.0) + { + // Simplified: just check vertical collision + } + + Position = Position.Add(resolved); + } + + // Collision flags + bool blockedX = !MthEqual(movement.X, resolved.X); + bool blockedZ = !MthEqual(movement.Z, resolved.Z); + HorizontalCollision = blockedX || blockedZ; + VerticalCollision = movement.Y != resolved.Y; + VerticalCollisionBelow = VerticalCollision && movement.Y < 0.0; + OnGround = VerticalCollisionBelow; + + // Fall distance tracking + if (OnGround) + FallDistance = 0; + else if (resolved.Y < 0) + FallDistance -= resolved.Y; + + // Zero velocity on blocked axes + if (HorizontalCollision) + { + DeltaMovement = new Vec3d( + blockedX ? 0 : DeltaMovement.X, + DeltaMovement.Y, + blockedZ ? 0 : DeltaMovement.Z); + } + + if (VerticalCollision) + { + // Slime block bounce would go here; for now just zero Y + DeltaMovement = new Vec3d(DeltaMovement.X, 0, DeltaMovement.Z); + } + } + + /// + /// Sneak edge detection: prevent walking off edges while sneaking. + /// Equivalent to Player.maybeBackOffFromEdge(Vec3, MoverType). + /// + private Vec3d MaybeBackOffFromEdge(World world, Vec3d movement) + { + if (movement.Y > 0) return movement; + + double step = 0.05; + double dx = movement.X; + double dz = movement.Z; + Aabb box = GetBoundingBox(); + + while (dx != 0.0 && CollisionDetector.CollectBlockColliders(world, + box.Move(dx, -1.0, 0)).Count == 0) + { + dx = dx < step && dx >= -step ? 0.0 : (dx > 0.0 ? dx - step : dx + step); + } + + while (dz != 0.0 && CollisionDetector.CollectBlockColliders(world, + box.Move(0, -1.0, dz)).Count == 0) + { + dz = dz < step && dz >= -step ? 0.0 : (dz > 0.0 ? dz - step : dz + step); + } + + while (dx != 0.0 && dz != 0.0 && CollisionDetector.CollectBlockColliders(world, + box.Move(dx, -1.0, dz)).Count == 0) + { + dx = dx < step && dx >= -step ? 0.0 : (dx > 0.0 ? dx - step : dx + step); + dz = dz < step && dz >= -step ? 0.0 : (dz > 0.0 ? dz - step : dz + step); + } + + return new Vec3d(dx, movement.Y, dz); + } + + /// + /// Clamp velocity for climbable blocks. + /// Equivalent to LivingEntity.handleOnClimbable(Vec3). + /// + private void HandleOnClimbable() + { + if (!OnClimbable) return; + + FallDistance = 0; + double vx = Math.Clamp(DeltaMovement.X, -PhysicsConsts.ClimbMaxSpeed, PhysicsConsts.ClimbMaxSpeed); + double vz = Math.Clamp(DeltaMovement.Z, -PhysicsConsts.ClimbMaxSpeed, PhysicsConsts.ClimbMaxSpeed); + double vy = Math.Max(DeltaMovement.Y, -PhysicsConsts.ClimbMaxSpeed); + + // Sneaking on ladder prevents sliding down + if (vy < 0.0 && Sneaking) + vy = 0.0; + + DeltaMovement = new Vec3d(vx, vy, vz); + } + + /// + /// Get effective gravity considering slow falling effect. + /// + private double GetEffectiveGravity() + { + double gravity = PhysicsConsts.DefaultGravity; + if (HasSlowFalling && DeltaMovement.Y <= 0.0) + return Math.Min(gravity, PhysicsConsts.SlowFallingCap); + return gravity; + } + + /// + /// Get speed based on friction: ground uses attribute speed * 0.216/(f^3), air uses 0.02. + /// Equivalent to LivingEntity.getFrictionInfluencedSpeed(float). + /// + private float GetFrictionInfluencedSpeed(float friction) + { + if (OnGround) + { + return MovementSpeed * (PhysicsConsts.GroundAccelerationFactor / (friction * friction * friction)); + } + else + { + return CreativeFlying ? MovementSpeed * 0.1f : PhysicsConsts.AirAcceleration; + } + } + + /// + /// Get the friction of the block below the player's feet. + /// + private float GetBlockFriction(World world) + { + Location belowFeet = new(Position.X, Position.Y - 0.5000010, Position.Z); + Material mat = world.GetBlock(belowFeet).Type; + return GetMaterialFriction(mat); + } + + /// + /// Apply block speed factor (soul sand, honey, etc.) + /// Equivalent to Entity.getBlockSpeedFactor(). + /// + private void ApplyBlockSpeedFactor(World world) + { + Location atFeet = new(Position.X, Position.Y, Position.Z); + Material mat = world.GetBlock(atFeet).Type; + float factor = GetMaterialSpeedFactor(mat); + + if (factor == 1.0f) + { + Location belowFeet = new(Position.X, Position.Y - 0.5000010, Position.Z); + mat = world.GetBlock(belowFeet).Type; + factor = GetMaterialSpeedFactor(mat); + } + + if (factor != 1.0f) + { + DeltaMovement = DeltaMovement.Multiply(factor, 1.0, factor); + } + } + + /// + /// Get friction value for a material. Default 0.6, special blocks differ. + /// + public static float GetMaterialFriction(Material mat) + { + return mat switch + { + Material.Ice or Material.PackedIce => PhysicsConsts.IceFriction, + Material.BlueIce => PhysicsConsts.BlueIceFriction, + Material.SlimeBlock => PhysicsConsts.SlimeBlockFriction, + Material.FrostedIce => PhysicsConsts.IceFriction, + _ => PhysicsConsts.DefaultBlockFriction + }; + } + + /// + /// Get speed factor for a material. + /// + public static float GetMaterialSpeedFactor(Material mat) + { + return mat switch + { + Material.SoulSand or Material.SoulSoil => PhysicsConsts.SoulSandSpeedFactor, + Material.HoneyBlock => PhysicsConsts.HoneySpeedFactor, + _ => PhysicsConsts.DefaultSpeedFactor + }; + } + + /// + /// Update environmental state flags (in water, in lava, on climbable, etc.) + /// Call before each Tick(). + /// + public void UpdateEnvironment(World world) + { + Location feetLoc = new(Position.X, Position.Y, Position.Z); + Location headLoc = new(Position.X, Position.Y + PlayerHeight * 0.5, Position.Z); + + Material feetBlock = world.GetBlock(feetLoc).Type; + Material headBlock = world.GetBlock(headLoc).Type; + + InWater = feetBlock == Material.Water || headBlock == Material.Water + || feetBlock == Material.BubbleColumn; + InLava = feetBlock == Material.Lava || headBlock == Material.Lava; + OnClimbable = feetBlock.CanBeClimbedOn(); + } + + /// + /// Set position from server teleport / initial spawn. + /// + public void SetPosition(double x, double y, double z) + { + Position = new Vec3d(x, y, z); + } + + /// + /// Set position and reset velocity (for teleports). + /// + public void Teleport(double x, double y, double z) + { + Position = new Vec3d(x, y, z); + DeltaMovement = Vec3d.Zero; + FallDistance = 0; + } + + private static bool MthEqual(double a, double b) + { + return Math.Abs(a - b) < 1.0E-5; + } + } +} diff --git a/MinecraftClient/Physics/Vec3d.cs b/MinecraftClient/Physics/Vec3d.cs new file mode 100644 index 00000000..bbda4331 --- /dev/null +++ b/MinecraftClient/Physics/Vec3d.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.CompilerServices; + +namespace MinecraftClient.Physics +{ + /// + /// Immutable 3D double vector, mirrors net.minecraft.world.phys.Vec3 + /// + public readonly struct Vec3d : IEquatable + { + public static readonly Vec3d Zero = new(0, 0, 0); + + public readonly double X; + public readonly double Y; + public readonly double Z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Add(double x, double y, double z) => new(X + x, Y + y, Z + z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Add(Vec3d other) => new(X + other.X, Y + other.Y, Z + other.Z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Subtract(Vec3d other) => new(X - other.X, Y - other.Y, Z - other.Z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Subtract(double x, double y, double z) => new(X - x, Y - y, Z - z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Scale(double factor) => new(X * factor, Y * factor, Z * factor); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Multiply(double x, double y, double z) => new(X * x, Y * y, Z * z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vec3d Multiply(Vec3d other) => new(X * other.X, Y * other.Y, Z * other.Z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double LengthSqr() => X * X + Y * Y + Z * Z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Length() => Math.Sqrt(LengthSqr()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double HorizontalDistanceSqr() => X * X + Z * Z; + + public Vec3d Normalize() + { + double len = Length(); + return len < 1.0E-7 ? Zero : new Vec3d(X / len, Y / len, Z / len); + } + + /// + /// Get component by axis index: 0=X, 1=Y, 2=Z + /// + public double Get(int axis) => axis switch + { + 0 => X, + 1 => Y, + 2 => Z, + _ => throw new ArgumentOutOfRangeException(nameof(axis)) + }; + + /// + /// Return a new Vec3d with one axis replaced + /// + public Vec3d With(int axis, double value) => axis switch + { + 0 => new Vec3d(value, Y, Z), + 1 => new Vec3d(X, value, Z), + 2 => new Vec3d(X, Y, value), + _ => throw new ArgumentOutOfRangeException(nameof(axis)) + }; + + public bool Equals(Vec3d other) => + X == other.X && Y == other.Y && Z == other.Z; + + public override bool Equals(object? obj) => + obj is Vec3d other && Equals(other); + + public override int GetHashCode() => + HashCode.Combine(X, Y, Z); + + public override string ToString() => + $"({X:F4}, {Y:F4}, {Z:F4})"; + + public static bool operator ==(Vec3d a, Vec3d b) => a.Equals(b); + public static bool operator !=(Vec3d a, Vec3d b) => !a.Equals(b); + public static Vec3d operator +(Vec3d a, Vec3d b) => a.Add(b); + public static Vec3d operator -(Vec3d a, Vec3d b) => a.Subtract(b); + public static Vec3d operator *(Vec3d a, double s) => a.Scale(s); + public static Vec3d operator -(Vec3d a) => new(-a.X, -a.Y, -a.Z); + } +} diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index d47bdffe..c9d3a153 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -1,9 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,7 +19,6 @@ using MinecraftClient.Protocol.Session; using MinecraftClient.Scripting; using MinecraftClient.WinAPI; using Sentry; -using Tomlet; using static MinecraftClient.Settings; using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig; using static MinecraftClient.Settings.MainConfigHelper.MainConfig.AdvancedConfig; @@ -47,34 +48,48 @@ namespace MinecraftClient public const string Version = MCHighestVersion; public const string MCLowestVersion = "1.4.6"; - public const string MCHighestVersion = "1.20.4"; + public const string MCHighestVersion = "26.1"; public static readonly string? BuildInfo = null; private static Tuple? offlinePrompt = null; private static IDisposable? _sentrySdk = null; private static bool useMcVersionOnce = false; + private static Thread? _restartThread = null; + private static readonly object _restartLock = new(); private static string settingsIniPath = "MinecraftClient.ini"; // [SENTRY] // Setting this string to an empty string will disable Sentry private const string SentryDSN = ""; + /// + /// Snapshot of all state collected before the console backend is initialized. + /// Passed to once the backend is ready. + /// + internal sealed class StartupState + { + public Settings.ConfigLoadResult ConfigResult { get; init; } + public bool NewlyGenerated { get; init; } + public bool SentryEnabled { get; init; } + } + /// /// The main entry point of Minecraft Console Client /// static void Main(string[] args) { // [SENTRY] Initialize Sentry SDK only if the DSN is not empty - if (SentryDSN != string.Empty) { + if (SentryDSN != string.Empty) + { _sentrySdk = SentrySdk.Init(options => { options.Dsn = SentryDSN; options.AutoSessionTracking = true; options.IsGlobalModeEnabled = true; - options.EnableTracing = true; + options.TracesSampleRate = 1.0; options.SendDefaultPii = false; }); - + AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => { SentrySdk.CaptureException((Exception)eventArgs.ExceptionObject); @@ -103,7 +118,6 @@ namespace MinecraftClient Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); }); - //Setup ConsoleIO ConsoleIO.LogPrefix = "§8[MCC] "; if (args.Length >= 1 && args[^1] == "BasicIO" || args.Length >= 1 && args[^1] == "BasicIO-NoColor") { @@ -115,107 +129,268 @@ namespace MinecraftClient args = args.Where(o => !Object.ReferenceEquals(o, args[^1])).ToArray(); } - if (!ConsoleIO.BasicIO) - ConsoleInteractive.ConsoleWriter.Init(); - - ConsoleIO.WriteLine($"Minecraft Console Client v{Version} - for MC {MCLowestVersion} to {MCHighestVersion} - Github.com/MCCTeam"); - - //Build information to facilitate processing of bug reports - if (BuildInfo != null) - ConsoleIO.WriteLineFormatted("§8" + BuildInfo); - //Debug input ? if (args.Length == 1 && args[0] == "--keyboard-debug") { + if (!ConsoleIO.BasicIO) + { + ConsoleIO.Backend = new ClassicConsoleBackend(); + ConsoleIO.Backend.Init(); + } ConsoleIO.WriteLine("Keyboard debug mode: Press any key to display info"); ConsoleIO.DebugReadInput(); } - //Process ini configuration file + // --- Load config as early as possible (no printing yet) --- + Settings.ConfigLoadResult configResult; + bool newlyGenerated = false; + + if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") { - bool loadSucceed, needWriteDefaultSetting, newlyGenerated = false; - if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") - { - (loadSucceed, needWriteDefaultSetting) = Settings.LoadFromFile(args[0]); - settingsIniPath = args[0]; + configResult = Settings.LoadFromFile(args[0]); + settingsIniPath = args[0]; - //remove ini configuration file from arguments array - List args_tmp = args.ToList(); - args_tmp.RemoveAt(0); - args = args_tmp.ToArray(); - } - else if (File.Exists("MinecraftClient.ini")) - { - (loadSucceed, needWriteDefaultSetting) = Settings.LoadFromFile("MinecraftClient.ini"); - } - else - { - loadSucceed = true; - needWriteDefaultSetting = true; - newlyGenerated = true; - } - - if (needWriteDefaultSetting) - { - Config.Main.Advanced.Language = Settings.GetDefaultGameLanguage(); - WriteBackSettings(false); - if (newlyGenerated) - ConsoleIO.WriteLineFormatted("§c" + Translations.mcc_settings_generated); - ConsoleIO.WriteLine(Translations.mcc_run_with_default_settings); - - // Only show the Sentry message if the DSN is not empty - // as Sentry will not be initialized if the DSN is empty - if (SentryDSN != string.Empty) { - ConsoleIO.WriteLine(Translations.mcc_sentry_logging); - } - } - else if (!loadSucceed) - { - ConsoleInteractive.ConsoleReader.StopReadThread(); - string command = " "; - while (command.Length > 0) - { - ConsoleIO.WriteLine(string.Empty); - ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_invaild_config, Config.Main.Advanced.InternalCmdChar.ToLogString())); - ConsoleIO.WriteLineFormatted(Translations.mcc_press_exit, acceptnewlines: true); - command = ConsoleInteractive.ConsoleReader.RequestImmediateInput().Trim(); - if (command.Length > 0) - { - if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' - && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) - command = command[1..]; - - if (command.StartsWith("exit") || command.StartsWith("quit")) - { - return; - } - else if (command.StartsWith("new")) - { - Config.Main.Advanced.Language = Settings.GetDefaultGameLanguage(); - WriteBackSettings(true); - ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_gen_new_config, settingsIniPath)); - return; - } - } - else - { - return; - } - } - return; - } - else - { - //Load external translation file. Should be called AFTER settings loaded - if (!Config.Main.Advanced.Language.StartsWith("en")) - ConsoleIO.WriteLine(string.Format(Translations.mcc_help_us_translate, Settings.TranslationProjectUrl)); - WriteBackSettings(true); // format - } - - if (!Config.Main.Advanced.EnableSentry) - _sentrySdk?.Dispose(); + List args_tmp = args.ToList(); + args_tmp.RemoveAt(0); + args = args_tmp.ToArray(); + } + else if (File.Exists("MinecraftClient.ini")) + { + configResult = Settings.LoadFromFile("MinecraftClient.ini"); + } + else + { + configResult = new Settings.ConfigLoadResult { Success = true, NeedWriteDefault = true }; + newlyGenerated = true; } + if (configResult.NeedWriteDefault) + Config.Main.Advanced.Language = Settings.GetDefaultGameLanguage(); + + if (!Config.Main.Advanced.EnableSentry) + _sentrySdk?.Dispose(); + + var startupState = new StartupState + { + ConfigResult = configResult, + NewlyGenerated = newlyGenerated, + SentryEnabled = SentryDSN != string.Empty, + }; + + // --- Determine console mode and initialize backend --- + if (!OperatingSystem.IsWindows()) + InstallCursesNativeResolver(); + + if (!ConsoleIO.BasicIO && Config.Console.General.ConsoleMode == ConsoleModeType.tui) + { + ConsoleIO.Backend?.Shutdown(); + try + { + var tuiBackend = new Tui.TuiConsoleBackend(); + ConsoleIO.Backend = tuiBackend; + tuiBackend.RunTuiMainLoop(args, startupState); + } + catch (Exception ex) + { + HandleTuiStartupFailure(ex); + } + return; + } + + // Classic mode: init backend, then print and process startup state. + if (!ConsoleIO.BasicIO) + { + ConsoleIO.Backend = new ClassicConsoleBackend(); + ConsoleIO.Backend.Init(); + + // Config deserialization triggers OnSettingUpdate before the backend + // exists, so console-specific settings (UseVT100ColorCode, colors, etc.) + // are never applied. Re-apply them now that the backend is ready. + Config.Console.OnSettingUpdate(); + } + + if (!ProcessStartupState(startupState)) + return; + + // Wait for this issue to be fixed before enabling it: https://github.com/Consolonia/Consolonia/issues/602 + // MaybePrintClassicModeTuiRecommendation(); + + RunStartupSequence(args); + } + + /// + /// Consolonia's Unix.Terminal uses [DllImport("libcoreclr.so")] to reach + /// dlopen/dlsym on .NET Core. The library ships a + /// SetDllImportResolver that maps libcoreclr.so to the current + /// process, but it is compiled under #if NET6_0 (exact TFM match) instead + /// of NET6_0_OR_GREATER, so it is dead code when the consuming project + /// targets net8.0+. On a self-contained single-file publish the physical + /// libcoreclr.so does not exist on the search path, causing a + /// DllNotFoundException that crashes the TUI. + /// + /// We work around this by registering our own resolver before any Consolonia + /// code runs: if any assembly asks for libcoreclr.so we return + /// (IntPtr)(-1) which the runtime interprets as "the current process". + /// + private static void InstallCursesNativeResolver() + { + AssemblyLoadContext.Default.ResolvingUnmanagedDll += (assembly, libraryName) => + libraryName == "libcoreclr.so" ? (IntPtr)(-1) : IntPtr.Zero; + } + + private static void HandleTuiStartupFailure(Exception exception) + { + Config.Console.General.ConsoleMode = ConsoleModeType.classic; + WriteBackSettings(enableBackup: false); + + ConsoleIO.Backend = new ClassicConsoleBackend(); + ConsoleIO.Backend.Init(); + Config.Console.OnSettingUpdate(); + + ConsoleIO.WriteLineFormatted("§c" + Translations.mcc_tui_startup_failed); + ConsoleIO.WriteLine(exception.ToString()); + ConsoleIO.WriteLineFormatted("§e" + Translations.mcc_report_issue); + ConsoleIO.WriteLineFormatted("§e" + Translations.mcc_tui_startup_fallback_classic); + } + + /// + /// Prints the application banner and processes the startup state collected before + /// the console backend was ready. Called once from classic mode or from TUI after + /// the view is initialized. + /// + /// True if startup can continue; false if config load failed and user chose to exit. + internal static bool ProcessStartupState(StartupState state) + { + if (Config.Console.General.Display_Icon_Banner && ConsoleIO.Backend is Tui.TuiConsoleBackend tuiBanner) + { + var view = tuiBanner.GetView(); + if (view is not null) + { + Avalonia.Threading.Dispatcher.UIThread.Post(() => + { + var panel = Tui.MccBannerPanelBuilder.Build(BuildInfo); + view.AppendControlToLog(panel); + }); + } + else + { + ShowClassicBanner(); + } + } + else + { + ShowClassicBanner(); + } + + var cfg = state.ConfigResult; + + if (cfg.NeedWriteDefault) + { + WriteBackSettings(false); + + if (cfg.IsLegacyUpgrade) + { + ConsoleIO.WriteLineFormatted("§c" + Translations.mcc_use_new_config); + ConsoleIO.WriteLineFormatted("§c" + string.Format(Translations.mcc_backup_old_config, cfg.LegacyBackupPath)); + } + + if (state.NewlyGenerated) + ConsoleIO.WriteLineFormatted("§c" + Translations.mcc_settings_generated); + + ConsoleIO.WriteLine(Translations.mcc_run_with_default_settings); + + if (state.SentryEnabled) + ConsoleIO.WriteLine(Translations.mcc_sentry_logging); + } + else if (!cfg.Success) + { + ConsoleIO.WriteLineFormatted("§c" + Translations.config_load_fail); + if (cfg.ErrorMessage is not null) + ConsoleIO.WriteLine(cfg.ErrorMessage); + HandleConfigLoadFailure(); + return false; + } + else + { + WriteBackSettings(true); + + if (!Config.Main.Advanced.Language.StartsWith("en")) + ConsoleIO.WriteLine(string.Format(Translations.mcc_help_us_translate, Settings.TranslationProjectUrl)); + } + + return true; + } + + private static void ShowClassicBanner() + { + ConsoleIO.WriteLine(string.Format(Translations.mcc_banner_classic, Version, MCLowestVersion, MCHighestVersion, "Github.com/MCCTeam")); + if (BuildInfo is not null) + ConsoleIO.WriteLineFormatted("§8" + BuildInfo); + if (Config.Main.Advanced.ShowGithubStarReminder) + ConsoleIO.WriteLineFormatted("§e" + Translations.mcc_banner_star_reminder); + } + + private static void MaybePrintClassicModeTuiRecommendation() + { + if (ConsoleIO.BasicIO + || Config.Console.General.ConsoleMode != ConsoleModeType.classic + || Console.IsInputRedirected) + { + return; + } + + char cmdChar = Config.Main.Advanced.InternalCmdChar.ToChar(); + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_console_mode_tui_recommendation, cmdChar)); + } + + /// + /// Handles a failed config load by prompting the user to fix or regenerate the config file. + /// + internal static void HandleConfigLoadFailure() + { + ConsoleIO.Backend?.StopReadThread(); + string command = " "; + while (command.Length > 0) + { + ConsoleIO.WriteLine(string.Empty); + ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_invaild_config, Config.Main.Advanced.InternalCmdChar.ToLogString())); + if (ConsoleIO.Backend is Tui.TuiConsoleBackend) + ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_use_quit_to_exit, Config.Main.Advanced.InternalCmdChar.ToLogString())); + else + ConsoleIO.WriteLineFormatted(Translations.mcc_press_exit, acceptnewlines: true); + command = ConsoleIO.ReadLine().Trim(); + if (command.Length > 0) + { + if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' + && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) + command = command[1..]; + + if (command.StartsWith("exit") || command.StartsWith("quit")) + { + return; + } + else if (command.StartsWith("new")) + { + Config.Main.Advanced.Language = Settings.GetDefaultGameLanguage(); + WriteBackSettings(true); + ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_gen_new_config, settingsIniPath)); + return; + } + } + else + { + return; + } + } + } + + /// + /// Runs the main startup sequence: CLI argument processing, auth, and connection. + /// Called from Main() for classic/basic mode, or from TuiConsoleBackend on a + /// background thread after the Avalonia UI loop has started. + /// + internal static void RunStartupSequence(string[] args) + { //Other command-line arguments if (args.Length >= 1) { @@ -360,7 +535,7 @@ namespace MinecraftClient ConsoleColorModeType.vt100_8bit)).Append(i); } sb.Append(ColorHelper.GetResetEscapeCode()).Append(']'); - ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb.ToString())); + ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb)); } { // Test 24 bit color StringBuilder sb = new(); @@ -374,7 +549,7 @@ namespace MinecraftClient ConsoleColorModeType.vt100_24bit)).Append(i); } sb.Append(ColorHelper.GetResetEscapeCode()).Append(']'); - ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb.ToString())); + ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb)); } } @@ -387,10 +562,12 @@ namespace MinecraftClient } // Setup exit cleaning code - ExitCleanUp.Add(() => { DoExit(0); }); + ExitCleanUp.Add(() => { DoExit(); }); //Asking the user to type in missing data such as Username and Password bool useBrowser = Config.Main.General.AccountType == LoginType.microsoft && Config.Main.General.Method == LoginMethod.browser; + bool useDeviceCode = Config.Main.General.AccountType == LoginType.microsoft && Config.Main.General.Method == LoginMethod.mcc; + bool skipPassword = useBrowser || useDeviceCode; if (string.IsNullOrWhiteSpace(InternalConfig.Account.Login) && !useBrowser) { ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.mcc_login_basic_io : Translations.mcc_login); @@ -402,7 +579,7 @@ namespace MinecraftClient } } InternalConfig.Username = InternalConfig.Account.Login; - if (string.IsNullOrWhiteSpace(InternalConfig.Account.Password) && !useBrowser && + if (string.IsNullOrWhiteSpace(InternalConfig.Account.Password) && !skipPassword && (Config.Main.Advanced.SessionCache == CacheType.none || !SessionCache.Contains(ToLowerIfNeed(InternalConfig.Account.Login)))) { RequestPassword(); @@ -476,7 +653,7 @@ namespace MinecraftClient if (result != ProtocolHandler.LoginResult.Success && string.IsNullOrWhiteSpace(InternalConfig.Account.Password) - && !(Config.Main.General.AccountType == LoginType.microsoft && Config.Main.General.Method == LoginMethod.browser)) + && !(Config.Main.General.AccountType == LoginType.microsoft)) RequestPassword(); } else ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_session_valid, session.PlayerName)); @@ -531,10 +708,10 @@ namespace MinecraftClient worldId = availableWorlds[worldIndex]; if (availableWorlds.Contains(worldId)) { - string RealmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, InternalConfig.Username, session.PlayerID, session.ID); - if (RealmsAddress != "") + string realmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, InternalConfig.Username, session.PlayerID, session.ID); + if (realmsAddress != "") { - addressInput = RealmsAddress; + addressInput = realmsAddress; isRealms = true; InternalConfig.MinecraftVersion = MCHighestVersion; } @@ -552,7 +729,7 @@ namespace MinecraftClient } else { - HandleFailure(Translations.error_realms_disabled, false, null); + HandleFailure(Translations.error_realms_disabled); return; } } @@ -565,7 +742,7 @@ namespace MinecraftClient if (InternalConfig.MinecraftVersion != "" && Settings.ToLowerIfNeed(InternalConfig.MinecraftVersion) != "auto") { - protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(InternalConfig.MinecraftVersion); + protocolversion = ProtocolHandler.MCVer2ProtocolVersion(InternalConfig.MinecraftVersion); if (protocolversion != 0) ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_use_version, InternalConfig.MinecraftVersion, protocolversion)); @@ -589,15 +766,16 @@ namespace MinecraftClient ConsoleIO.WriteLine(Translations.mcc_retrieve); if (!ProtocolHandler.GetServerInfo(InternalConfig.ServerIP, InternalConfig.ServerPort, ref protocolversion, ref forgeInfo)) { - HandleFailure(Translations.error_ping, true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); + HandleFailure(Translations.error_ping, true, ChatBot.DisconnectReason.ConnectionLost); return; } } if ((Config.Main.General.AccountType == LoginType.microsoft || Config.Main.General.AccountType == LoginType.yggdrasil) - && (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser) + && InternalConfig.Account.Password != "-" && Config.Signature.LoginWithSecureProfile - && protocolversion >= 759 /* 1.19 and above */) + && protocolversion >= 759 /* 1.19 and above */ + && !string.IsNullOrWhiteSpace(session.ID)) { // Load cached profile key from disk if necessary if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk) @@ -616,11 +794,11 @@ namespace MinecraftClient ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_profile_key_valid, session.PlayerName)); } - if (playerKeyPair == null || playerKeyPair.NeedRefresh()) + if (playerKeyPair is null || playerKeyPair.NeedRefresh()) { ConsoleIO.WriteLineFormatted(Translations.mcc_fetching_key, acceptnewlines: true); playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID, Config.Main.General.AccountType == LoginType.yggdrasil); - if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null) + if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair is not null) { KeysCache.Store(loginLower, playerKeyPair); } @@ -628,7 +806,7 @@ namespace MinecraftClient } //Force-enable Forge support? - if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo == null) + if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo is null) { if (ProtocolHandler.ProtocolMayForceForge(protocolversion)) { @@ -637,7 +815,7 @@ namespace MinecraftClient } else { - HandleFailure(Translations.error_forgeforce, true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); + HandleFailure(Translations.error_forgeforce, true, ChatBot.DisconnectReason.ConnectionLost); return; } } @@ -666,7 +844,7 @@ namespace MinecraftClient { // [SENTRY] SentrySdk.CaptureException(e); - + ConsoleIO.WriteLine(e.Message); ConsoleIO.WriteLine(e.StackTrace ?? ""); HandleFailure(); // Other error @@ -677,8 +855,7 @@ namespace MinecraftClient else { string failureMessage = Translations.error_login; - string failureReason = string.Empty; - failureReason = result switch + string failureReason = result switch { #pragma warning disable format // @formatter:off ProtocolHandler.LoginResult.AccountMigrated => Translations.error_login_migrated, @@ -703,7 +880,8 @@ namespace MinecraftClient /// public static void ReloadSettings(bool keepAccountAndServerSettings = false) { - if (Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings).Item1) + var result = Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings); + if (result.Success) ConsoleIO.WriteLine(string.Format(Translations.config_load, settingsIniPath)); } @@ -719,33 +897,89 @@ namespace MinecraftClient /// Disconnect the current client from the server and restart it /// /// Optional delay, in seconds, before restarting + /// Optional, keep account and server settings public static void Restart(int delaySeconds = 0, bool keepAccountAndServerSettings = false) { - ConsoleInteractive.ConsoleReader.StopReadThread(); - new Thread(new ThreadStart(delegate + TryRestart(delaySeconds, keepAccountAndServerSettings); + } + + internal static bool HasRestartPendingForAnotherThread + { + get { - if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); } - if (delaySeconds > 0) + lock (_restartLock) + return HasRestartPendingForAnotherThreadNoLock(); + } + } + + internal static bool TryRestart(int delaySeconds = 0, bool keepAccountAndServerSettings = false) + { + lock (_restartLock) + { + if (HasRestartPendingForAnotherThreadNoLock()) + return false; + + ConsoleIO.Backend?.StopReadThread(); + var thread = new Thread(new ThreadStart(delegate { - ConsoleIO.WriteLine(string.Format(Translations.mcc_restart_delay, delaySeconds)); - Thread.Sleep(delaySeconds * 1000); - } - ConsoleIO.WriteLine(Translations.mcc_restart); - ReloadSettings(keepAccountAndServerSettings); - InitializeClient(); - })).Start(); + try + { + if (client is not null) { client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt is not null) + { + if (ConsoleIO.Backend is not null) + ConsoleIO.Backend.OnInputChange -= ConsoleIO.OfflineAutocompleteHandler; + offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); + } + if (delaySeconds > 0) + { + ConsoleIO.WriteLine(string.Format(Translations.mcc_restart_delay, delaySeconds)); + Thread.Sleep(delaySeconds * 1000); + } + ConsoleIO.WriteLine(Translations.mcc_restart); + ReloadSettings(keepAccountAndServerSettings); + InitializeClient(); + } + finally + { + lock (_restartLock) + { + if (_restartThread == Thread.CurrentThread) + _restartThread = null; + } + } + })); + _restartThread = thread; + thread.Start(); + return true; + } + } + + private static bool HasRestartPendingForAnotherThreadNoLock() + { + return _restartThread is not null + && _restartThread.IsAlive + && _restartThread != Thread.CurrentThread; } public static void DoExit(int exitcode = 0) { - WriteBackSettings(true); - ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + WriteBackSettings(); ConsoleIO.WriteLineFormatted("§a" + string.Format(Translations.config_saving, settingsIniPath)); - if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); } - if (Config.Main.Advanced.PlayerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); } + if (client is not null) { client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt is not null) + { + if (ConsoleIO.Backend is not null) + ConsoleIO.Backend.OnInputChange -= ConsoleIO.OfflineAutocompleteHandler; + offlinePrompt.Item2.Cancel(); + if (Thread.CurrentThread != offlinePrompt.Item1) + offlinePrompt.Item1.Join(1000); + offlinePrompt = null; + ConsoleIO.Reset(); + } + if (Config.Main.Advanced.PlayerHeadAsIcon && OperatingSystem.IsWindows()) { ConsoleIcon.RevertToMCCIcon(); } + ConsoleIO.Backend?.Shutdown(); Environment.Exit(exitcode); } @@ -754,7 +988,7 @@ namespace MinecraftClient /// public static void Exit(int exitcode = 0) { - new Thread(new ThreadStart(() => { DoExit(exitcode); })).Start(); + new Thread(() => { DoExit(exitcode); }).Start(); } /// @@ -764,19 +998,29 @@ namespace MinecraftClient /// Error message to display and optionally pass to AutoRelog bot /// Specify if the error is related to an incompatible or unkown server version /// If set, the error message will be processed by the AutoRelog bot - public static void HandleFailure(string? errorMessage = null, bool versionError = false, ChatBots.AutoRelog.DisconnectReason? disconnectReason = null) + public static void HandleFailure(string? errorMessage = null, bool versionError = false, ChatBot.DisconnectReason? disconnectReason = null) { - if (!String.IsNullOrEmpty(errorMessage)) + bool autoRelogHandled = false; + + if (!string.IsNullOrEmpty(errorMessage)) { ConsoleIO.Reset(); - while (Console.KeyAvailable) - Console.ReadKey(true); + if (ConsoleIO.Backend is not Tui.TuiConsoleBackend) + { + try + { + while (Console.KeyAvailable) + Console.ReadKey(true); + } + catch { } + } ConsoleIO.WriteLine(errorMessage); if (disconnectReason.HasValue) { + autoRelogHandled = true; if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage)) - return; //AutoRelog is triggering a restart of the client + return; } } @@ -785,7 +1029,7 @@ namespace MinecraftClient if (versionError) { ConsoleIO.WriteLine(Translations.mcc_server_version); - InternalConfig.MinecraftVersion = ConsoleInteractive.ConsoleReader.RequestImmediateInput(); + InternalConfig.MinecraftVersion = ConsoleIO.ReadLine(); if (InternalConfig.MinecraftVersion != "") { useMcVersionOnce = true; @@ -794,14 +1038,17 @@ namespace MinecraftClient } } - if (disconnectReason.HasValue) { - if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage!)) - return; //AutoRelog is triggering a restart of the client, don't turn on the offline prompt - } - - if (offlinePrompt == null) + if (!autoRelogHandled && disconnectReason.HasValue) { - ConsoleInteractive.ConsoleReader.StopReadThread(); + if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage!)) + return; + } + + if (offlinePrompt is null) + { + ConsoleIO.Backend?.StopReadThread(); + if (ConsoleIO.Backend is not null) + ConsoleIO.Backend.OnInputChange += ConsoleIO.OfflineAutocompleteHandler; var cancellationTokenSource = new CancellationTokenSource(); offlinePrompt = new(new Thread(new ThreadStart(delegate @@ -810,72 +1057,67 @@ namespace MinecraftClient string command = " "; ConsoleIO.WriteLine(string.Empty); ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_disconnected, Config.Main.Advanced.InternalCmdChar.ToLogString())); - ConsoleIO.WriteLineFormatted(Translations.mcc_press_exit, acceptnewlines: true); + if (ConsoleIO.Backend is Tui.TuiConsoleBackend) + ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_use_quit_to_exit, Config.Main.Advanced.InternalCmdChar.ToLogString())); + else + ConsoleIO.WriteLineFormatted(Translations.mcc_press_exit, acceptnewlines: true); while (!cancellationTokenSource.IsCancellationRequested) { if (exitThread) return; - while (command.Length > 0) + command = ConsoleIO.ReadLine().Trim(); + + if (command.Length == 0) { - if (cancellationTokenSource.IsCancellationRequested) - return; - - command = ConsoleInteractive.ConsoleReader.RequestImmediateInput().Trim(); - if (command.Length > 0) - { - string message = ""; - - if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' - && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) - command = command[1..]; - - if (command.StartsWith("reco")) - { - message = Commands.Reco.DoReconnect(Config.AppVar.ExpandVars(command)); - if (message == "") - { - exitThread = true; - break; - } - } - else if (command.StartsWith("connect")) - { - message = Commands.Connect.DoConnect(Config.AppVar.ExpandVars(command)); - if (message == "") - { - exitThread = true; - break; - } - } - else if (command.StartsWith("exit") || command.StartsWith("quit")) - { - message = Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); - } - else if (command.StartsWith("help")) - { - ConsoleIO.WriteLineFormatted("§8MCC: " + - Config.Main.Advanced.InternalCmdChar.ToLogString() + - new Commands.Reco().GetCmdDescTranslated()); - ConsoleIO.WriteLineFormatted("§8MCC: " + - Config.Main.Advanced.InternalCmdChar.ToLogString() + - new Commands.Connect().GetCmdDescTranslated()); - } - else - ConsoleIO.WriteLineFormatted(string.Format(Translations.icmd_unknown, command.Split(' ')[0])); - - if (message != "") - ConsoleIO.WriteLineFormatted("§8MCC: " + message); - } - else - { + if (ConsoleIO.Backend is not Tui.TuiConsoleBackend) Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); - } + continue; } - if (exitThread) - return; + string message = ""; + + if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' + && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) + command = command[1..]; + + if (command.StartsWith("reco")) + { + message = Commands.Reco.DoReconnect(Config.AppVar.ExpandVars(command)); + if (message == "") + { + exitThread = true; + continue; + } + } + else if (command.StartsWith("connect")) + { + message = Commands.Connect.DoConnect(Config.AppVar.ExpandVars(command)); + if (message == "") + { + exitThread = true; + continue; + } + } + else if (command.StartsWith("exit") || command.StartsWith("quit")) + { + message = Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); + } + else if (command.StartsWith("help")) + { + ConsoleIO.WriteLineFormatted("§8MCC: " + + Config.Main.Advanced.InternalCmdChar.ToLogString() + + new Commands.Reco().GetCmdDescTranslated()); + ConsoleIO.WriteLineFormatted("§8MCC: " + + Config.Main.Advanced.InternalCmdChar.ToLogString() + + new Commands.Connect().GetCmdDescTranslated()); + } + else + ConsoleIO.WriteLineFormatted(string.Format(Translations.icmd_unknown, command.Split(' ')[0])); + + if (message != "") + ConsoleIO.WriteLineFormatted("§8MCC: " + message); } })), cancellationTokenSource); offlinePrompt.Item1.Start(); @@ -905,7 +1147,7 @@ namespace MinecraftClient /// public static Type[] GetTypesInNamespace(string nameSpace, Assembly? assembly = null) { - if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } + if (assembly is null) { assembly = Assembly.GetExecutingAssembly(); } return assembly.GetTypes().Where(t => string.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); } diff --git a/MinecraftClient/Protocol/DataTypeGenerator.cs b/MinecraftClient/Protocol/DataTypeGenerator.cs index af5adce8..d8315b81 100644 --- a/MinecraftClient/Protocol/DataTypeGenerator.cs +++ b/MinecraftClient/Protocol/DataTypeGenerator.cs @@ -21,13 +21,13 @@ namespace MinecraftClient.Protocol /// private static Dictionary LoadRegistry(string registriesJsonFile, string jsonRegistryName) { - Json.JSONData rawJson = Json.ParseJson(File.ReadAllText(registriesJsonFile)); - Json.JSONData rawRegistry = rawJson.Properties[jsonRegistryName].Properties["entries"]; + var rawJson = Json.ParseJson(File.ReadAllText(registriesJsonFile)); + var rawRegistry = rawJson![jsonRegistryName]!["entries"]!.AsObject(); Dictionary registry = new(); - foreach (KeyValuePair entry in rawRegistry.Properties) + foreach (var entry in rawRegistry) { - int entryId = int.Parse(entry.Value.Properties["protocol_id"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture); + int entryId = int.Parse(entry.Value!["protocol_id"].GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); //minecraft:item_name => ItemName string entryName = String.Concat( diff --git a/MinecraftClient/Protocol/Dialogs/DialogNbtParser.cs b/MinecraftClient/Protocol/Dialogs/DialogNbtParser.cs new file mode 100644 index 00000000..2636b5be --- /dev/null +++ b/MinecraftClient/Protocol/Dialogs/DialogNbtParser.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using MinecraftClient.Dialogs; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Dialogs; + +public sealed class DialogNbtParser +{ + public DialogDefinition Parse(Dictionary nbt) + { + var type = NormalizeType(GetString(nbt, "type") ?? "minecraft:notice"); + var common = ParseCommon(nbt, type); + var actions = new List(); + DialogActionDefinition? cancelAction = null; + var columns = GetInt(nbt, "columns", 1); + var buttonWidth = GetInt(nbt, "button_width", 150); + + switch (type) + { + case "minecraft:notice": + var noticeAction = ParseButton(nbt, "action", 1); + actions.Add(noticeAction ?? new DialogButton(1, Translations.dialog_action_ok, null)); + cancelAction = actions[0].Action; + break; + + case "minecraft:confirmation": + AddIfNotNull(actions, ParseButton(nbt, "yes", 1)); + AddIfNotNull(actions, ParseButton(nbt, "no", 2)); + cancelAction = actions.Count >= 2 ? actions[1].Action : null; + break; + + case "minecraft:multi_action": + actions.AddRange(ParseButtonList(GetValue(nbt, "actions"))); + cancelAction = ParseButton(nbt, "exit_action", 0)?.Action; + break; + + case "minecraft:dialog_list": + actions.AddRange(ParseDialogListActions(GetValue(nbt, "dialogs"))); + cancelAction = ParseButton(nbt, "exit_action", 0)?.Action; + break; + + case "minecraft:server_links": + cancelAction = ParseButton(nbt, "exit_action", 0)?.Action; + break; + } + + return new DialogDefinition( + type, + common.Title, + common.ExternalTitle, + common.CanCloseWithEscape, + common.Pause, + common.AfterAction, + common.Body, + common.Inputs, + actions, + cancelAction, + columns, + buttonWidth); + } + + public DialogDefinition? TryParse(Dictionary? nbt) + { + return nbt is null ? null : Parse(nbt); + } + + private static DialogCommon ParseCommon(Dictionary nbt, string type) + { + var title = ParseComponent(GetValue(nbt, "title")); + var externalTitle = nbt.TryGetValue("external_title", out var externalTitleValue) + ? ParseComponent(externalTitleValue) + : null; + var canCloseWithEscape = GetBool(nbt, "can_close_with_escape", true); + var pause = GetBool(nbt, "pause", true); + var afterAction = ParseAfterAction(GetString(nbt, "after_action") ?? "close"); + var body = ParseBody(GetValue(nbt, "body")); + var inputs = ParseInputs(GetValue(nbt, "inputs")); + + return new DialogCommon(title, externalTitle, canCloseWithEscape, pause, afterAction, body, inputs); + } + + private static IReadOnlyList ParseBody(object? value) + { + if (value is null) + return []; + + List body = []; + foreach (var item in Enumerate(value)) + { + if (item is Dictionary compound) + { + var type = NormalizeType(GetString(compound, "type") ?? "minecraft:plain_message"); + if (type == "minecraft:item") + { + var description = compound.TryGetValue("description", out var desc) + ? ParsePlainMessage(desc) + : string.Empty; + body.Add(new DialogBody(DialogBodyKind.Item, string.IsNullOrWhiteSpace(description) ? Translations.dialog_item_body : description, type)); + continue; + } + + body.Add(new DialogBody(DialogBodyKind.PlainMessage, ParsePlainMessage(compound), type)); + continue; + } + + body.Add(new DialogBody(DialogBodyKind.PlainMessage, ParseComponent(item), "minecraft:plain_message")); + } + + return body; + } + + private static string ParsePlainMessage(object? value) + { + if (value is Dictionary compound && compound.TryGetValue("contents", out var contents)) + return ParseComponent(contents); + + return ParseComponent(value); + } + + private static IReadOnlyList ParseInputs(object? value) + { + if (value is null) + return []; + + List inputs = []; + foreach (var item in Enumerate(value)) + { + if (item is not Dictionary inputData) + continue; + + var key = GetString(inputData, "key"); + if (string.IsNullOrWhiteSpace(key)) + continue; + + var control = inputData.TryGetValue("control", out var controlValue) && controlValue is Dictionary controlData + ? controlData + : inputData; + + var type = NormalizeType(GetString(control, "type") ?? "minecraft:text"); + inputs.Add(type switch + { + "minecraft:boolean" => ParseBooleanInput(key, type, control), + "minecraft:number_range" => ParseNumberInput(key, type, control), + "minecraft:single_option" => ParseOptionInput(key, type, control), + "minecraft:text" => ParseTextInput(key, type, control), + _ => new DialogInput(key, DialogInputKind.Unknown, ParseComponent(GetValue(control, "label")), string.Empty, Type: type) + }); + } + + return inputs; + } + + private static DialogInput ParseTextInput(string key, string type, Dictionary control) + { + return new DialogInput( + key, + DialogInputKind.Text, + ParseComponent(GetValue(control, "label")), + GetString(control, "initial") ?? string.Empty, + MaxLength: GetInt(control, "max_length", 32), + LabelVisible: GetBool(control, "label_visible", true), + Multiline: control.ContainsKey("multiline"), + Type: type); + } + + private static DialogInput ParseBooleanInput(string key, string type, Dictionary control) + { + var initial = GetBool(control, "initial", false); + return new DialogInput( + key, + DialogInputKind.Boolean, + ParseComponent(GetValue(control, "label")), + initial ? "true" : "false", + OnTrue: GetString(control, "on_true") ?? "true", + OnFalse: GetString(control, "on_false") ?? "false", + Type: type); + } + + private static DialogInput ParseOptionInput(string key, string type, Dictionary control) + { + var options = ParseOptions(GetValue(control, "options")); + var initial = options.FirstOrDefault(static option => option.Initial)?.Id + ?? options.FirstOrDefault()?.Id + ?? string.Empty; + return new DialogInput( + key, + DialogInputKind.SingleOption, + ParseComponent(GetValue(control, "label")), + initial, + LabelVisible: GetBool(control, "label_visible", true), + Options: options, + Type: type); + } + + private static DialogInput ParseNumberInput(string key, string type, Dictionary control) + { + var range = control.TryGetValue("range_info", out var rangeValue) && rangeValue is Dictionary rangeData + ? rangeData + : control; + var start = GetFloat(range, "start", 0); + var end = GetFloat(range, "end", 1); + var initial = TryGetFloat(range, "initial") ?? ((start + end) / 2F); + return new DialogInput( + key, + DialogInputKind.NumberRange, + ParseComponent(GetValue(control, "label")), + NumberToString(initial), + Start: start, + End: end, + InitialNumber: initial, + Step: TryGetFloat(range, "step"), + Type: type); + } + + private static IReadOnlyList ParseOptions(object? value) + { + if (value is null) + return []; + + List options = []; + foreach (var item in Enumerate(value)) + { + if (item is string id) + { + options.Add(new DialogOption(id, id, false)); + continue; + } + + if (item is Dictionary option) + { + var optionId = GetString(option, "id"); + if (optionId is null) + continue; + + var display = option.TryGetValue("display", out var displayValue) + ? ParseComponent(displayValue) + : optionId; + options.Add(new DialogOption(optionId, display, GetBool(option, "initial", false))); + } + } + + return options; + } + + private static List ParseButtonList(object? value) + { + List buttons = []; + var index = 1; + foreach (var item in Enumerate(value)) + { + if (item is Dictionary buttonData) + buttons.Add(ParseButton(buttonData, index++) ?? new DialogButton(index - 1, Translations.dialog_action_unnamed, null)); + } + + return buttons; + } + + private static IEnumerable ParseDialogListActions(object? value) + { + List buttons = []; + var index = 1; + foreach (var item in Enumerate(value)) + { + switch (item) + { + case string tag when tag.StartsWith('#'): + buttons.Add(new DialogButton(index++, tag, new DialogActionDefinition(DialogActionKind.Unknown, Type: "dialog_tag"))); + break; + case string resource: + buttons.Add(new DialogButton(index++, resource, new DialogActionDefinition(DialogActionKind.ShowDialog, Value: resource, Type: "dialog_reference_name"))); + break; + case Dictionary dialog: + var nested = new DialogNbtParser().Parse(dialog); + buttons.Add(new DialogButton(index++, nested.DisplayTitle(), new DialogActionDefinition(DialogActionKind.ShowDialog, NestedDialog: nested))); + break; + } + } + + return buttons; + } + + private static DialogButton? ParseButton(Dictionary owner, string key, int index) + { + return owner.TryGetValue(key, out var value) && value is Dictionary data + ? ParseButton(data, index) + : null; + } + + private static DialogButton? ParseButton(Dictionary data, int index) + { + var label = data.TryGetValue("label", out var labelValue) + ? ParseComponent(labelValue) + : Translations.dialog_action_unnamed; + var action = data.TryGetValue("action", out var actionValue) && actionValue is Dictionary actionData + ? ParseAction(actionData) + : null; + return new DialogButton(index, label, action); + } + + private static DialogActionDefinition ParseAction(Dictionary action) + { + var type = NormalizeType(GetString(action, "type") ?? GetString(action, "action") ?? "minecraft:none"); + return type switch + { + "minecraft:run_command" => new DialogActionDefinition(DialogActionKind.RunCommand, Value: GetString(action, "command"), Type: type), + "minecraft:dynamic/run_command" => new DialogActionDefinition(DialogActionKind.RunCommand, Value: GetString(action, "template"), Type: type), + "minecraft:custom" => new DialogActionDefinition(DialogActionKind.CustomClick, Id: GetString(action, "id"), Payload: GetCompound(action, "payload"), Type: type), + "minecraft:dynamic/custom" => new DialogActionDefinition(DialogActionKind.CustomClick, Id: GetString(action, "id"), Payload: GetCompound(action, "additions"), Type: type), + "minecraft:open_url" => new DialogActionDefinition(DialogActionKind.OpenUrl, Value: GetString(action, "url"), Type: type), + "minecraft:suggest_command" => new DialogActionDefinition(DialogActionKind.SuggestCommand, Value: GetString(action, "command"), Type: type), + "minecraft:copy_to_clipboard" => new DialogActionDefinition(DialogActionKind.CopyToClipboard, Value: GetString(action, "value"), Type: type), + "minecraft:show_dialog" => ParseShowDialogAction(action, type), + _ => new DialogActionDefinition(DialogActionKind.Unknown, Type: type) + }; + } + + private static DialogActionDefinition ParseShowDialogAction(Dictionary action, string type) + { + if (!action.TryGetValue("dialog", out var value)) + return new DialogActionDefinition(DialogActionKind.ShowDialog, Type: type); + + if (value is Dictionary dialogData) + return new DialogActionDefinition(DialogActionKind.ShowDialog, NestedDialog: new DialogNbtParser().Parse(dialogData), Type: type); + + if (value is int protocolId) + return new DialogActionDefinition(DialogActionKind.ShowDialog, DialogReferenceId: protocolId, Type: type); + + return new DialogActionDefinition(DialogActionKind.ShowDialog, Value: value.ToString(), Type: type); + } + + private static DialogAfterAction ParseAfterAction(string value) + { + return value switch + { + "none" => DialogAfterAction.None, + "wait_for_response" => DialogAfterAction.WaitForResponse, + _ => DialogAfterAction.Close + }; + } + + private static string ParseComponent(object? value) + { + if (value is null) + return string.Empty; + + try + { + return value switch + { + Dictionary compound => ChatParser.ParseText(compound), + string text when text.StartsWith('{') || text.StartsWith('[') => ChatParser.ParseText(text), + string text => text, + _ => value.ToString() ?? string.Empty + }; + } + catch + { + return value.ToString() ?? string.Empty; + } + } + + private static IEnumerable Enumerate(object? value) + { + if (value is null) + yield break; + + if (value is object[] array) + { + foreach (var item in array) + yield return item; + yield break; + } + + yield return value; + } + + private static object? GetValue(Dictionary data, string key) + { + return data.TryGetValue(key, out var value) ? value : null; + } + + private static string? GetString(Dictionary data, string key) + { + return data.TryGetValue(key, out var value) ? value as string ?? value.ToString() : null; + } + + private static Dictionary? GetCompound(Dictionary data, string key) + { + return data.TryGetValue(key, out var value) && value is Dictionary compound ? compound : null; + } + + private static bool GetBool(Dictionary data, string key, bool fallback) + { + if (!data.TryGetValue(key, out var value)) + return fallback; + + return value switch + { + bool boolean => boolean, + byte number => number != 0, + sbyte number => number != 0, + int number => number != 0, + string text when bool.TryParse(text, out var parsed) => parsed, + _ => fallback + }; + } + + private static int GetInt(Dictionary data, string key, int fallback) + { + if (!data.TryGetValue(key, out var value)) + return fallback; + + return value switch + { + byte number => number, + short number => number, + int number => number, + long number => (int)number, + string text when int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed) => parsed, + _ => fallback + }; + } + + private static float GetFloat(Dictionary data, string key, float fallback) + { + return TryGetFloat(data, key) ?? fallback; + } + + private static float? TryGetFloat(Dictionary data, string key) + { + if (!data.TryGetValue(key, out var value)) + return null; + + return value switch + { + float number => number, + double number => (float)number, + int number => number, + long number => number, + string text when float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed) => parsed, + _ => null + }; + } + + private static string NormalizeType(string type) + { + return type.Contains(':', StringComparison.Ordinal) ? type : "minecraft:" + type; + } + + private static void AddIfNotNull(List buttons, DialogButton? button) + { + if (button is not null) + buttons.Add(button); + } + + private static string NumberToString(float value) + { + var integer = (int)value; + return integer == value + ? integer.ToString(CultureInfo.InvariantCulture) + : value.ToString(CultureInfo.InvariantCulture); + } + + private sealed record DialogCommon( + string Title, + string? ExternalTitle, + bool CanCloseWithEscape, + bool Pause, + DialogAfterAction AfterAction, + IReadOnlyList Body, + IReadOnlyList Inputs); +} diff --git a/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesIn.cs b/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesIn.cs index c9ca6e59..d140bbcc 100644 --- a/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesIn.cs +++ b/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesIn.cs @@ -2,16 +2,26 @@ namespace MinecraftClient.Protocol.Handlers; public enum ConfigurationPacketTypesIn { - PluginMessage, + CookieRequest, + CustomReportDetails, Disconnect, + FeatureFlags, FinishConfiguration, KeepAlive, + KnownDataPacks, Ping, + PluginMessage, RegistryData, - ResourcePack, RemoveResourcePack, - FeatureFlags, + ResetChat, + ResourcePack, + ServerLinks, + StoreCookie, + Transfer, UpdateTags, - + ClearDialog, // Added in 1.21.6 + ShowDialog, // Added in 1.21.6 + CodeOfConduct, // Added in 1.21.9 + Unknown -} \ No newline at end of file +} diff --git a/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesOut.cs b/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesOut.cs index f951a38d..1bb492d2 100644 --- a/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesOut.cs +++ b/MinecraftClient/Protocol/Handlers/ConfigurationPacketTypesOut.cs @@ -8,6 +8,10 @@ public enum ConfigurationPacketTypesOut KeepAlive, Pong, ResourcePackResponse, - + CookieResponse, + KnownDataPacks, + CustomClickAction, // Added in 1.21.6 + AcceptCodeOfConduct, // Added in 1.21.9 + Unknown } \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index dd109709..a3e95cb4 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; @@ -6,6 +6,8 @@ using MinecraftClient.Inventory; using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Mapping; using MinecraftClient.Mapping.EntityPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; using MinecraftClient.Protocol.Message; namespace MinecraftClient.Protocol.Handlers @@ -13,21 +15,17 @@ namespace MinecraftClient.Protocol.Handlers /// /// Handle data types encoding / decoding /// - class DataTypes + public class DataTypes(int protocol) { /// /// Protocol version for adjusting data types /// - private readonly int protocolversion; + private readonly int protocolversion = protocol; /// - /// Initialize a new DataTypes instance + /// Protocol version used to adjust wire encodings. /// - /// Protocol version - public DataTypes(int protocol) - { - protocolversion = protocol; - } + public int ProtocolVersion => protocolversion; /// /// Read some data from a cache of bytes and remove it from the cache @@ -413,46 +411,187 @@ namespace MinecraftClient.Protocol.Handlers return ReadNextNbt(cache, true); } + public object? ReadNextNbtTag(Queue cache) + { + var tagType = ReadNextByte(cache); + return tagType == 0 ? null : ReadNbtField(cache, tagType); + } + + /// + /// Read an ItemStackTemplate (26.1+) from a cache of bytes. + /// Unlike ItemStack, this uses item-first encoding: item_id, count, DataComponentPatch. + /// ItemStackTemplate is always non-empty (no count=0 sentinel). + /// + public Item ReadNextItemStackTemplate(Queue cache, ItemPalette itemPalette) + { + var itemId = ReadNextVarInt(cache); + var itemCount = ReadNextVarInt(cache); + var item = new Item(itemPalette.FromId(itemId), itemCount, null); + + var numberOfComponentsToAdd = ReadNextVarInt(cache); + var numberofComponentsToRemove = ReadNextVarInt(cache); + var structuredComponentHandler = new StructuredComponentsHandler(protocolversion, this, itemPalette); + var strcturedComponentsToAdd = new List(numberOfComponentsToAdd); + + for (var i = 0; i < numberOfComponentsToAdd; i++) + { + var componentTypeId = ReadNextVarInt(cache); + strcturedComponentsToAdd.Add(structuredComponentHandler.Parse(componentTypeId, cache)); + } + + for (var i = 0; i < numberofComponentsToRemove; i++) + ReadNextVarInt(cache); + + if (strcturedComponentsToAdd.Count > 0) + item.Components = strcturedComponentsToAdd; + + return item; + } + /// /// Read a single item slot from a cache of bytes and remove it from the cache /// /// The item that was read or NULL for an empty slot public Item? ReadNextItemSlot(Queue cache, ItemPalette itemPalette) { - // MC 1.13.2 and greater - if (protocolversion >= Protocol18Handler.MC_1_13_Version) + var itemId = -1; + var itemCount = 0; + var nbt = null as Dictionary; + var item = null as Item; + var strcturedComponentsToAdd = new List(); + + switch (protocolversion) { - var itemPresent = ReadNextBool(cache); + // MC 1.13.2 and greater + case >= Protocol18Handler.MC_1_20_6_Version: + itemCount = ReadNextVarInt(cache); - if (!itemPresent) - return null; + if (itemCount <= 0) return null; - var itemId = ReadNextVarInt(cache); + itemId = ReadNextVarInt(cache); + item = new Item(itemPalette.FromId(itemId), itemCount, null); - if (itemId == -1) - return null; + var numberOfComponentsToAdd = ReadNextVarInt(cache); + var numberofComponentsToRemove = ReadNextVarInt(cache); + var structuredComponentHandler = new StructuredComponentsHandler(protocolversion, this, itemPalette); + var parsedComponents = new List(numberOfComponentsToAdd); - var type = itemPalette.FromId(itemId); - var itemCount = ReadNextByte(cache); - var nbt = ReadNextNbt(cache); - return new Item(type, itemCount, nbt); + for (var i = 0; i < numberOfComponentsToAdd; i++) + { + var componentTypeId = ReadNextVarInt(cache); + var componentName = structuredComponentHandler.GetComponentName(componentTypeId); + parsedComponents.Add($"{i}:{componentTypeId}:{componentName}:next={GetQueuePreview(cache, 24)}"); + + try + { + strcturedComponentsToAdd.Add(structuredComponentHandler.Parse(componentTypeId, cache)); + } + catch (Exception ex) + { + var preview = GetQueuePreview(cache, 48); + throw new System.IO.InvalidDataException( + $"Failed to decode item component {componentTypeId} ({componentName}) for itemId {itemId}, " + + $"itemCount {itemCount}, addCount {numberOfComponentsToAdd}, removeCount {numberofComponentsToRemove}, " + + $"componentIndex {i}, remainingBytes {cache.Count}, nextBytes {preview}, " + + $"componentTrace [{string.Join(" | ", parsedComponents)}].", + ex); + } + } + + for (var i = 0; i < numberofComponentsToRemove; i++) + ReadNextVarInt(cache); + + if (strcturedComponentsToAdd.Count > 0) + item.Components = strcturedComponentsToAdd; + + return item; + case >= Protocol18Handler.MC_1_13_2_Version: + { + var itemPresent = ReadNextBool(cache); + + if (!itemPresent) + return null; + + itemId = ReadNextVarInt(cache); + + if (itemId == -1) + return null; + + var type = itemPalette.FromId(itemId); + itemCount = ReadNextByte(cache); + nbt = ReadNextNbt(cache); + return new Item(type, itemCount, itemId, nbt); + } + case >= Protocol18Handler.MC_1_13_Version: + { + itemId = ReadNextShort(cache); + + if (itemId == -1) + return null; + + var type = itemPalette.FromId(itemId); + itemCount = ReadNextByte(cache); + nbt = ReadNextNbt(cache); + return new Item(type, itemCount, itemId, nbt); + } + default: + { + itemId = ReadNextShort(cache); + + if (itemId == -1) + return null; + + itemCount = ReadNextByte(cache); + var data = ReadNextShort(cache); + nbt = ReadNextNbt(cache); + + // For 1.8 - 1.12.2 we combine Item Id and Item Data/Damage to a single value using: (id << 16) | data + return new Item(itemPalette.FromId((itemId << 16) | (ushort)data), itemCount, data, nbt); + } } - else + } + + private void ReadNextDetail(Queue cache) + { + var potionEffectId = ReadNextVarInt(cache); + + // Details + var potionEffectAmplifier = ReadNextVarInt(cache); + var duration = ReadNextVarInt(cache); // -1 for infinite + var ambient = ReadNextBool(cache); + var showParticles = ReadNextBool(cache); + var showIcon = ReadNextBool(cache); + var hasHiddenEffect = ReadNextBool(cache); + + if (hasHiddenEffect) { - var itemId = ReadNextShort(cache); - - if (itemId == -1) - return null; - - var itemCount = ReadNextByte(cache); - var data = ReadNextShort(cache); - var nbt = ReadNextNbt(cache); - - // For 1.8 - 1.12.2 we combine Item Id and Item Data/Damage to a single value using: (id << 16) | data - return new Item(itemPalette.FromId((itemId << 16) | (ushort)data), itemCount, data, nbt); + ReadNextDetail(cache); } } + private static string GetQueuePreview(Queue cache, int maxBytes) + { + if (cache.Count == 0) + return ""; + + var bytes = cache.ToArray(); + var length = Math.Min(bytes.Length, maxBytes); + var preview = new StringBuilder(length * 3); + + for (var i = 0; i < length; i++) + { + if (i > 0) + preview.Append(' '); + + preview.Append(bytes[i].ToString("X2")); + } + + if (bytes.Length > maxBytes) + preview.Append(" ..."); + + return preview.ToString(); + } + /// /// Read entity information from a cache of bytes and remove it from the cache /// @@ -504,11 +643,32 @@ namespace MinecraftClient.Protocol.Handlers int data = -1; byte entityPitch, entityYaw; - if (living) + if (protocolversion >= Protocol18Handler.MC_1_21_9_Version) + { + // 1.21.9+: LpVec3 movement before angles, unified format + ReadNextLpVec3(cache); // Movement (LpVec3) + entityPitch = ReadNextByte(cache); // xRot + entityYaw = ReadNextByte(cache); // yRot + ReadNextByte(cache); // yHeadRot + data = ReadNextVarInt(cache); // Data + } + else if (living) { entityYaw = ReadNextByte(cache); // Yaw entityPitch = ReadNextByte(cache); // Pitch entityPitch = ReadNextByte(cache); // Head Pitch + + // Velocity (3 shorts) + if (protocolversion < Protocol18Handler.MC_1_9_Version) + { + // no velocity for living entities in <1.9 + } + else + { + ReadNextShort(cache); + ReadNextShort(cache); + ReadNextShort(cache); + } } else { @@ -522,27 +682,29 @@ namespace MinecraftClient.Protocol.Handlers data = protocolversion >= Protocol18Handler.MC_1_19_Version ? ReadNextVarInt(cache) : ReadNextInt(cache); - } - // In 1.8 those 3 fields for Velocity are optional - if (protocolversion < Protocol18Handler.MC_1_9_Version) - { - if (data != 0) + // Velocity (3 shorts) + if (protocolversion < Protocol18Handler.MC_1_9_Version) + { + if (data != 0) + { + ReadNextShort(cache); + ReadNextShort(cache); + ReadNextShort(cache); + } + } + else { ReadNextShort(cache); ReadNextShort(cache); ReadNextShort(cache); } } - else - { - ReadNextShort(cache); - ReadNextShort(cache); - ReadNextShort(cache); - } - return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ), entityYaw, entityPitch, + var entity = new Entity(entityID, entityType, new Location(entityX, entityY, entityZ), entityYaw, entityPitch, data); + entity.UUID = entityUUID; + return entity; } /// @@ -674,188 +836,322 @@ namespace MinecraftClient.Protocol.Handlers public Dictionary ReadNextMetadata(Queue cache, ItemPalette itemPalette, EntityMetadataPalette metadataPalette) { - Dictionary data = new(); - byte key = ReadNextByte(cache); - byte terminteValue = protocolversion <= Protocol18Handler.MC_1_8_Version - ? (byte)0x7f // 1.8 (https://wiki.vg/index.php?title=Entity_metadata&oldid=6220#Entity_Metadata_Format) - : (byte)0xff; // 1.9+ - - while (key != terminteValue) + try { - int typeId = protocolversion <= Protocol18Handler.MC_1_8_Version - ? key >> 5 // 1.8 - : ReadNextVarInt(cache); // 1.9+ + Dictionary data = new(); + byte key = ReadNextByte(cache); + byte terminteValue = protocolversion <= Protocol18Handler.MC_1_8_Version + ? (byte)0x7f // 1.8 (https://wiki.vg/index.php?title=Entity_metadata&oldid=6220#Entity_Metadata_Format) + : (byte)0xff; // 1.9+ - EntityMetaDataType type; - try + while (key != terminteValue) { - type = metadataPalette.GetDataType(typeId); - } - catch (KeyNotFoundException) - { - throw new System.IO.InvalidDataException("Unknown Metadata Type ID " + typeId + - ". Is this up to date for new MC Version?"); - } + int typeId = protocolversion <= Protocol18Handler.MC_1_8_Version + ? key >> 5 // 1.8 + : ReadNextVarInt(cache); // 1.9+ - if (protocolversion <= Protocol18Handler.MC_1_8_Version) - key = (byte)(key & 0x1f); + EntityMetaDataType type; + try + { + type = metadataPalette.GetDataType(typeId); + } + catch (KeyNotFoundException) + { + throw new System.IO.InvalidDataException("Unknown Metadata Type ID " + typeId + + ". Is this up to date for new MC Version?"); + } - // Value's data type is depended on Type - object? value = null; + if (protocolversion <= Protocol18Handler.MC_1_8_Version) + key = (byte)(key & 0x1f); - switch (type) - { - case EntityMetaDataType.Short: // 1.8 only - value = ReadNextShort(cache); - break; - case EntityMetaDataType.Int: // 1.8 only - value = ReadNextInt(cache); - break; - case EntityMetaDataType.Vector3Int: // 1.8 only - value = new List() - { - ReadNextInt(cache), - ReadNextInt(cache), - ReadNextInt(cache), - }; - break; - case EntityMetaDataType.Byte: // byte - value = ReadNextByte(cache); - break; - case EntityMetaDataType.VarInt: // VarInt - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.VarLong: // Long - value = ReadNextVarLong(cache); - break; - case EntityMetaDataType.Float: // Float - value = ReadNextFloat(cache); - break; - case EntityMetaDataType.String: // String - value = ReadNextString(cache); - break; - case EntityMetaDataType.Chat: // Chat - value = ReadNextChat(cache); - break; - case EntityMetaDataType.OptionalChat: // Optional Chat - if (ReadNextBool(cache)) - value = ReadNextChat(cache); - break; - case EntityMetaDataType.Slot: // Slot - value = ReadNextItemSlot(cache, itemPalette); - break; - case EntityMetaDataType.Boolean: // Boolean - value = ReadNextBool(cache); - break; - case EntityMetaDataType.Rotation: // Rotation (3x floats) - value = new List - { - ReadNextFloat(cache), - ReadNextFloat(cache), - ReadNextFloat(cache) - }; - break; - case EntityMetaDataType.Position: // Position - value = ReadNextLocation(cache); - break; - case EntityMetaDataType.OptionalPosition: // Optional Position - if (ReadNextBool(cache)) - { - value = ReadNextLocation(cache); - } + // Value's data type is depended on Type + object? value = null; - break; - case EntityMetaDataType.Direction: // Direction (VarInt) - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.OptionalUuid: // Optional UUID - if (ReadNextBool(cache)) - { - value = ReadNextUUID(cache); - } - - break; - case EntityMetaDataType.BlockId: // BlockID (VarInt) - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.OptionalBlockId: // Optional BlockID (VarInt) - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.Nbt: // NBT - value = ReadNextNbt(cache); - break; - case EntityMetaDataType.Particle: // Particle - // Skip data only, not used - ReadParticleData(cache, itemPalette); - break; - case EntityMetaDataType.VillagerData: // Villager Data (3x VarInt) - value = new List - { - ReadNextVarInt(cache), - ReadNextVarInt(cache), - ReadNextVarInt(cache) - }; - break; - case EntityMetaDataType.OptionalVarInt: // Optional VarInt - if (ReadNextBool(cache)) - { + switch (type) + { + case EntityMetaDataType.Short: // 1.8 only + value = ReadNextShort(cache); + break; + case EntityMetaDataType.Int: // 1.8 only + value = ReadNextInt(cache); + break; + case EntityMetaDataType.Vector3Int: // 1.8 only + value = new List() + { + ReadNextInt(cache), + ReadNextInt(cache), + ReadNextInt(cache), + }; + break; + case EntityMetaDataType.Byte: // byte + value = ReadNextByte(cache); + break; + case EntityMetaDataType.VarInt: // VarInt value = ReadNextVarInt(cache); - } + break; + case EntityMetaDataType.VarLong: // Long + value = ReadNextVarLong(cache); + break; + case EntityMetaDataType.Float: // Float + value = ReadNextFloat(cache); + break; + case EntityMetaDataType.String: // String + value = ReadNextString(cache); + break; + case EntityMetaDataType.Chat: // Chat + value = ReadNextChat(cache); + break; + case EntityMetaDataType.OptionalChat: // Optional Chat + if (ReadNextBool(cache)) + value = ReadNextChat(cache); + break; + case EntityMetaDataType.Slot: // Slot + value = ReadNextItemSlot(cache, itemPalette); + break; + case EntityMetaDataType.Boolean: // Boolean + value = ReadNextBool(cache); + break; + case EntityMetaDataType.Rotation: // Rotation (3x floats) + value = new List + { + ReadNextFloat(cache), + ReadNextFloat(cache), + ReadNextFloat(cache) + }; + break; + case EntityMetaDataType.Position: // Position + value = ReadNextLocation(cache); + break; + case EntityMetaDataType.OptionalPosition: // Optional Position + if (ReadNextBool(cache)) + { + value = ReadNextLocation(cache); + } - break; - case EntityMetaDataType.Pose: // Pose - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.CatVariant: // Cat Variant - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.FrogVariant: // Frog Varint - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.GlobalPosition: // GlobalPos - // Dimension and blockPos, currently not in use - value = new Tuple(ReadNextString(cache), ReadNextLocation(cache)); - break; - case EntityMetaDataType.OptionalGlobalPosition: - // FIXME: wiki.vg is bool + string + location - // but minecraft-data is bool + string - if (ReadNextBool(cache)) - { + break; + case EntityMetaDataType.Direction: // Direction (VarInt) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.OptionalUuid: // Optional UUID + case EntityMetaDataType.OptionalLivingEntityReference: // Optional Living Entity Reference (1.21.5+, same wire format) + if (ReadNextBool(cache)) + { + value = ReadNextUUID(cache); + } + + break; + case EntityMetaDataType.BlockId: // BlockID (VarInt) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.OptionalBlockId: // Optional BlockID (VarInt) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.Nbt: // NBT + value = ReadNextNbt(cache); + break; + case EntityMetaDataType.Particle: // Particle + ReadParticleData(cache, itemPalette); + break; + case EntityMetaDataType.Particles: // List of Particle (1.20.6+) + int particleCount = ReadNextVarInt(cache); + for (int i = 0; i < particleCount; i++) + ReadParticleData(cache, itemPalette); + break; + case EntityMetaDataType.VillagerData: // Villager Data (3x VarInt) + value = new List + { + ReadNextVarInt(cache), + ReadNextVarInt(cache), + ReadNextVarInt(cache) + }; + break; + case EntityMetaDataType.OptionalVarInt: // Optional VarInt + + if (protocolversion < Protocol18Handler.MC_1_20_6_Version) + { + if (ReadNextBool(cache)) + value = ReadNextVarInt(cache); + } + else value = ReadNextVarInt(cache); + + break; + case EntityMetaDataType.Pose: // Pose + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.CatVariant: // Cat Variant + case EntityMetaDataType.CatSoundVariant: // Cat Sound Variant (26.1+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.CowVariant: // Cow Variant (1.21.5+) + case EntityMetaDataType.CowSoundVariant: // Cow Sound Variant (26.1+) + case EntityMetaDataType.WolfVariant: // Wolf Variant (1.20.6+) + case EntityMetaDataType.WolfSoundVariant: // Wolf Sound Variant (1.21.5+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.FrogVariant: // Frog Variant + case EntityMetaDataType.PigVariant: // Pig Variant (1.21.5+) + case EntityMetaDataType.PigSoundVariant: // Pig Sound Variant (26.1+) + case EntityMetaDataType.ChickenVariant: // Chicken Variant (1.21.5+) + case EntityMetaDataType.ChickenSoundVariant: // Chicken Sound Variant (26.1+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.GlobalPosition: // GlobalPos // Dimension and blockPos, currently not in use value = new Tuple(ReadNextString(cache), ReadNextLocation(cache)); - } + break; + case EntityMetaDataType.OptionalGlobalPosition: + // FIXME: wiki.vg is bool + string + location + // but minecraft-data is bool + string + if (ReadNextBool(cache)) + { + // Dimension and blockPos, currently not in use + value = new Tuple(ReadNextString(cache), ReadNextLocation(cache)); + } - break; - case EntityMetaDataType.PaintingVariant: // Painting Variant - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.SnifferState: // Sniffer state - value = ReadNextVarInt(cache); - break; - case EntityMetaDataType.Vector3: // Vector 3f - value = new List - { - ReadNextFloat(cache), - ReadNextFloat(cache), - ReadNextFloat(cache) - }; - break; - case EntityMetaDataType.Quaternion: // Quaternion - value = new List - { - ReadNextFloat(cache), - ReadNextFloat(cache), - ReadNextFloat(cache), - ReadNextFloat(cache) - }; - break; + break; + case EntityMetaDataType.PaintingVariant: // Painting Variant + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.SnifferState: // Sniffer state + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.ArmadilloState: // Armadillo state (1.20.6+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.CopperGolemState: // Copper Golem state (1.21.9+) + case EntityMetaDataType.WeatheringCopperState: // Weathering Copper state (1.21.9+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.ZombieNautilusVariant: // ZombieNautilus Variant (1.21.11+) + case EntityMetaDataType.HumanoidArm: // Humanoid Arm (1.21.11+) + value = ReadNextVarInt(cache); + break; + case EntityMetaDataType.ResolvableProfile: // ResolvableProfile (1.21.9+) + ReadNextResolvableProfile(cache); + break; + case EntityMetaDataType.Vector3: // Vector 3f + value = new List + { + ReadNextFloat(cache), + ReadNextFloat(cache), + ReadNextFloat(cache) + }; + break; + case EntityMetaDataType.Quaternion: // Quaternion + value = new List + { + ReadNextFloat(cache), + ReadNextFloat(cache), + ReadNextFloat(cache), + ReadNextFloat(cache) + }; + break; + } + + data[key] = value; + key = ReadNextByte(cache); } - data[key] = value; - key = ReadNextByte(cache); + return data; + } + catch (Exception) + { + return new Dictionary(); + } + } + + private static bool HasLpVec3Continuation(int firstByte) => (firstByte & 4) == 4; + + private static double UnpackLpVec3(long packedAxis) + { + return Math.Min((double)(packedAxis & 32767L), 32766.0) * 2.0 / 32766.0 - 1.0; + } + + /// + /// Read and decode an LpVec3 (low-precision vec3) from the cache (1.21.9+). + /// Returned vector is expressed in blocks per tick. + /// + public (double X, double Y, double Z) ReadNextLpVec3Values(Queue cache) + { + int first = ReadNextByte(cache); + if (first == 0) + return (0.0, 0.0, 0.0); + + int second = ReadNextByte(cache); + uint high = (uint)ReadNextInt(cache); + long packed = ((long)high << 16) | (long)(second << 8) | (uint)first; + + long scale = first & 3; + if (HasLpVec3Continuation(first)) + scale |= ((long)ReadNextVarInt(cache) & 0xFFFFFFFFL) << 2; + + return ( + UnpackLpVec3(packed >> 3) * scale, + UnpackLpVec3(packed >> 18) * scale, + UnpackLpVec3(packed >> 33) * scale + ); + } + + /// + /// Read an LpVec3 (low-precision vec3) from the cache (1.21.9+) and discard it. + /// + public void ReadNextLpVec3(Queue cache) + { + ReadNextLpVec3Values(cache); + } + + /// + /// Consume bytes for a ResolvableProfile (1.21.9+). + /// Wire: Either(GameProfile, Partial) + PlayerSkin.Patch + /// + private void ReadNextResolvableProfile(Queue cache) + { + bool isFullProfile = ReadNextBool(cache); // Either flag: true=GameProfile, false=Partial + if (isFullProfile) + { + ReadNextUUID(cache); // UUID + ReadNextString(cache); // player name (max 16 chars) + ReadGameProfileProperties(cache); + } + else + { + // Partial: optional name, optional UUID, properties + if (ReadNextBool(cache)) + ReadNextString(cache); // optional player name + if (ReadNextBool(cache)) + ReadNextUUID(cache); // optional UUID + ReadGameProfileProperties(cache); } - return data; + // PlayerSkin.Patch: 4 optional fields + // body (optional ResourceLocation string) + if (ReadNextBool(cache)) + ReadNextString(cache); + // cape + if (ReadNextBool(cache)) + ReadNextString(cache); + // elytra + if (ReadNextBool(cache)) + ReadNextString(cache); + // model (optional bool: true=SLIM, false=WIDE) + if (ReadNextBool(cache)) + ReadNextBool(cache); + } + + /// + /// Read GameProfile properties (PropertyMap): VarInt count, then per entry: + /// name string, value string, optional signature string. + /// + private void ReadGameProfileProperties(Queue cache) + { + int count = ReadNextVarInt(cache); + for (int i = 0; i < count; i++) + { + ReadNextString(cache); // property name + ReadNextString(cache); // property value + if (ReadNextBool(cache)) // has signature? + ReadNextString(cache); // signature + } } /// @@ -879,15 +1175,21 @@ namespace MinecraftClient.Protocol.Handlers switch (particleId) { + case 1: // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextVarInt(cache); // BlockState (minecraft:block) + break; + case 2: - // 1.18 + + // 1.18 if (protocolversion > Protocol18Handler.MC_1_17_1_Version) - ReadNextVarInt(cache); // Block state (minecraft:block) + ReadNextVarInt(cache); // Block state (minecraft:block before 1.20.6, minecraft:block_marker in 1.20.6+) break; case 3: - if (protocolversion is < Protocol18Handler.MC_1_17_Version or > Protocol18Handler.MC_1_17_1_Version) + if (protocolversion is (< Protocol18Handler.MC_1_17_Version or > Protocol18Handler.MC_1_17_1_Version) + and < Protocol18Handler.MC_1_20_6_Version) ReadNextVarInt( - cache); // Block State (minecraft:block before 1.18, minecraft:block_marker after 1.18) + cache); // Block State (minecraft:block before 1.18, minecraft:block_marker after 1.18 up to 1.20.6) break; case 4: if (protocolversion is Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version) @@ -898,11 +1200,24 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion < Protocol18Handler.MC_1_15_Version) ReadDustParticle(cache); break; + case 13: + // 1.20.6+ - minecraft:dust + ReadDustParticle(cache); + break; case 14: - // 1.15 - 1.16.5 and 1.18 - 1.19.4 - if (protocolversion is >= Protocol18Handler.MC_1_15_Version and < Protocol18Handler.MC_1_17_Version - or > Protocol18Handler.MC_1_17_1_Version) - ReadDustParticle(cache); + switch (protocolversion) + { + // 1.15 - 1.16.5 and 1.18 - 1.20.4 + case >= Protocol18Handler.MC_1_15_Version and < Protocol18Handler.MC_1_17_Version + or > Protocol18Handler.MC_1_17_1_Version and < Protocol18Handler.MC_1_20_6_Version: + ReadDustParticle(cache); + break; + // 1.20.6+ + case >= Protocol18Handler.MC_1_20_6_Version: + ReadDustParticleColorTransition(cache); + break; + } + break; case 15: switch (protocolversion) @@ -910,7 +1225,8 @@ namespace MinecraftClient.Protocol.Handlers case Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version: ReadDustParticle(cache); break; - case > Protocol18Handler.MC_1_17_1_Version: + // 1.18 - 1.20.4 + case > Protocol18Handler.MC_1_17_1_Version and < Protocol18Handler.MC_1_20_6_Version: ReadDustParticleColorTransition(cache); break; } @@ -920,21 +1236,26 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion is Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version) ReadDustParticleColorTransition(cache); break; + case 20: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextInt(cache); // minecraft:entity_effect + break; case 23: // 1.15 - 1.16.5 if (protocolversion is >= Protocol18Handler.MC_1_15_Version and < Protocol18Handler.MC_1_17_Version) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 24: - // 1.18 - 1.19.2 onwards + // 1.18 - 1.19.3 if (protocolversion is > Protocol18Handler.MC_1_17_1_Version and < Protocol18Handler.MC_1_19_3_Version) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 25: - // 1.17 - 1.17.1 and 1.19.3 onwards + // 1.17 - 1.17.1 and 1.19.3 - 1.20.4 if (protocolversion is Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version - or >= Protocol18Handler.MC_1_19_3_Version) + or (>= Protocol18Handler.MC_1_19_3_Version and < Protocol18Handler.MC_1_20_6_Version)) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 27: @@ -942,8 +1263,14 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion < Protocol18Handler.MC_1_15_Version) ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) break; + case 28: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextVarInt(cache); // minecraft:falling_dust (BlockState) + break; case 30: - if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) + // 1.19.3 - 1.20.4 + if (protocolversion is >= Protocol18Handler.MC_1_19_3_Version and < Protocol18Handler.MC_1_20_6_Version) ReadNextFloat(cache); // Roll (minecraft:sculk_charge) break; case 32: @@ -951,6 +1278,11 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion is >= Protocol18Handler.MC_1_15_Version and < Protocol18Handler.MC_1_17_Version) ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) break; + case 35: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextFloat(cache); // minecraft:sculk_charge (Roll) + break; case 36: switch (protocolversion) { @@ -958,6 +1290,7 @@ namespace MinecraftClient.Protocol.Handlers case Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version: ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) break; + // 1.18 - 1.19.2 case > Protocol18Handler.MC_1_17_1_Version and < Protocol18Handler.MC_1_19_3_Version: // minecraft:vibration ReadNextLocation(cache); // Origin (Starting Position) @@ -968,7 +1301,7 @@ namespace MinecraftClient.Protocol.Handlers break; case 37: - // minecraft:vibration + // minecraft:vibration - 1.17 - 1.17.1 if (protocolversion is Protocol18Handler.MC_1_17_Version or Protocol18Handler.MC_1_17_1_Version) { ReadNextDouble(cache); // Origin X @@ -982,11 +1315,13 @@ namespace MinecraftClient.Protocol.Handlers break; case 39: - if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) + // 1.19.3 - 1.20.4 + if (protocolversion is >= Protocol18Handler.MC_1_19_3_Version and < Protocol18Handler.MC_1_20_6_Version) ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) break; case 40: - if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) + // 1.19.3 - 1.20.4 + if (protocolversion is >= Protocol18Handler.MC_1_19_3_Version and < Protocol18Handler.MC_1_20_6_Version) { var positionSourceType = ReadNextString(cache); switch (positionSourceType) @@ -1004,6 +1339,26 @@ namespace MinecraftClient.Protocol.Handlers } break; + case 44: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextItemSlot(cache, itemPalette); // minecraft:item (Item) + break; + case 45: + // 1.21+ + if (protocolversion >= Protocol18Handler.MC_1_21_Version) + ReadVibration(cache); + break; + case 99: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextVarInt(cache); // minecraft:shriek (Delay) + break; + case 105: + // 1.20.6+ + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + ReadNextVarInt(cache); // minecraft:dust_pillar (BlockState) + break; } } @@ -1020,10 +1375,19 @@ namespace MinecraftClient.Protocol.Handlers ReadNextFloat(cache); // From red ReadNextFloat(cache); // From green ReadNextFloat(cache); // From blue - ReadNextFloat(cache); // Scale ReadNextFloat(cache); // To red ReadNextFloat(cache); // To green - ReadNextFloat(cache); // To Blue + ReadNextFloat(cache); // To blue + ReadNextFloat(cache); // Scale + } + + private void ReadVibration(Queue cache) + { + ReadNextVarInt(cache); // Position Source Type + ReadNextLocation(cache); // Block Position + ReadNextVarInt(cache); // Entity ID + ReadNextFloat(cache); // Entity eye height + ReadNextVarInt(cache); // Ticks } /// @@ -1032,12 +1396,14 @@ namespace MinecraftClient.Protocol.Handlers /// The item that was read or NULL for an empty slot public VillagerTrade ReadNextTrade(Queue cache, ItemPalette itemPalette) { - Item inputItem1 = ReadNextItemSlot(cache, itemPalette)!; + Item inputItem1 = ReadNextTradeCost(cache, itemPalette)!; Item outputItem = ReadNextItemSlot(cache, itemPalette)!; Item? inputItem2 = null; - if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) + inputItem2 = ReadNextOptionalTradeCost(cache, itemPalette); + else if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) inputItem2 = ReadNextItemSlot(cache, itemPalette); else { @@ -1056,21 +1422,62 @@ namespace MinecraftClient.Protocol.Handlers maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand); } + private Item? ReadNextTradeCost(Queue cache, ItemPalette itemPalette) + { + if (protocolversion < Protocol18Handler.MC_1_20_6_Version) + return ReadNextItemSlot(cache, itemPalette); + + var itemId = ReadNextVarInt(cache); + var itemCount = ReadNextVarInt(cache); + var item = new Item(itemPalette.FromId(itemId), itemCount, null); + var componentCount = ReadNextVarInt(cache); + + if (componentCount > 0) + { + var structuredComponentHandler = new StructuredComponentsHandler(protocolversion, this, itemPalette); + var components = new List(componentCount); + for (var i = 0; i < componentCount; i++) + { + var componentTypeId = ReadNextVarInt(cache); + components.Add(structuredComponentHandler.Parse(componentTypeId, cache)); + } + + item.Components = components; + } + + return item; + } + + private Item? ReadNextOptionalTradeCost(Queue cache, ItemPalette itemPalette) + { + if (!ReadNextBool(cache)) + return null; + + return ReadNextTradeCost(cache, itemPalette); + } + public string ReadNextChat(Queue cache) { if (protocolversion >= Protocol18Handler.MC_1_20_4_Version) { - // Read as NBT - var r = ReadNextNbt(cache); - var msg = ChatParser.ParseText(r); - return msg; - } - else - { - // Read as String - var json = ReadNextString(cache); - return ChatParser.ParseText(json); + // Vanilla 1.20.4+ uses NBT here, but Hypixel exposed a JSON-string fallback on the same path. + Queue fallbackCache = new(cache); + try + { + var r = ReadNextNbt(cache); + return ChatParser.ParseText(r); + } + catch (System.IO.InvalidDataException) + { + cache.Clear(); + foreach (var b in fallbackCache) + cache.Enqueue(b); + } } + + // Read as String + var json = ReadNextString(cache); + return ChatParser.ParseText(json); } /// @@ -1083,6 +1490,17 @@ namespace MinecraftClient.Protocol.Handlers return GetNbt(nbt, true); } + public byte[] GetNbtTag(object? tag) + { + if (tag is null) + return [0]; + + var tagData = GetNbtField(tag, out var tagType); + var data = new List { tagType }; + data.AddRange(tagData); + return data.ToArray(); + } + /// /// Build an uncompressed Named Binary Tag blob for sending over the network (internal) /// @@ -1091,25 +1509,38 @@ namespace MinecraftClient.Protocol.Handlers /// Byte array for this NBT tag private byte[] GetNbt(Dictionary? nbt, bool root) { - if (nbt == null || nbt.Count == 0) - return new byte[] { 0 }; // TAG_End + if (nbt is null || nbt.Count == 0) + return [0]; // TAG_End List bytes = new(); if (root) { + if (protocolversion >= Protocol18Handler.MC_1_20_4_Version + && nbt.Count == 1 + && nbt.TryGetValue("", out var rootVal) && rootVal is string rootStr) + { + bytes.Add(8); // TAG_String + var strBytes = Encoding.UTF8.GetBytes(rootStr); + bytes.AddRange(GetUShort((ushort)strBytes.Length)); + bytes.AddRange(strBytes); + return bytes.ToArray(); + } + bytes.Add(10); // TAG_Compound - // NBT root name - string? rootName = null; + if (protocolversion < Protocol18Handler.MC_1_20_2_Version) + { + string? rootName = null; - if (nbt.ContainsKey("")) - rootName = nbt[""] as string; + if (nbt.ContainsKey("")) + rootName = nbt[""] as string; - rootName ??= ""; + rootName ??= ""; - bytes.AddRange(GetUShort((ushort)rootName.Length)); - bytes.AddRange(Encoding.ASCII.GetBytes(rootName)); + bytes.AddRange(GetUShort((ushort)rootName.Length)); + bytes.AddRange(Encoding.ASCII.GetBytes(rootName)); + } } foreach (var item in nbt) @@ -1401,21 +1832,47 @@ namespace MinecraftClient.Protocol.Handlers public byte[] GetLocation(Location location) { byte[] locationBytes; + ulong x = (ulong)(int)Math.Floor(location.X) & 0x3FFFFFF; + ulong y = (ulong)(int)Math.Floor(location.Y) & 0xFFF; + ulong z = (ulong)(int)Math.Floor(location.Z) & 0x3FFFFFF; if (protocolversion >= Protocol18Handler.MC_1_14_Version) { - locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | - ((((ulong)location.Z) & 0x3FFFFFF) << 12) | - (((ulong)location.Y) & 0xFFF)); + locationBytes = BitConverter.GetBytes((x << 38) | (z << 12) | y); } else - locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | - ((((ulong)location.Y) & 0xFFF) << 26) | - (((ulong)location.Z) & 0x3FFFFFF)); + locationBytes = BitConverter.GetBytes((x << 38) | (y << 26) | z); Array.Reverse(locationBytes); //Endianness return locationBytes; } + /// + /// Get a byte array representing the given item as a HashedStack (1.21.5+). + /// Used for serverbound container_click where the server expects HashedStack instead of full ItemStack. + /// Wire format: Optional<ActualItem> where ActualItem = holderRegistry(item_id) + VarInt(count) + HashedPatchMap. + /// Since MCC doesn't track component hashes, we send an empty HashedPatchMap (0 added, 0 removed). + /// The server will detect the stateId mismatch and resync. + /// + public byte[] GetHashedItemSlot(Item? item, ItemPalette itemPalette) + { + List slotData = new(); + + if (item is null || item.IsEmpty) + { + slotData.AddRange(GetBool(false)); + } + else + { + slotData.AddRange(GetBool(true)); + slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + slotData.AddRange(GetVarInt(item.Count)); + slotData.AddRange(GetVarInt(0)); // HashedPatchMap: 0 added components + slotData.AddRange(GetVarInt(0)); // HashedPatchMap: 0 removed components + } + + return slotData.ToArray(); + } + /// /// Get a byte array representing the given item as an item slot /// @@ -1425,31 +1882,69 @@ namespace MinecraftClient.Protocol.Handlers public byte[] GetItemSlot(Item? item, ItemPalette itemPalette) { List slotData = new(); - if (protocolversion > Protocol18Handler.MC_1_13_Version) + + if (protocolversion >= Protocol18Handler.MC_1_20_6_Version) { - // MC 1.13 and greater - if (item == null || item.IsEmpty) - slotData.AddRange(GetBool(false)); // No item + if (item is null || item.IsEmpty) + { + slotData.AddRange(GetVarInt(0)); + } else { - slotData.AddRange(GetBool(true)); // Item is present + slotData.AddRange(GetVarInt(item.Count)); slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + + if (item.Components is not null && item.Components.Count > 0) + { + slotData.AddRange(GetVarInt(item.Components.Count)); + slotData.AddRange(GetVarInt(0)); // components to remove + foreach (var component in item.Components) + { + slotData.AddRange(GetVarInt(component.TypeId)); + var serialized = component.Serialize(); + slotData.AddRange(serialized); + } + } + else + { + slotData.AddRange(GetVarInt(0)); // no components to add + slotData.AddRange(GetVarInt(0)); // no components to remove + } + } + } + else if (protocolversion >= Protocol18Handler.MC_1_13_2_Version) + { + if (item is null || item.IsEmpty) + slotData.AddRange(GetBool(false)); + else + { + slotData.AddRange(GetBool(true)); + slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + slotData.Add((byte)item.Count); + slotData.AddRange(GetNbt(item.NBT)); + } + } + else if (protocolversion >= Protocol18Handler.MC_1_13_Version) + { + if (item is null || item.IsEmpty) + slotData.AddRange(GetShort(-1)); + else + { + slotData.AddRange(GetShort((short)itemPalette.ToId(item.Type))); slotData.Add((byte)item.Count); slotData.AddRange(GetNbt(item.NBT)); } } else { - // MC 1.12.2 and lower - if (item == null || item.IsEmpty) + if (item is null || item.IsEmpty) slotData.AddRange(GetShort(-1)); else { - // For 1.8 - 1.12.2 we combine Item Id and Item Data to a single value using: (id << 16) | data - // Thus to get an ID we do a right shift by 16 bits slotData.AddRange(GetShort((short)(itemPalette.ToId(item.Type) >> 16))); slotData.Add((byte)item.Count); - slotData.Add((byte)item.Data); + // Legacy (<1.13) item slot wire format uses a SHORT for item damage/data. + slotData.AddRange(GetShort((short)item.Data)); slotData.AddRange(GetNbt(item.NBT)); } } @@ -1457,6 +1952,39 @@ namespace MinecraftClient.Protocol.Handlers return slotData.ToArray(); } + /// + /// Get a byte array representing the given item as a non-empty ItemStackTemplate. + /// + /// Item + /// Item Palette + /// ItemStackTemplate representation + public byte[] GetItemStackTemplate(Item item, ItemPalette itemPalette) + { + List slotData = new(); + + slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + slotData.AddRange(GetVarInt(item.Count)); + + if (item.Components is not null && item.Components.Count > 0) + { + slotData.AddRange(GetVarInt(item.Components.Count)); + slotData.AddRange(GetVarInt(0)); // components to remove + foreach (var component in item.Components) + { + slotData.AddRange(GetVarInt(component.TypeId)); + var serialized = component.Serialize(); + slotData.AddRange(serialized); + } + } + else + { + slotData.AddRange(GetVarInt(0)); // no components to add + slotData.AddRange(GetVarInt(0)); // no components to remove + } + + return slotData.ToArray(); + } + /// /// Get a byte array representing an array of item slots /// @@ -1524,7 +2052,7 @@ namespace MinecraftClient.Protocol.Handlers /// String representation public string ByteArrayToString(byte[]? bytes) { - if (bytes == null) + if (bytes is null) return "null"; else return BitConverter.ToString(bytes).Replace("-", " "); @@ -1565,7 +2093,7 @@ namespace MinecraftClient.Protocol.Handlers { List fields = new(); fields.AddRange(GetLastSeenMessageList(ack.lastSeen, isOnlineMode)); - if (!isOnlineMode || ack.lastReceived == null) + if (!isOnlineMode || ack.lastReceived is null) fields.AddRange(GetBool(false)); // Has last received message else { @@ -1578,4 +2106,4 @@ namespace MinecraftClient.Protocol.Handlers return fields.ToArray(); } } -} \ No newline at end of file +} diff --git a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs index e781aa18..644be909 100755 --- a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs +++ b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs @@ -11,17 +11,8 @@ namespace MinecraftClient.Protocol.Handlers.Forge /// /// Represents an individual forge mod. /// - public class ForgeMod + public record ForgeMod(string ModID, string Version) { - public ForgeMod(String ModID, String Version) - { - this.ModID = ModID; - this.Version = Version; - } - - public readonly String ModID; - public readonly String Version; - public override string ToString() { return ModID + " v" + Version; @@ -63,7 +54,7 @@ namespace MinecraftClient.Protocol.Handlers.Forge /// /// The modinfo JSON tag. /// Forge protocol version - internal ForgeInfo(Json.JSONData data, FMLVersion fmlVersion) + internal ForgeInfo(System.Text.Json.Nodes.JsonObject data, FMLVersion fmlVersion) { Mods = new List(); Version = fmlVersion; @@ -91,10 +82,10 @@ namespace MinecraftClient.Protocol.Handlers.Forge // }] // } - foreach (Json.JSONData mod in data.Properties["modList"].DataArray) + foreach (var mod in data["modList"]!.AsArray()) { - String modid = mod.Properties["modid"].StringValue; - String modversion = mod.Properties["version"].StringValue; + String modid = mod!["modid"]!.GetStringValue(); + String modversion = mod["version"]!.GetStringValue(); Mods.Add(new ForgeMod(modid, modversion)); } @@ -131,10 +122,10 @@ namespace MinecraftClient.Protocol.Handlers.Forge // "fmlNetworkVersion": 2 // } - foreach (Json.JSONData mod in data.Properties["mods"].DataArray) + foreach (var mod in data["mods"]!.AsArray()) { - String modid = mod.Properties["modId"].StringValue; - String modmarker = mod.Properties["modmarker"].StringValue; + String modid = mod!["modId"]!.GetStringValue(); + String modmarker = mod["modmarker"]!.GetStringValue(); Mods.Add(new ForgeMod(modid, modmarker)); } @@ -142,7 +133,7 @@ namespace MinecraftClient.Protocol.Handlers.Forge break; case FMLVersion.FML3: // Example ModInfo for Minecraft 1.18 and greater (FML3) - + // "forgeData": { // "channels": [], // "mods": [], @@ -157,7 +148,7 @@ namespace MinecraftClient.Protocol.Handlers.Forge // - Here is the discussion: // see https://github.com/MinecraftForge/MinecraftForge/pull/8169 - string encodedData = data.Properties["d"].StringValue; + string encodedData = data["d"]!.GetStringValue(); Queue dataPackage = decodeOptimized(encodedData); DataTypes dataTypes = new DataTypes(Protocol18Handler.MC_1_18_1_Version); @@ -178,24 +169,26 @@ namespace MinecraftClient.Protocol.Handlers.Forge // [ Channel Version ][ String ] // [ Required On Client ][ Bool ] - for (var i = 0; i < modsSize; i++) { + for (var i = 0; i < modsSize; i++) + { var channelSizeAndVersionFlag = dataTypes.ReadNextVarInt(dataPackage); var channelSize = channelSizeAndVersionFlag >> 1; int VERSION_FLAG_IGNORESERVERONLY = 0b1; var isIgnoreServerOnly = (channelSizeAndVersionFlag & VERSION_FLAG_IGNORESERVERONLY) != 0; - + var modId = dataTypes.ReadNextString(dataPackage); - + string IGNORESERVERONLY = "IGNORED"; var modVersion = isIgnoreServerOnly ? IGNORESERVERONLY : dataTypes.ReadNextString(dataPackage); - - for (var i1 = 0; i1 < channelSize; i1++) { + + for (var i1 = 0; i1 < channelSize; i1++) + { dataTypes.ReadNextString(dataPackage); // channelName dataTypes.ReadNextString(dataPackage); // channelVersion dataTypes.ReadNextBool(dataPackage); // requiredOnClient } - + mods.Add(modId, modVersion); Mods.Add(new ForgeMod(modId, modVersion)); } @@ -222,7 +215,8 @@ namespace MinecraftClient.Protocol.Handlers.Forge /// The code below is converted from forge source code, see: /// https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L361 /// - private static Queue decodeOptimized(string encodedData) { + private static Queue decodeOptimized(string encodedData) + { int size0 = encodedData[0]; int size1 = encodedData[1]; int size = size0 | (size1 << 15); diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index 07d37c72..57f1bb92 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -5,702 +5,780 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c { internal static class DeclareCommands { - private static int RootIdx; + private const byte NodeTypeMask = 0x03; + private const byte NodeExecutableFlag = 0x04; + private const byte NodeRedirectFlag = 0x08; + private const byte NodeCustomSuggestionsFlag = 0x10; + private const byte NodeRestrictedFlag = 0x20; + + private static readonly Dictionary s_argumentTypeCatalog = CreateArgumentTypeCatalog(); + private static readonly ArgumentTypeLayout s_unknownLegacyArgumentType = new("minecraft:unknown"); + + // Generated from tools/gen_command_argument_registry.py with IDE-only registrations excluded. + private static readonly string[] s_modernArgumentTypes1206 = + [ + "brigadier:bool", "brigadier:float", "brigadier:double", "brigadier:integer", "brigadier:long", "brigadier:string", + "entity", "game_profile", "block_pos", "column_pos", "vec3", "vec2", "block_state", "block_predicate", + "item_stack", "item_predicate", "color", "component", "style", "message", "nbt_compound_tag", "nbt_tag", + "nbt_path", "objective", "objective_criteria", "operation", "particle", "angle", "rotation", + "scoreboard_slot", "score_holder", "swizzle", "team", "item_slot", "item_slots", "resource_location", + "function", "entity_anchor", "int_range", "float_range", "dimension", "gamemode", "time", + "resource_or_tag", "resource_or_tag_key", "resource", "resource_key", "template_mirror", + "template_rotation", "heightmap", "loot_table", "loot_predicate", "loot_modifier", "uuid" + ]; + + private static readonly string[] s_modernArgumentTypes1215 = + [ + "brigadier:bool", "brigadier:float", "brigadier:double", "brigadier:integer", "brigadier:long", "brigadier:string", + "entity", "game_profile", "block_pos", "column_pos", "vec3", "vec2", "block_state", "block_predicate", + "item_stack", "item_predicate", "color", "component", "style", "message", "nbt_compound_tag", "nbt_tag", + "nbt_path", "objective", "objective_criteria", "operation", "particle", "angle", "rotation", + "scoreboard_slot", "score_holder", "swizzle", "team", "item_slot", "item_slots", "resource_location", + "function", "entity_anchor", "int_range", "float_range", "dimension", "gamemode", "time", + "resource_or_tag", "resource_or_tag_key", "resource", "resource_key", "resource_selector", + "template_mirror", "template_rotation", "heightmap", "loot_table", "loot_predicate", "loot_modifier", "uuid" + ]; + + private static readonly string[] s_modernArgumentTypes1216 = + [ + "brigadier:bool", "brigadier:float", "brigadier:double", "brigadier:integer", "brigadier:long", "brigadier:string", + "entity", "game_profile", "block_pos", "column_pos", "vec3", "vec2", "block_state", "block_predicate", + "item_stack", "item_predicate", "color", "hex_color", "component", "style", "message", + "nbt_compound_tag", "nbt_tag", "nbt_path", "objective", "objective_criteria", "operation", "particle", + "angle", "rotation", "scoreboard_slot", "score_holder", "swizzle", "team", "item_slot", "item_slots", + "resource_location", "function", "entity_anchor", "int_range", "float_range", "dimension", "gamemode", + "time", "resource_or_tag", "resource_or_tag_key", "resource", "resource_key", "resource_selector", + "template_mirror", "template_rotation", "heightmap", "loot_table", "loot_predicate", "loot_modifier", + "dialog", "uuid" + ]; + + private static int RootIdx = -1; private static CommandNode[] Nodes = Array.Empty(); + private static bool HasLoadedTree; + internal static string? LastReadError { get; private set; } + + public static bool IsCommandTreeAvailable => HasValidCommandTree(); public static void Read(DataTypes dataTypes, Queue packetData, int protocolVersion) { - int count = dataTypes.ReadNextVarInt(packetData); - Nodes = new CommandNode[count]; - for (int i = 0; i < count; ++i) + Reset(); + ConsoleIO.OnDeclareMinecraftCommand(Array.Empty()); + + try { - byte flags = dataTypes.ReadNextByte(packetData); - - int childCount = dataTypes.ReadNextVarInt(packetData); - int[] childs = new int[childCount]; - for (int j = 0; j < childCount; ++j) - childs[j] = dataTypes.ReadNextVarInt(packetData); - - int redirectNode = ((flags & 0x08) == 0x08) ? dataTypes.ReadNextVarInt(packetData) : -1; - - string? name = ((flags & 0x03) == 1 || (flags & 0x03) == 2) ? dataTypes.ReadNextString(packetData) : null; - - int parserId = ((flags & 0x03) == 2) ? dataTypes.ReadNextVarInt(packetData) : -1; - Parser? parser = null; - if ((flags & 0x03) == 2) - { - if (protocolVersion <= Protocol18Handler.MC_1_19_2_Version) - parser = parserId switch - { - 1 => new ParserFloat(dataTypes, packetData), - 2 => new ParserDouble(dataTypes, packetData), - 3 => new ParserInteger(dataTypes, packetData), - 4 => new ParserLong(dataTypes, packetData), - 5 => new ParserString(dataTypes, packetData), - 6 => new ParserEntity(dataTypes, packetData), - 8 => new ParserBlockPos(dataTypes, packetData), - 9 => new ParserColumnPos(dataTypes, packetData), - 10 => new ParserVec3(dataTypes, packetData), - 11 => new ParserVec2(dataTypes, packetData), - 18 => new ParserMessage(dataTypes, packetData), - 27 => new ParserRotation(dataTypes, packetData), - 29 => new ParserScoreHolder(dataTypes, packetData), - 43 => new ParserResourceOrTag(dataTypes, packetData), - 44 => new ParserResource(dataTypes, packetData), - 50 => new ParserForgeEnum(dataTypes, packetData), - _ => new ParserEmpty(dataTypes, packetData), - }; - else if (protocolVersion <= Protocol18Handler.MC_1_19_3_Version) // 1.19.3 - parser = parserId switch - { - 1 => new ParserFloat(dataTypes, packetData), - 2 => new ParserDouble(dataTypes, packetData), - 3 => new ParserInteger(dataTypes, packetData), - 4 => new ParserLong(dataTypes, packetData), - 5 => new ParserString(dataTypes, packetData), - 6 => new ParserEntity(dataTypes, packetData), - 8 => new ParserBlockPos(dataTypes, packetData), - 9 => new ParserColumnPos(dataTypes, packetData), - 10 => new ParserVec3(dataTypes, packetData), - 11 => new ParserVec2(dataTypes, packetData), - 18 => new ParserMessage(dataTypes, packetData), - 27 => new ParserRotation(dataTypes, packetData), - 29 => new ParserScoreHolder(dataTypes, packetData), - 41 => new ParserResourceOrTag(dataTypes, packetData), - 42 => new ParserResourceOrTag(dataTypes, packetData), - 43 => new ParserResource(dataTypes, packetData), - 44 => new ParserResource(dataTypes, packetData), - 50 => new ParserForgeEnum(dataTypes, packetData), - _ => new ParserEmpty(dataTypes, packetData), - }; - else if (protocolVersion <= Protocol18Handler.MC_1_20_2_Version)// 1.19.4 - 1.20.2 - parser = parserId switch - { - 1 => new ParserFloat(dataTypes, packetData), - 2 => new ParserDouble(dataTypes, packetData), - 3 => new ParserInteger(dataTypes, packetData), - 4 => new ParserLong(dataTypes, packetData), - 5 => new ParserString(dataTypes, packetData), - 6 => new ParserEntity(dataTypes, packetData), - 8 => new ParserBlockPos(dataTypes, packetData), - 9 => new ParserColumnPos(dataTypes, packetData), - 10 => new ParserVec3(dataTypes, packetData), - 11 => new ParserVec2(dataTypes, packetData), - 18 => new ParserMessage(dataTypes, packetData), - 27 => new ParserRotation(dataTypes, packetData), - 29 => new ParserScoreHolder(dataTypes, packetData), - 40 => new ParserTime(dataTypes, packetData), - 41 => new ParserResourceOrTag(dataTypes, packetData), - 42 => new ParserResourceOrTag(dataTypes, packetData), - 43 => new ParserResource(dataTypes, packetData), - 44 => new ParserResource(dataTypes, packetData), - 50 => protocolVersion == Protocol18Handler.MC_1_19_4_Version ? - new ParserForgeEnum(dataTypes, packetData) : - new ParserEmpty(dataTypes, packetData), - 51 => (protocolVersion >= Protocol18Handler.MC_1_20_Version && - protocolVersion <= Protocol18Handler.MC_1_20_2_Version) ? // 1.20 - 1.20.2 - new ParserForgeEnum(dataTypes, packetData) : - new ParserEmpty(dataTypes, packetData), - _ => new ParserEmpty(dataTypes, packetData), - }; - else // 1.20.3+ - parser = parserId switch - { - 1 => new ParserFloat(dataTypes, packetData), - 2 => new ParserDouble(dataTypes, packetData), - 3 => new ParserInteger(dataTypes, packetData), - 4 => new ParserLong(dataTypes, packetData), - 5 => new ParserString(dataTypes, packetData), - 6 => new ParserEntity(dataTypes, packetData), - 8 => new ParserBlockPos(dataTypes, packetData), - 9 => new ParserColumnPos(dataTypes, packetData), - 10 => new ParserVec3(dataTypes, packetData), - 11 => new ParserVec2(dataTypes, packetData), - 18 => new ParserMessage(dataTypes, packetData), - 27 => new ParserRotation(dataTypes, packetData), - 30 => new ParserScoreHolder(dataTypes, packetData), - 41 => new ParserTime(dataTypes, packetData), - 42 => new ParserResourceOrTag(dataTypes, packetData), - 43 => new ParserResourceOrTag(dataTypes, packetData), - 44 => new ParserResource(dataTypes, packetData), - 45 => new ParserResource(dataTypes, packetData), - 52 => new ParserForgeEnum(dataTypes, packetData), - _ => new ParserEmpty(dataTypes, packetData), - }; - } - - string? suggestionsType = ((flags & 0x10) == 0x10) ? dataTypes.ReadNextString(packetData) : null; - - Nodes[i] = new(flags, childs, redirectNode, name, parser, suggestionsType, parserId); + ReadCommandTree(dataTypes, packetData, protocolVersion); } - RootIdx = dataTypes.ReadNextVarInt(packetData); - - ConsoleIO.OnDeclareMinecraftCommand(ExtractRootCommand()); - } - - private static string[] ExtractRootCommand() - { - List commands = new(); - CommandNode root = Nodes[RootIdx]; - foreach (var child in root.Clildren) + catch (Exception ex) { - string? childName = Nodes[child].Name; - if (childName != null) - commands.Add(childName); + LastReadError = ex.ToString(); + Reset(); } - return commands.ToArray(); + + ConsoleIO.OnDeclareMinecraftCommand(HasLoadedTree ? ExtractRootCommand() : Array.Empty()); } public static List> CollectSignArguments(string command) { List> needSigned = new(); - CollectSignArguments(RootIdx, command, needSigned); - return needSigned; + if (!HasValidCommandTree() || string.IsNullOrEmpty(command)) + return needSigned; + + return TryMatchNode(RootIdx, command, 0, needSigned, out List> matchedArguments) + ? matchedArguments + : []; } - private static void CollectSignArguments(int NodeIdx, string command, List> arguments) + private static void ReadCommandTree(DataTypes dataTypes, Queue packetData, int protocolVersion) { - CommandNode node = Nodes[NodeIdx]; - string last_arg = command; - switch (node.Flags & 0x03) + int count = dataTypes.ReadNextVarInt(packetData); + Nodes = new CommandNode[count]; + + for (int i = 0; i < count; ++i) { - case 0: // root - break; - case 1: // literal + byte flags = dataTypes.ReadNextByte(packetData); + int[] children = ReadChildIndices(dataTypes, packetData); + int redirectNode = (flags & NodeRedirectFlag) != 0 ? dataTypes.ReadNextVarInt(packetData) : -1; + + CommandNodeKind nodeKind = (CommandNodeKind)(flags & NodeTypeMask); + CommandNode node = nodeKind switch + { + CommandNodeKind.Root => new(flags, children, redirectNode), + CommandNodeKind.Literal => new(flags, children, redirectNode, dataTypes.ReadNextString(packetData)), + CommandNodeKind.Argument => ReadArgumentNode(dataTypes, packetData, protocolVersion, flags, children, redirectNode), + _ => throw new InvalidOperationException($"Unsupported DeclareCommands node type {(byte)nodeKind}.") + }; + + Nodes[i] = node; + } + + RootIdx = dataTypes.ReadNextVarInt(packetData); + HasLoadedTree = IsValidNodeIndex(RootIdx); + } + + private static CommandNode ReadArgumentNode( + DataTypes dataTypes, + Queue packetData, + int protocolVersion, + byte flags, + int[] children, + int redirectNode) + { + string name = dataTypes.ReadNextString(packetData); + int parserId = dataTypes.ReadNextVarInt(packetData); + + if (!TryResolveArgumentTypeLayout(protocolVersion, parserId, out ArgumentTypeLayout layout)) + throw new InvalidOperationException($"Unsupported DeclareCommands argument type id {parserId} for protocol {protocolVersion}."); + + CommandArgumentDescriptor descriptor = ReadArgumentDescriptor(dataTypes, packetData, layout); + string? suggestionsType = (flags & NodeCustomSuggestionsFlag) != 0 ? dataTypes.ReadNextString(packetData) : null; + + return new(flags, children, redirectNode, name, descriptor, suggestionsType, parserId); + } + + private static int[] ReadChildIndices(DataTypes dataTypes, Queue packetData) + { + int childCount = dataTypes.ReadNextVarInt(packetData); + int[] children = new int[childCount]; + + for (int i = 0; i < childCount; ++i) + children[i] = dataTypes.ReadNextVarInt(packetData); + + return children; + } + + private static CommandArgumentDescriptor ReadArgumentDescriptor(DataTypes dataTypes, Queue packetData, ArgumentTypeLayout layout) + { + switch (layout.PayloadKind) + { + case ArgumentPayloadKind.None: + return layout.CreateDescriptor(); + case ArgumentPayloadKind.BrigadierFloat: + ReadNumberBounds(dataTypes, packetData, static (types, data) => types.ReadNextFloat(data)); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.BrigadierDouble: + ReadNumberBounds(dataTypes, packetData, static (types, data) => types.ReadNextDouble(data)); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.BrigadierInteger: + ReadNumberBounds(dataTypes, packetData, static (types, data) => types.ReadNextInt(data)); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.BrigadierLong: + ReadNumberBounds(dataTypes, packetData, static (types, data) => types.ReadNextLong(data)); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.BrigadierString: + ArgumentConsumption consumption = dataTypes.ReadNextVarInt(packetData) switch { - string[] arg = command.Split(' ', 2, StringSplitOptions.None); - if (!(arg.Length == 2 && node.Name! == arg[0])) - return; - last_arg = arg[1]; - } - break; - case 2: // argument - { - int argCnt = (node.Paser == null) ? 1 : node.Paser.GetArgCnt(); - string[] arg = command.Split(' ', argCnt + 1, StringSplitOptions.None); - if ((node.Flags & 0x04) > 0) - { - if (node.Paser != null && node.Paser.GetName() == "minecraft:message") - arguments.Add(new(node.Name!, command)); - } - if (!(arg.Length == argCnt + 1)) - return; - last_arg = arg[^1]; - } - break; + 0 => ArgumentConsumption.SingleToken, + 1 => ArgumentConsumption.QuotedStringOrWord, + 2 => ArgumentConsumption.GreedyTail, + int stringType => throw new InvalidOperationException($"Unsupported brigadier:string type {stringType}.") + }; + return layout.CreateDescriptor(consumption); + case ArgumentPayloadKind.Entity: + case ArgumentPayloadKind.ScoreHolder: + dataTypes.ReadNextByte(packetData); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.Time: + dataTypes.ReadNextInt(packetData); + return layout.CreateDescriptor(); + case ArgumentPayloadKind.RegistryKey: + case ArgumentPayloadKind.ForgeEnum: + dataTypes.ReadNextString(packetData); + return layout.CreateDescriptor(); default: - break; - } - - while (Nodes[NodeIdx].RedirectNode >= 0) - NodeIdx = Nodes[NodeIdx].RedirectNode; - - foreach (int childIdx in Nodes[NodeIdx].Clildren) - CollectSignArguments(childIdx, last_arg, arguments); - } - - internal class CommandNode - { - public byte Flags; - public int[] Clildren; - public int RedirectNode; - public string? Name; - public Parser? Paser; - public string? SuggestionsType; - public int ParserId; // Added for easy debug - - - public CommandNode(byte Flags, - int[] Clildren, - int RedirectNode = -1, - string? Name = null, - Parser? Paser = null, - string? SuggestionsType = null, - int parserId = -1) - { - this.Flags = Flags; - this.Clildren = Clildren; - this.RedirectNode = RedirectNode; - this.Name = Name; - this.Paser = Paser; - this.SuggestionsType = SuggestionsType; - ParserId = parserId; + throw new InvalidOperationException($"Unsupported DeclareCommands payload kind {layout.PayloadKind}."); } } - internal abstract class Parser + private static void ReadNumberBounds(DataTypes dataTypes, Queue packetData, Func, TValue> readValue) { - public abstract string GetName(); - - public abstract int GetArgCnt(); - - public abstract bool Check(string text); + byte flags = dataTypes.ReadNextByte(packetData); + if ((flags & 0x01) != 0) + _ = readValue(dataTypes, packetData); + if ((flags & 0x02) != 0) + _ = readValue(dataTypes, packetData); } - internal class ParserEmpty : Parser + private static string[] ExtractRootCommand() { + if (!HasValidCommandTree()) + return Array.Empty(); - public ParserEmpty(DataTypes dataTypes, Queue packetData) { } + List commands = new(); + CommandNode root = Nodes[RootIdx]; - public override bool Check(string text) + foreach (int child in root.Children) { + if (!IsValidNodeIndex(child)) + continue; + + string? childName = Nodes[child].Name; + if (!string.IsNullOrEmpty(childName)) + commands.Add(childName); + } + + return commands.ToArray(); + } + + private static bool TryMatchNode( + int nodeIdx, + string command, + int position, + List> signedArguments, + out List> matchedArguments) + { + matchedArguments = signedArguments; + if (!IsValidNodeIndex(nodeIdx)) + return false; + + CommandNode node = Nodes[nodeIdx]; + if (!TryConsumeNode(node, command, position, out int nextPosition, out Tuple? signedCapture)) + return false; + + List> currentArguments = signedArguments; + if (signedCapture is not null) + { + currentArguments = new List>(signedArguments.Count + 1); + currentArguments.AddRange(signedArguments); + currentArguments.Add(signedCapture); + } + + int traversalNodeIdx = ResolveRedirect(nodeIdx); + bool canStopHere = node.IsExecutable || + (traversalNodeIdx != nodeIdx && IsValidNodeIndex(traversalNodeIdx) && Nodes[traversalNodeIdx].IsExecutable); + + if (nextPosition == command.Length) + { + if (canStopHere) + { + matchedArguments = currentArguments; + return true; + } + + return false; + } + + int childPosition = nextPosition; + if (node.Kind != CommandNodeKind.Root) + { + if (command[childPosition] != ' ') + return false; + childPosition++; + } + + return TryMatchChildren(traversalNodeIdx, command, childPosition, currentArguments, out matchedArguments); + } + + private static bool TryMatchChildren( + int nodeIdx, + string command, + int position, + List> signedArguments, + out List> matchedArguments) + { + matchedArguments = signedArguments; + if (!IsValidNodeIndex(nodeIdx)) + return false; + + int[] children = Nodes[nodeIdx].Children; + + for (int pass = 0; pass < 2; ++pass) + { + foreach (int childIdx in children) + { + if (!IsValidNodeIndex(childIdx)) + continue; + + bool isLiteral = Nodes[childIdx].Kind == CommandNodeKind.Literal; + if ((pass == 0 && !isLiteral) || (pass == 1 && isLiteral)) + continue; + + if (TryMatchNode(childIdx, command, position, signedArguments, out matchedArguments)) + return true; + } + } + + return false; + } + + private static bool TryConsumeNode( + CommandNode node, + string command, + int position, + out int nextPosition, + out Tuple? signedCapture) + { + nextPosition = position; + signedCapture = null; + + switch (node.Kind) + { + case CommandNodeKind.Root: + return true; + case CommandNodeKind.Literal: + return TryConsumeLiteral(command, position, node.Name!, out nextPosition); + case CommandNodeKind.Argument: + if (node.Argument is null || !TryConsumeArgument(command, position, node.Argument.Value, out nextPosition)) + return false; + + if (node.Argument.Value.IsSigned) + signedCapture = new Tuple(node.Name!, command[position..nextPosition]); + + return true; + default: + return false; + } + } + + private static bool TryConsumeLiteral(string command, int position, string literal, out int nextPosition) + { + nextPosition = position; + if (position + literal.Length > command.Length) + return false; + + if (string.CompareOrdinal(command, position, literal, 0, literal.Length) != 0) + return false; + + nextPosition = position + literal.Length; + return nextPosition == command.Length || command[nextPosition] == ' '; + } + + private static bool TryConsumeArgument(string command, int position, CommandArgumentDescriptor descriptor, out int nextPosition) + { + nextPosition = position; + + return descriptor.Consumption switch + { + ArgumentConsumption.SingleToken => TryConsumeSingleToken(command, position, out nextPosition), + ArgumentConsumption.QuotedStringOrWord => TryConsumeQuotedStringOrWord(command, position, out nextPosition), + ArgumentConsumption.GreedyTail => TryConsumeGreedyTail(command, position, out nextPosition), + ArgumentConsumption.FixedTokenCount => TryConsumeFixedTokenCount(command, position, descriptor.TokenCount, out nextPosition), + _ => false + }; + } + + private static bool TryConsumeSingleToken(string command, int position, out int nextPosition) + { + nextPosition = position; + if (position >= command.Length) + return false; + + int cursor = position; + while (cursor < command.Length && command[cursor] != ' ') + cursor++; + + nextPosition = cursor; + return cursor > position; + } + + private static bool TryConsumeQuotedStringOrWord(string command, int position, out int nextPosition) + { + nextPosition = position; + if (position >= command.Length) + return false; + + if (command[position] != '"') + return TryConsumeSingleToken(command, position, out nextPosition); + + bool escaped = false; + for (int cursor = position + 1; cursor < command.Length; ++cursor) + { + char current = command[cursor]; + if (escaped) + { + escaped = false; + continue; + } + + if (current == '\\') + { + escaped = true; + continue; + } + + if (current == '"') + { + nextPosition = cursor + 1; + return nextPosition == command.Length || command[nextPosition] == ' '; + } + } + + return false; + } + + private static bool TryConsumeGreedyTail(string command, int position, out int nextPosition) + { + nextPosition = command.Length; + return position < command.Length; + } + + private static bool TryConsumeFixedTokenCount(string command, int position, int tokenCount, out int nextPosition) + { + nextPosition = position; + int cursor = position; + + for (int i = 0; i < tokenCount; ++i) + { + if (!TryConsumeSingleToken(command, cursor, out int tokenEnd)) + return false; + + cursor = tokenEnd; + if (i < tokenCount - 1) + { + if (cursor >= command.Length || command[cursor] != ' ') + return false; + + cursor++; + } + } + + nextPosition = cursor; + return true; + } + + private static int ResolveRedirect(int nodeIdx) + { + if (!IsValidNodeIndex(nodeIdx)) + return -1; + + HashSet visited = new(); + int current = nodeIdx; + + while (IsValidNodeIndex(current) && Nodes[current].RedirectNode >= 0) + { + if (!visited.Add(current)) + return current; + + current = Nodes[current].RedirectNode; + } + + return IsValidNodeIndex(current) ? current : -1; + } + + private static bool TryResolveArgumentTypeLayout(int protocolVersion, int parserId, out ArgumentTypeLayout layout) + { + return protocolVersion >= Protocol18Handler.MC_1_20_6_Version + ? TryResolveModernArgumentTypeLayout(protocolVersion, parserId, out layout) + : TryResolveLegacyArgumentTypeLayout(protocolVersion, parserId, out layout); + } + + private static bool TryResolveModernArgumentTypeLayout(int protocolVersion, int parserId, out ArgumentTypeLayout layout) + { + string[] registry = protocolVersion switch + { + >= Protocol18Handler.MC_1_21_6_Version => s_modernArgumentTypes1216, + >= Protocol18Handler.MC_1_21_5_Version => s_modernArgumentTypes1215, + _ => s_modernArgumentTypes1206 + }; + + if (parserId < 0 || parserId >= registry.Length) + { + layout = default; + return false; + } + + return s_argumentTypeCatalog.TryGetValue(ToCanonicalArgumentTypeName(registry[parserId]), out layout); + } + + private static bool TryResolveLegacyArgumentTypeLayout(int protocolVersion, int parserId, out ArgumentTypeLayout layout) + { + string? name; + + if (protocolVersion <= Protocol18Handler.MC_1_19_2_Version) + { + name = parserId switch + { + 1 => "brigadier:float", + 2 => "brigadier:double", + 3 => "brigadier:integer", + 4 => "brigadier:long", + 5 => "brigadier:string", + 6 => "minecraft:entity", + 8 => "minecraft:block_pos", + 9 => "minecraft:column_pos", + 10 => "minecraft:vec3", + 11 => "minecraft:vec2", + 18 => "minecraft:message", + 27 => "minecraft:rotation", + 29 => "minecraft:score_holder", + 43 => "minecraft:resource_or_tag", + 44 => "minecraft:resource", + 50 => "forge:enum", + _ => null + }; + } + else if (protocolVersion <= Protocol18Handler.MC_1_19_3_Version) + { + name = parserId switch + { + 1 => "brigadier:float", + 2 => "brigadier:double", + 3 => "brigadier:integer", + 4 => "brigadier:long", + 5 => "brigadier:string", + 6 => "minecraft:entity", + 8 => "minecraft:block_pos", + 9 => "minecraft:column_pos", + 10 => "minecraft:vec3", + 11 => "minecraft:vec2", + 18 => "minecraft:message", + 27 => "minecraft:rotation", + 29 => "minecraft:score_holder", + 41 => "minecraft:resource_or_tag", + 42 => "minecraft:resource_or_tag_key", + 43 => "minecraft:resource", + 44 => "minecraft:resource_key", + 50 => "forge:enum", + _ => null + }; + } + else if (protocolVersion <= Protocol18Handler.MC_1_20_2_Version) + { + name = parserId switch + { + 1 => "brigadier:float", + 2 => "brigadier:double", + 3 => "brigadier:integer", + 4 => "brigadier:long", + 5 => "brigadier:string", + 6 => "minecraft:entity", + 8 => "minecraft:block_pos", + 9 => "minecraft:column_pos", + 10 => "minecraft:vec3", + 11 => "minecraft:vec2", + 18 => "minecraft:message", + 27 => "minecraft:rotation", + 29 => "minecraft:score_holder", + 40 => "minecraft:time", + 41 => "minecraft:resource_or_tag", + 42 => "minecraft:resource_or_tag_key", + 43 => "minecraft:resource", + 44 => "minecraft:resource_key", + 50 when protocolVersion == Protocol18Handler.MC_1_19_4_Version => "forge:enum", + 51 when protocolVersion is >= Protocol18Handler.MC_1_20_Version and <= Protocol18Handler.MC_1_20_2_Version => "forge:enum", + _ => null + }; + } + else + { + name = parserId switch + { + 1 => "brigadier:float", + 2 => "brigadier:double", + 3 => "brigadier:integer", + 4 => "brigadier:long", + 5 => "brigadier:string", + 6 => "minecraft:entity", + 8 => "minecraft:block_pos", + 9 => "minecraft:column_pos", + 10 => "minecraft:vec3", + 11 => "minecraft:vec2", + 18 or 19 => "minecraft:message", + 27 => "minecraft:rotation", + 30 => "minecraft:score_holder", + 41 => "minecraft:time", + 42 => "minecraft:resource_or_tag", + 43 => "minecraft:resource_or_tag_key", + 44 => "minecraft:resource", + 45 => "minecraft:resource_key", + 52 => "forge:enum", + _ => null + }; + } + + if (name is null) + { + layout = s_unknownLegacyArgumentType; return true; } - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return ""; - } + return s_argumentTypeCatalog.TryGetValue(name, out layout); } - internal class ParserFloat : Parser + private static string ToCanonicalArgumentTypeName(string rawName) { - private byte Flags; - private float Min = float.MinValue, Max = float.MaxValue; - - public ParserFloat(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - if ((Flags & 0x01) > 0) - Min = dataTypes.ReadNextFloat(packetData); - if ((Flags & 0x02) > 0) - Max = dataTypes.ReadNextFloat(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "brigadier:float"; - } + return rawName.Contains(':', StringComparison.Ordinal) ? rawName : "minecraft:" + rawName; } - internal class ParserDouble : Parser + private static void Reset() { - private byte Flags; - private double Min = double.MinValue, Max = double.MaxValue; - - public ParserDouble(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - if ((Flags & 0x01) > 0) - Min = dataTypes.ReadNextDouble(packetData); - if ((Flags & 0x02) > 0) - Max = dataTypes.ReadNextDouble(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "brigadier:double"; - } + RootIdx = -1; + Nodes = Array.Empty(); + HasLoadedTree = false; + LastReadError = null; } - internal class ParserInteger : Parser + private static bool HasValidCommandTree() { - private byte Flags; - private int Min = int.MinValue, Max = int.MaxValue; - - public ParserInteger(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - if ((Flags & 0x01) > 0) - Min = dataTypes.ReadNextInt(packetData); - if ((Flags & 0x02) > 0) - Max = dataTypes.ReadNextInt(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "brigadier:integer"; - } + return HasLoadedTree && IsValidNodeIndex(RootIdx); } - internal class ParserLong : Parser + private static bool IsValidNodeIndex(int nodeIdx) { - private byte Flags; - private long Min = long.MinValue, Max = long.MaxValue; - - public ParserLong(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - if ((Flags & 0x01) > 0) - Min = dataTypes.ReadNextLong(packetData); - if ((Flags & 0x02) > 0) - Max = dataTypes.ReadNextLong(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "brigadier:long"; - } + return nodeIdx >= 0 && nodeIdx < Nodes.Length; } - internal class ParserString : Parser + private static Dictionary CreateArgumentTypeCatalog() { - private StringType Type; + Dictionary catalog = new(StringComparer.Ordinal); - private enum StringType { SINGLE_WORD, QUOTABLE_PHRASE, GREEDY_PHRASE }; - - public ParserString(DataTypes dataTypes, Queue packetData) + static void Add( + Dictionary items, + string name, + ArgumentPayloadKind payloadKind = ArgumentPayloadKind.None, + ArgumentConsumption consumption = ArgumentConsumption.SingleToken, + int tokenCount = 1, + bool isSigned = false) { - Type = (StringType)dataTypes.ReadNextVarInt(packetData); + items[name] = new ArgumentTypeLayout(name, payloadKind, consumption, tokenCount, isSigned); } - public override bool Check(string text) + static void AddFixedTokens(Dictionary items, string name, int tokenCount) { - return true; + Add(items, name, consumption: ArgumentConsumption.FixedTokenCount, tokenCount: tokenCount); } - public override int GetArgCnt() - { - return 1; - } + Add(catalog, "brigadier:bool"); + Add(catalog, "brigadier:float", ArgumentPayloadKind.BrigadierFloat); + Add(catalog, "brigadier:double", ArgumentPayloadKind.BrigadierDouble); + Add(catalog, "brigadier:integer", ArgumentPayloadKind.BrigadierInteger); + Add(catalog, "brigadier:long", ArgumentPayloadKind.BrigadierLong); + Add(catalog, "brigadier:string", ArgumentPayloadKind.BrigadierString); + Add(catalog, "minecraft:entity", ArgumentPayloadKind.Entity); + Add(catalog, "minecraft:game_profile"); + AddFixedTokens(catalog, "minecraft:block_pos", 3); + AddFixedTokens(catalog, "minecraft:column_pos", 2); + AddFixedTokens(catalog, "minecraft:vec3", 3); + AddFixedTokens(catalog, "minecraft:vec2", 2); + Add(catalog, "minecraft:block_state"); + Add(catalog, "minecraft:block_predicate"); + Add(catalog, "minecraft:item_stack"); + Add(catalog, "minecraft:item_predicate"); + Add(catalog, "minecraft:color"); + Add(catalog, "minecraft:hex_color"); + Add(catalog, "minecraft:component"); + Add(catalog, "minecraft:style"); + Add(catalog, "minecraft:message", consumption: ArgumentConsumption.GreedyTail, isSigned: true); + Add(catalog, "minecraft:nbt_compound_tag"); + Add(catalog, "minecraft:nbt_tag"); + Add(catalog, "minecraft:nbt_path"); + Add(catalog, "minecraft:objective"); + Add(catalog, "minecraft:objective_criteria"); + Add(catalog, "minecraft:operation"); + Add(catalog, "minecraft:particle"); + Add(catalog, "minecraft:angle"); + AddFixedTokens(catalog, "minecraft:rotation", 2); + Add(catalog, "minecraft:scoreboard_slot"); + Add(catalog, "minecraft:score_holder", ArgumentPayloadKind.ScoreHolder); + Add(catalog, "minecraft:swizzle"); + Add(catalog, "minecraft:team"); + Add(catalog, "minecraft:item_slot"); + Add(catalog, "minecraft:item_slots"); + Add(catalog, "minecraft:resource_location"); + Add(catalog, "minecraft:function"); + Add(catalog, "minecraft:entity_anchor"); + Add(catalog, "minecraft:int_range"); + Add(catalog, "minecraft:float_range"); + Add(catalog, "minecraft:dimension"); + Add(catalog, "minecraft:gamemode"); + Add(catalog, "minecraft:time", ArgumentPayloadKind.Time); + Add(catalog, "minecraft:resource_or_tag", ArgumentPayloadKind.RegistryKey); + Add(catalog, "minecraft:resource_or_tag_key", ArgumentPayloadKind.RegistryKey); + Add(catalog, "minecraft:resource", ArgumentPayloadKind.RegistryKey); + Add(catalog, "minecraft:resource_key", ArgumentPayloadKind.RegistryKey); + Add(catalog, "minecraft:resource_selector", ArgumentPayloadKind.RegistryKey); + Add(catalog, "minecraft:template_mirror"); + Add(catalog, "minecraft:template_rotation"); + Add(catalog, "minecraft:heightmap"); + Add(catalog, "minecraft:loot_table"); + Add(catalog, "minecraft:loot_predicate"); + Add(catalog, "minecraft:loot_modifier"); + Add(catalog, "minecraft:dialog"); + Add(catalog, "minecraft:uuid"); + Add(catalog, "forge:enum", ArgumentPayloadKind.ForgeEnum); - public override string GetName() - { - return "brigadier:string"; - } + return catalog; } - internal class ParserEntity : Parser + private enum CommandNodeKind : byte { - private byte Flags; - - public ParserEntity(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:entity"; - } + Root = 0, + Literal = 1, + Argument = 2 } - internal class ParserBlockPos : Parser + private enum ArgumentConsumption { - - public ParserBlockPos(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 3; - } - - public override string GetName() - { - return "minecraft:block_pos"; - } + SingleToken, + QuotedStringOrWord, + GreedyTail, + FixedTokenCount } - internal class ParserColumnPos : Parser + private enum ArgumentPayloadKind { - - public ParserColumnPos(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 3; - } - - public override string GetName() - { - return "minecraft:column_pos"; - } + None, + BrigadierFloat, + BrigadierDouble, + BrigadierInteger, + BrigadierLong, + BrigadierString, + Entity, + ScoreHolder, + Time, + RegistryKey, + ForgeEnum } - internal class ParserVec3 : Parser + private sealed record CommandNode( + byte Flags, + int[] Children, + int RedirectNode = -1, + string? Name = null, + CommandArgumentDescriptor? Argument = null, + string? SuggestionsType = null, + int ParserId = -1) { - - public ParserVec3(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 3; - } - - public override string GetName() - { - return "minecraft:vec3"; - } + public CommandNodeKind Kind => (CommandNodeKind)(Flags & NodeTypeMask); + public bool IsExecutable => (Flags & NodeExecutableFlag) != 0; + public bool IsRestricted => (Flags & NodeRestrictedFlag) != 0; } - internal class ParserVec2 : Parser + private readonly record struct CommandArgumentDescriptor( + string Name, + ArgumentConsumption Consumption, + int TokenCount = 1, + bool IsSigned = false); + + private readonly struct ArgumentTypeLayout { + public string Name { get; } + public ArgumentPayloadKind PayloadKind { get; } + public ArgumentConsumption Consumption { get; } + public int TokenCount { get; } + public bool IsSigned { get; } - public ParserVec2(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) + public ArgumentTypeLayout( + string name, + ArgumentPayloadKind payloadKind = ArgumentPayloadKind.None, + ArgumentConsumption consumption = ArgumentConsumption.SingleToken, + int tokenCount = 1, + bool isSigned = false) { - return true; + Name = name; + PayloadKind = payloadKind; + Consumption = consumption; + TokenCount = tokenCount; + IsSigned = isSigned; } - public override int GetArgCnt() + public CommandArgumentDescriptor CreateDescriptor() { - return 2; + return new CommandArgumentDescriptor(Name, Consumption, TokenCount, IsSigned); } - public override string GetName() + public CommandArgumentDescriptor CreateDescriptor(ArgumentConsumption consumption) { - return "minecraft:vec2"; - } - } - - internal class ParserRotation : Parser - { - - public ParserRotation(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 2; - } - - public override string GetName() - { - return "minecraft:rotation"; - } - } - - internal class ParserMessage : Parser - { - public ParserMessage(DataTypes dataTypes, Queue packetData) { } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:message"; - } - } - - internal class ParserScoreHolder : Parser - { - private byte Flags; - - public ParserScoreHolder(DataTypes dataTypes, Queue packetData) - { - Flags = dataTypes.ReadNextByte(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:score_holder"; - } - } - - internal class ParserRange : Parser - { - private bool Decimals; - - public ParserRange(DataTypes dataTypes, Queue packetData) - { - Decimals = dataTypes.ReadNextBool(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:range"; - } - } - - internal class ParserResourceOrTag : Parser - { - private string Registry; - - public ParserResourceOrTag(DataTypes dataTypes, Queue packetData) - { - Registry = dataTypes.ReadNextString(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:resource_or_tag"; - } - } - - internal class ParserResource : Parser - { - private string Registry; - - public ParserResource(DataTypes dataTypes, Queue packetData) - { - Registry = dataTypes.ReadNextString(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:resource"; - } - } - - /// - /// Undocumented parser type for 1.19.4+ - /// - internal class ParserTime : Parser - { - public ParserTime(DataTypes dataTypes, Queue packetData) - { - dataTypes.ReadNextInt(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "minecraft:time"; - } - } - - internal class ParserForgeEnum : Parser - { - public ParserForgeEnum(DataTypes dataTypes, Queue packetData) - { - dataTypes.ReadNextString(packetData); - } - - public override bool Check(string text) - { - return true; - } - - public override int GetArgCnt() - { - return 1; - } - - public override string GetName() - { - return "forge:enum"; + return new CommandArgumentDescriptor(Name, consumption, TokenCount, IsSigned); } } } diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs index 0757dcb8..7a7e9eb6 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs @@ -118,9 +118,9 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly) - { 0x03, PacketTypesOut.MessageAcknowledgment }, // - { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 - { 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x03, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x04, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x05, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) { 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) { 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) { 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) @@ -147,25 +147,24 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) { 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) { 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) - { 0x20, PacketTypesOut.PlayerSession }, // Added in 1.19.3 - { 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) - { 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) - { 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) - { 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) - { 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) - { 0x26, PacketTypesOut.SelectTrade }, // - { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) - { 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) - { 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) - { 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, // - { 0x2B, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) - { 0x2C, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) - { 0x2D, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) - { 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) - { 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing) - { 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) - { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) + { 0x20, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x21, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x22, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x23, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x24, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x25, PacketTypesOut.SelectTrade }, // + { 0x26, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) + { 0x27, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x28, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) + { 0x29, PacketTypesOut.UpdateCommandBlockMinecart }, // + { 0x2A, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x2B, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) + { 0x2C, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) + { 0x2D, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) + { 0x2E, PacketTypesOut.Animation }, // (Wiki name: Swing) + { 0x2F, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x30, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x31, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; protected override Dictionary GetListIn() => typeIn; diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1202.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1202.cs index 1e771589..7781a9c9 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1202.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1202.cs @@ -178,7 +178,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x34, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) { 0x35, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; - + private readonly Dictionary configurationTypesIn = new() { { 0x00, ConfigurationPacketTypesIn.PluginMessage }, @@ -201,7 +201,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x04, ConfigurationPacketTypesOut.Pong }, { 0x05, ConfigurationPacketTypesOut.ResourcePackResponse } }; - + protected override Dictionary GetListIn() => typeIn; protected override Dictionary GetListOut() => typeOut; protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1204.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1204.cs index 0f7bcd04..46721231 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1204.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1204.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; namespace MinecraftClient.Protocol.Handlers.PacketPalettes; public class PacketPalette1204 : PacketTypePalette - { - private readonly Dictionary typeIn = new() +{ + private readonly Dictionary typeIn = new() { { 0x00, PacketTypesIn.Bundle }, // Added in 1.19.4 { 0x01, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) @@ -125,7 +125,7 @@ public class PacketPalette1204 : PacketTypePalette { 0x74, PacketTypesIn.Tags }, // (Wiki name: Update Tags) }; - private readonly Dictionary typeOut = new() + private readonly Dictionary typeOut = new() { { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) @@ -184,7 +184,7 @@ public class PacketPalette1204 : PacketTypePalette { 0x36, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; - private readonly Dictionary configurationTypesIn = new() + private readonly Dictionary configurationTypesIn = new() { { 0x00, ConfigurationPacketTypesIn.PluginMessage }, { 0x01, ConfigurationPacketTypesIn.Disconnect }, @@ -198,7 +198,7 @@ public class PacketPalette1204 : PacketTypePalette { 0x09, ConfigurationPacketTypesIn.UpdateTags }, }; - private readonly Dictionary configurationTypesOut = new() + private readonly Dictionary configurationTypesOut = new() { { 0x00, ConfigurationPacketTypesOut.ClientInformation }, { 0x01, ConfigurationPacketTypesOut.PluginMessage }, @@ -207,9 +207,9 @@ public class PacketPalette1204 : PacketTypePalette { 0x04, ConfigurationPacketTypesOut.Pong }, { 0x05, ConfigurationPacketTypesOut.ResourcePackResponse } }; - - protected override Dictionary GetListIn() => typeIn; - protected override Dictionary GetListOut() => typeOut; - protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; - protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; - } \ No newline at end of file + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1206.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1206.cs new file mode 100644 index 00000000..f053d41e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1206.cs @@ -0,0 +1,230 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1206 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Added in 1.19.4 + { 0x01, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) + { 0x02, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb) + { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound)) + { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics) + { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) + { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage) + { 0x07, PacketTypesIn.BlockEntityData }, // + { 0x08, PacketTypesIn.BlockAction }, // + { 0x09, PacketTypesIn.BlockChange }, // (Wiki name: Block Update) + { 0x0A, PacketTypesIn.BossBar }, // + { 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty) + { 0x0C, PacketTypesIn.ChunkBatchFinished }, // Added in 1.20.2 + { 0x0D, PacketTypesIn.ChunkBatchStarted }, // Added in 1.20.2 + { 0x0E, PacketTypesIn.ChunksBiomes }, // Added in 1.19.4 + { 0x0F, PacketTypesIn.ClearTiles }, // + { 0x10, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response) + { 0x11, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands) + { 0x12, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound)) + { 0x13, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content) + { 0x14, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property) + { 0x15, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot) + { 0x16, PacketTypesIn.CookieRequest }, // Added in 1.20.6 + { 0x17, PacketTypesIn.SetCooldown }, // + { 0x18, PacketTypesIn.ChatSuggestions }, // Added in 1.19.1 + { 0x19, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) + { 0x1A, PacketTypesIn.DamageEvent }, // Added in 1.19.4 + { 0x1B, PacketTypesIn.DebugSample }, // Added in 1.20.6 + { 0x1C, PacketTypesIn.HideMessage }, // Added in 1.19.1 + { 0x1D, PacketTypesIn.Disconnect }, // + { 0x1E, PacketTypesIn.ProfilelessChatMessage }, // Added in 1.19.3 (Wiki name: Disguised Chat Message) + { 0x1F, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) + { 0x20, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) + { 0x21, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) + { 0x22, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) + { 0x23, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) + { 0x24, PacketTypesIn.HurtAnimation }, // Added in 1.19.4 + { 0x25, PacketTypesIn.InitializeWorldBorder }, // + { 0x26, PacketTypesIn.KeepAlive }, // + { 0x27, PacketTypesIn.ChunkData }, // + { 0x28, PacketTypesIn.Effect }, // (Wiki name: World Event) + { 0x29, PacketTypesIn.Particle }, // Changed in 1.19 (Wiki name: Level Particle) (No need to be implemented) + { 0x2A, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) + { 0x2B, PacketTypesIn.JoinGame }, // Changed in 1.20.2 (Wiki name: Login (play)) + { 0x2C, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) + { 0x2D, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) + { 0x2E, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) + { 0x2F, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation) + { 0x30, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation) + { 0x31, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle) + { 0x32, PacketTypesIn.OpenBook }, // + { 0x33, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen) + { 0x34, PacketTypesIn.OpenSignEditor }, // + { 0x35, PacketTypesIn.Ping }, // (Wiki name: Ping (play)) + { 0x36, PacketTypesIn.PingResponse }, // Added in 1.20.2 + { 0x37, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe) + { 0x38, PacketTypesIn.PlayerAbilities }, // + { 0x39, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message) + { 0x3A, PacketTypesIn.EndCombatEvent }, // (Wiki name: End Combat) + { 0x3B, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Enter Combat) + { 0x3C, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Combat Death) + { 0x3D, PacketTypesIn.PlayerRemove }, // Added in 1.19.3 (Not used) + { 0x3E, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) + { 0x3F, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) + { 0x40, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Synchronize Player Position) + { 0x41, PacketTypesIn.UnlockRecipes }, // (Wiki name: Update Recipe Book) + { 0x42, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) + { 0x43, PacketTypesIn.RemoveEntityEffect }, // + { 0x44, PacketTypesIn.ResetScore }, // Added in 1.20.3 + { 0x45, PacketTypesIn.RemoveResourcePack }, // Added in 1.20.3 + { 0x46, PacketTypesIn.ResourcePackSend }, // (Wiki name: Add Resource pack (play)) + { 0x47, PacketTypesIn.Respawn }, // Changed in 1.20.2 + { 0x48, PacketTypesIn.EntityHeadLook }, // (Wiki name: Set Head Rotation) + { 0x49, PacketTypesIn.MultiBlockChange }, // (Wiki name: Update Section Blocks) + { 0x4A, PacketTypesIn.SelectAdvancementTab }, // + { 0x4B, PacketTypesIn.ServerData }, // Added in 1.19 + { 0x4C, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text) + { 0x4D, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center) + { 0x4E, PacketTypesIn.WorldBorderLerpSize }, // + { 0x4F, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size) + { 0x50, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay) + { 0x51, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance) + { 0x52, PacketTypesIn.Camera }, // (Wiki name: Set Camera) + { 0x53, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Held Item) + { 0x54, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Center Chunk) + { 0x55, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Render Distance) + { 0x56, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position) + { 0x57, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective) + { 0x58, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata) + { 0x59, PacketTypesIn.AttachEntity }, // (Wiki name: Link Entities) + { 0x5A, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Velocity) + { 0x5B, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment) + { 0x5C, PacketTypesIn.SetExperience }, // Changed in 1.20.2 + { 0x5D, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health) + { 0x5E, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Update Objectives) - Changed in 1.20.3 + { 0x5F, PacketTypesIn.SetPassengers }, // + { 0x60, PacketTypesIn.Teams }, // (Wiki name: Update Teams) + { 0x61, PacketTypesIn.UpdateScore }, // (Wiki name: Update Score) + { 0x62, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance) + { 0x63, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test) + { 0x64, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time) + { 0x65, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) + { 0x66, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Title Animation Times) + { 0x67, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) + { 0x68, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) (No need to be implemented) + { 0x69, PacketTypesIn.StartConfiguration }, // Added in 1.20.2 + { 0x6A, PacketTypesIn.StopSound }, // + { 0x6B, PacketTypesIn.StoreCookie }, // Added in 1.20.6 + { 0x6C, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) + { 0x6D, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Set Tab List Header And Footer) + { 0x6E, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tag Query Response) + { 0x6F, PacketTypesIn.CollectItem }, // (Wiki name: Pickup Item) + { 0x70, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) + { 0x71, PacketTypesIn.SetTickingState }, // Added in 1.20.3 + { 0x72, PacketTypesIn.StepTick }, // Added in 1.20.3 + { 0x73, PacketTypesIn.Transfer }, // Added in 1.20.6 + { 0x74, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) (Unused) + { 0x75, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) + { 0x76, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) + { 0x77, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) (Unused) + { 0x78, PacketTypesIn.Tags }, // (Wiki name: Update Tags) + { 0x79, PacketTypesIn.ProjectilePower }, // Added in 1.20.6 + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) + { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) + { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficulty) + { 0x03, PacketTypesOut.MessageAcknowledgment }, // Added in 1.19.1 + { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x05, PacketTypesOut.SignedChatCommand }, // Added in 1.20.6 + { 0x06, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x07, PacketTypesOut.PlayerSession }, // Added in 1.19.3 + { 0x08, PacketTypesOut.ChunkBatchReceived }, // Added in 1.20.2 + { 0x09, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) + { 0x0A, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) + { 0x0B, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) + { 0x0C, PacketTypesOut.AcknowledgeConfiguration }, // Added in 1.20.2 + { 0x0D, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) + { 0x0E, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) + { 0x0F, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) + { 0x10, PacketTypesOut.ChangeContainerSlotState }, // Added in 1.20.3 + { 0x11, PacketTypesOut.CookieResponse }, // Added in 1.20.6 + { 0x12, PacketTypesOut.PluginMessage }, // (Wiki name: Serverbound Plugin Message) + { 0x13, PacketTypesOut.DebugSampleSubscription }, // Added in 1.20.6 + { 0x14, PacketTypesOut.EditBook }, // + { 0x15, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) + { 0x16, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) + { 0x17, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) + { 0x18, PacketTypesOut.KeepAlive }, // (Wiki name: Serverbound Keep Alive (play)) + { 0x19, PacketTypesOut.LockDifficulty }, // + { 0x1A, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) + { 0x1B, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) + { 0x1C, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) + { 0x1D, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) + { 0x1E, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) + { 0x1F, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) + { 0x20, PacketTypesOut.PickItem }, // + { 0x21, PacketTypesOut.PingRequest }, // Added in 1.20.2 + { 0x22, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) + { 0x23, PacketTypesOut.PlayerAbilities }, // + { 0x24, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) + { 0x25, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) + { 0x26, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) + { 0x27, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) + { 0x28, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x29, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x2A, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x2B, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x2C, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x2D, PacketTypesOut.SelectTrade }, // + { 0x2E, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (No need to be implemented yet) + { 0x2F, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x30, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Program Command Block) + { 0x31, PacketTypesOut.UpdateCommandBlockMinecart }, // (Wiki name: Program Command Block Minecart) + { 0x32, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x33, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Program Jigsaw Block) + { 0x34, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Program Structure Block) + { 0x35, PacketTypesOut.UpdateSign }, // (Wiki name: Update Sign) + { 0x36, PacketTypesOut.Animation }, // (Wiki name: Swing Arm) + { 0x37, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x38, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x39, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks } + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette121.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette121.cs new file mode 100644 index 00000000..8c35b2c8 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette121.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette121 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Added in 1.19.4 + { 0x01, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) + { 0x02, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb) + { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound)) + { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics) + { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) + { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage) + { 0x07, PacketTypesIn.BlockEntityData }, // + { 0x08, PacketTypesIn.BlockAction }, // + { 0x09, PacketTypesIn.BlockChange }, // (Wiki name: Block Update) + { 0x0A, PacketTypesIn.BossBar }, // + { 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty) + { 0x0C, PacketTypesIn.ChunkBatchFinished }, // Added in 1.20.2 + { 0x0D, PacketTypesIn.ChunkBatchStarted }, // Added in 1.20.2 + { 0x0E, PacketTypesIn.ChunksBiomes }, // Added in 1.19.4 + { 0x0F, PacketTypesIn.ClearTiles }, // + { 0x10, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response) + { 0x11, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands) + { 0x12, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound)) + { 0x13, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content) + { 0x14, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property) + { 0x15, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot) + { 0x16, PacketTypesIn.CookieRequest }, // Added in 1.20.6 + { 0x17, PacketTypesIn.SetCooldown }, // + { 0x18, PacketTypesIn.ChatSuggestions }, // Added in 1.19.1 + { 0x19, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) + { 0x1A, PacketTypesIn.DamageEvent }, // Added in 1.19.4 + { 0x1B, PacketTypesIn.DebugSample }, // Added in 1.20.6 + { 0x1C, PacketTypesIn.HideMessage }, // Added in 1.19.1 + { 0x1D, PacketTypesIn.Disconnect }, // + { 0x1E, PacketTypesIn.ProfilelessChatMessage }, // Added in 1.19.3 (Wiki name: Disguised Chat Message) + { 0x1F, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) + { 0x20, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) + { 0x21, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) + { 0x22, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) + { 0x23, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) + { 0x24, PacketTypesIn.HurtAnimation }, // Added in 1.19.4 + { 0x25, PacketTypesIn.InitializeWorldBorder }, // + { 0x26, PacketTypesIn.KeepAlive }, // + { 0x27, PacketTypesIn.ChunkData }, // + { 0x28, PacketTypesIn.Effect }, // (Wiki name: World Event) + { 0x29, PacketTypesIn.Particle }, // Changed in 1.19 (Wiki name: Level Particle) (No need to be implemented) + { 0x2A, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) + { 0x2B, PacketTypesIn.JoinGame }, // Changed in 1.20.2 (Wiki name: Login (play)) + { 0x2C, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) + { 0x2D, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) + { 0x2E, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) + { 0x2F, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation) + { 0x30, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation) + { 0x31, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle) + { 0x32, PacketTypesIn.OpenBook }, // + { 0x33, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen) + { 0x34, PacketTypesIn.OpenSignEditor }, // + { 0x35, PacketTypesIn.Ping }, // (Wiki name: Ping (play)) + { 0x36, PacketTypesIn.PingResponse }, // Added in 1.20.2 + { 0x37, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe) + { 0x38, PacketTypesIn.PlayerAbilities }, // + { 0x39, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message) + { 0x3A, PacketTypesIn.EndCombatEvent }, // (Wiki name: End Combat) + { 0x3B, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Enter Combat) + { 0x3C, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Combat Death) + { 0x3D, PacketTypesIn.PlayerRemove }, // Added in 1.19.3 (Not used) + { 0x3E, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) + { 0x3F, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) + { 0x40, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Synchronize Player Position) + { 0x41, PacketTypesIn.UnlockRecipes }, // (Wiki name: Update Recipe Book) + { 0x42, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) + { 0x43, PacketTypesIn.RemoveEntityEffect }, // + { 0x44, PacketTypesIn.ResetScore }, // Added in 1.20.3 + { 0x45, PacketTypesIn.RemoveResourcePack }, // Added in 1.20.3 + { 0x46, PacketTypesIn.ResourcePackSend }, // (Wiki name: Add Resource pack (play)) + { 0x47, PacketTypesIn.Respawn }, // Changed in 1.20.2 + { 0x48, PacketTypesIn.EntityHeadLook }, // (Wiki name: Set Head Rotation) + { 0x49, PacketTypesIn.MultiBlockChange }, // (Wiki name: Update Section Blocks) + { 0x4A, PacketTypesIn.SelectAdvancementTab }, // + { 0x4B, PacketTypesIn.ServerData }, // Added in 1.19 + { 0x4C, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text) + { 0x4D, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center) + { 0x4E, PacketTypesIn.WorldBorderLerpSize }, // + { 0x4F, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size) + { 0x50, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay) + { 0x51, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance) + { 0x52, PacketTypesIn.Camera }, // (Wiki name: Set Camera) + { 0x53, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Held Item) + { 0x54, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Center Chunk) + { 0x55, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Render Distance) + { 0x56, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position) + { 0x57, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective) + { 0x58, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata) + { 0x59, PacketTypesIn.AttachEntity }, // (Wiki name: Link Entities) + { 0x5A, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Velocity) + { 0x5B, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment) + { 0x5C, PacketTypesIn.SetExperience }, // Changed in 1.20.2 + { 0x5D, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health) + { 0x5E, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Update Objectives) - Changed in 1.20.3 + { 0x5F, PacketTypesIn.SetPassengers }, // + { 0x60, PacketTypesIn.Teams }, // (Wiki name: Update Teams) + { 0x61, PacketTypesIn.UpdateScore }, // (Wiki name: Update Score) + { 0x62, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance) + { 0x63, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test) + { 0x64, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time) + { 0x65, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) + { 0x66, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Title Animation Times) + { 0x67, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) + { 0x68, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) (No need to be implemented) + { 0x69, PacketTypesIn.StartConfiguration }, // Added in 1.20.2 + { 0x6A, PacketTypesIn.StopSound }, // + { 0x6B, PacketTypesIn.StoreCookie }, // Added in 1.20.6 + { 0x6C, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) + { 0x6D, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Set Tab List Header And Footer) + { 0x6E, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tag Query Response) + { 0x6F, PacketTypesIn.CollectItem }, // (Wiki name: Pickup Item) + { 0x70, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) + { 0x71, PacketTypesIn.SetTickingState }, // Added in 1.20.3 + { 0x72, PacketTypesIn.StepTick }, // Added in 1.20.3 + { 0x73, PacketTypesIn.Transfer }, // Added in 1.20.6 + { 0x74, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) (Unused) + { 0x75, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) + { 0x76, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) + { 0x77, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) (Unused) + { 0x78, PacketTypesIn.Tags }, // (Wiki name: Update Tags) + { 0x79, PacketTypesIn.ProjectilePower }, // Added in 1.20.6 + { 0x7A, PacketTypesIn.CustomReportDetails }, // Added in 1.21 + { 0x7B, PacketTypesIn.ServerLinks } // Added in 1.21 + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) + { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) + { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficulty) + { 0x03, PacketTypesOut.MessageAcknowledgment }, // Added in 1.19.1 + { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x05, PacketTypesOut.SignedChatCommand }, // Added in 1.20.6 + { 0x06, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x07, PacketTypesOut.PlayerSession }, // Added in 1.19.3 + { 0x08, PacketTypesOut.ChunkBatchReceived }, // Added in 1.20.2 + { 0x09, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) + { 0x0A, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) + { 0x0B, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) + { 0x0C, PacketTypesOut.AcknowledgeConfiguration }, // Added in 1.20.2 + { 0x0D, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) + { 0x0E, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) + { 0x0F, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) + { 0x10, PacketTypesOut.ChangeContainerSlotState }, // Added in 1.20.3 + { 0x11, PacketTypesOut.CookieResponse }, // Added in 1.20.6 + { 0x12, PacketTypesOut.PluginMessage }, // (Wiki name: Serverbound Plugin Message) + { 0x13, PacketTypesOut.DebugSampleSubscription }, // Added in 1.20.6 + { 0x14, PacketTypesOut.EditBook }, // + { 0x15, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) + { 0x16, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) + { 0x17, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) + { 0x18, PacketTypesOut.KeepAlive }, // (Wiki name: Serverbound Keep Alive (play)) + { 0x19, PacketTypesOut.LockDifficulty }, // + { 0x1A, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) + { 0x1B, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) + { 0x1C, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) + { 0x1D, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) + { 0x1E, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) + { 0x1F, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) + { 0x20, PacketTypesOut.PickItem }, // + { 0x21, PacketTypesOut.PingRequest }, // Added in 1.20.2 + { 0x22, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) + { 0x23, PacketTypesOut.PlayerAbilities }, // + { 0x24, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) + { 0x25, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) + { 0x26, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) + { 0x27, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) + { 0x28, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x29, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x2A, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x2B, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x2C, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x2D, PacketTypesOut.SelectTrade }, // + { 0x2E, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (No need to be implemented yet) + { 0x2F, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x30, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Program Command Block) + { 0x31, PacketTypesOut.UpdateCommandBlockMinecart }, // (Wiki name: Program Command Block Minecart) + { 0x32, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x33, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Program Jigsaw Block) + { 0x34, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Program Structure Block) + { 0x35, PacketTypesOut.UpdateSign }, // (Wiki name: Update Sign) + { 0x36, PacketTypesOut.Animation }, // (Wiki name: Swing Arm) + { 0x37, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x38, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x39, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, // Added in 1.21 (Not used) + { 0x10, ConfigurationPacketTypesIn.ServerLinks } // Added in 1.21 (Not used) + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1212.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1212.cs new file mode 100644 index 00000000..e5aa2e63 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1212.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1212 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.SpawnExperienceOrb }, // Add Experience Orb + { 0x03, PacketTypesIn.EntityAnimation }, // Animate + { 0x04, PacketTypesIn.Statistics }, // Award Stats + { 0x05, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x06, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x07, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x08, PacketTypesIn.BlockAction }, // Block Event + { 0x09, PacketTypesIn.BlockChange }, // Block Update + { 0x0A, PacketTypesIn.BossBar }, // Boss Event + { 0x0B, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0C, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0D, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0E, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0F, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x10, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x11, PacketTypesIn.DeclareCommands }, // Commands + { 0x12, PacketTypesIn.CloseWindow }, // Container Close + { 0x13, PacketTypesIn.WindowItems }, // Container Set Content + { 0x14, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x15, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x16, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x17, PacketTypesIn.SetCooldown }, // Cooldown + { 0x18, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x19, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x1A, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1B, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1C, PacketTypesIn.HideMessage }, // Delete Chat + { 0x1D, PacketTypesIn.Disconnect }, // Disconnect + { 0x1E, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x1F, PacketTypesIn.EntityStatus }, // Entity Event + { 0x20, PacketTypesIn.EntityPositionSync }, // Entity Position Sync (new in 1.21.2) + { 0x21, PacketTypesIn.Explosion }, // Explode + { 0x22, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x23, PacketTypesIn.ChangeGameState }, // Game Event + { 0x24, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open + { 0x25, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x26, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x27, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x28, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x29, PacketTypesIn.Effect }, // Level Event + { 0x2A, PacketTypesIn.Particle }, // Level Particles + { 0x2B, PacketTypesIn.UpdateLight }, // Light Update + { 0x2C, PacketTypesIn.JoinGame }, // Login + { 0x2D, PacketTypesIn.MapData }, // Map Item Data + { 0x2E, PacketTypesIn.TradeList }, // Merchant Offers + { 0x2F, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x30, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x31, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track (new in 1.21.2) + { 0x32, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x33, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x34, PacketTypesIn.OpenBook }, // Open Book + { 0x35, PacketTypesIn.OpenWindow }, // Open Screen + { 0x36, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x37, PacketTypesIn.Ping }, // Ping + { 0x38, PacketTypesIn.PingResponse }, // Pong Response + { 0x39, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x3A, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x3B, PacketTypesIn.ChatMessage }, // Player Chat + { 0x3C, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x3D, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x3E, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x3F, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x40, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x41, PacketTypesIn.FacePlayer }, // Player Look At + { 0x42, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x43, PacketTypesIn.PlayerRotation }, // Player Rotation (new in 1.21.2) + { 0x44, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add (new in 1.21.2, replaces UnlockRecipes) + { 0x45, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove (new in 1.21.2) + { 0x46, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings (new in 1.21.2) + { 0x47, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x48, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x49, PacketTypesIn.ResetScore }, // Reset Score + { 0x4A, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x4B, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x4C, PacketTypesIn.Respawn }, // Respawn + { 0x4D, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x4E, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x4F, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x50, PacketTypesIn.ServerData }, // Server Data + { 0x51, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x52, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x53, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x54, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x55, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x56, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x57, PacketTypesIn.Camera }, // Set Camera + { 0x58, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x59, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x5A, PacketTypesIn.SetCursorItem }, // Set Cursor Item (new in 1.21.2) + { 0x5B, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x5C, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x5D, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x5E, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x5F, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x60, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x61, PacketTypesIn.SetExperience }, // Set Experience + { 0x62, PacketTypesIn.UpdateHealth }, // Set Health + { 0x63, PacketTypesIn.SetHeldSlot }, // Set Held Slot (new in 1.21.2, replaces HeldItemChange) + { 0x64, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x65, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x66, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory (new in 1.21.2) + { 0x67, PacketTypesIn.Teams }, // Set Player Team + { 0x68, PacketTypesIn.UpdateScore }, // Set Score + { 0x69, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x6A, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x6B, PacketTypesIn.TimeUpdate }, // Set Time + { 0x6C, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x6D, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x6E, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x6F, PacketTypesIn.SoundEffect }, // Sound + { 0x70, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x71, PacketTypesIn.StopSound }, // Stop Sound + { 0x72, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x73, PacketTypesIn.SystemChat }, // System Chat + { 0x74, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x75, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x76, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x77, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x78, PacketTypesIn.SetTickingState }, // Ticking State + { 0x79, PacketTypesIn.StepTick }, // Ticking Step + { 0x7A, PacketTypesIn.Transfer }, // Transfer + { 0x7B, PacketTypesIn.Advancements }, // Update Advancements + { 0x7C, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x7D, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x7E, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x7F, PacketTypesIn.Tags }, // Update Tags + { 0x80, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x81, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x82, PacketTypesIn.ServerLinks } // Server Links + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected (new in 1.21.2) + { 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x04, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x05, PacketTypesOut.ChatCommand }, // Chat Command + { 0x06, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x07, PacketTypesOut.ChatMessage }, // Chat + { 0x08, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x09, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0A, PacketTypesOut.ClientStatus }, // Client Command + { 0x0B, PacketTypesOut.ClientTickEnd }, // Client Tick End (new in 1.21.2) + { 0x0C, PacketTypesOut.ClientSettings }, // Client Information + { 0x0D, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x0E, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x0F, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x10, PacketTypesOut.ClickWindow }, // Container Click + { 0x11, PacketTypesOut.CloseWindow }, // Container Close + { 0x12, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x13, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x14, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x15, PacketTypesOut.DebugSampleSubscription }, // Debug Sample Subscription + { 0x16, PacketTypesOut.EditBook }, // Edit Book + { 0x17, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x18, PacketTypesOut.InteractEntity }, // Interact + { 0x19, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1A, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1B, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1C, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1D, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x1E, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x1F, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x20, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x21, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x22, PacketTypesOut.PickItem }, // Pick Item + { 0x23, PacketTypesOut.PingRequest }, // Ping Request + { 0x24, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x25, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x26, PacketTypesOut.PlayerDigging }, // Player Action + { 0x27, PacketTypesOut.EntityAction }, // Player Command + { 0x28, PacketTypesOut.SteerVehicle }, // Player Input + { 0x29, PacketTypesOut.Pong }, // Pong + { 0x2A, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2B, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x2C, PacketTypesOut.NameItem }, // Rename Item + { 0x2D, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x2E, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x2F, PacketTypesOut.SelectTrade }, // Select Trade + { 0x30, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x31, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x32, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x33, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x34, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x35, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x36, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x37, PacketTypesOut.UpdateSign }, // Sign Update + { 0x38, PacketTypesOut.Animation }, // Swing + { 0x39, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x3A, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x3B, PacketTypesOut.UseItem }, // Use Item + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks } + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1214.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1214.cs new file mode 100644 index 00000000..a7b90eca --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1214.cs @@ -0,0 +1,245 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1214 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.SpawnExperienceOrb }, // Add Experience Orb + { 0x03, PacketTypesIn.EntityAnimation }, // Animate + { 0x04, PacketTypesIn.Statistics }, // Award Stats + { 0x05, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x06, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x07, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x08, PacketTypesIn.BlockAction }, // Block Event + { 0x09, PacketTypesIn.BlockChange }, // Block Update + { 0x0A, PacketTypesIn.BossBar }, // Boss Event + { 0x0B, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0C, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0D, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0E, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0F, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x10, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x11, PacketTypesIn.DeclareCommands }, // Commands + { 0x12, PacketTypesIn.CloseWindow }, // Container Close + { 0x13, PacketTypesIn.WindowItems }, // Container Set Content + { 0x14, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x15, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x16, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x17, PacketTypesIn.SetCooldown }, // Cooldown + { 0x18, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x19, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x1A, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1B, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1C, PacketTypesIn.HideMessage }, // Delete Chat + { 0x1D, PacketTypesIn.Disconnect }, // Disconnect + { 0x1E, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x1F, PacketTypesIn.EntityStatus }, // Entity Event + { 0x20, PacketTypesIn.EntityPositionSync }, // Entity Position Sync (new in 1.21.2) + { 0x21, PacketTypesIn.Explosion }, // Explode + { 0x22, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x23, PacketTypesIn.ChangeGameState }, // Game Event + { 0x24, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open + { 0x25, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x26, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x27, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x28, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x29, PacketTypesIn.Effect }, // Level Event + { 0x2A, PacketTypesIn.Particle }, // Level Particles + { 0x2B, PacketTypesIn.UpdateLight }, // Light Update + { 0x2C, PacketTypesIn.JoinGame }, // Login + { 0x2D, PacketTypesIn.MapData }, // Map Item Data + { 0x2E, PacketTypesIn.TradeList }, // Merchant Offers + { 0x2F, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x30, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x31, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track (new in 1.21.2) + { 0x32, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x33, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x34, PacketTypesIn.OpenBook }, // Open Book + { 0x35, PacketTypesIn.OpenWindow }, // Open Screen + { 0x36, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x37, PacketTypesIn.Ping }, // Ping + { 0x38, PacketTypesIn.PingResponse }, // Pong Response + { 0x39, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x3A, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x3B, PacketTypesIn.ChatMessage }, // Player Chat + { 0x3C, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x3D, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x3E, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x3F, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x40, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x41, PacketTypesIn.FacePlayer }, // Player Look At + { 0x42, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x43, PacketTypesIn.PlayerRotation }, // Player Rotation (new in 1.21.2) + { 0x44, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add (new in 1.21.2, replaces UnlockRecipes) + { 0x45, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove (new in 1.21.2) + { 0x46, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings (new in 1.21.2) + { 0x47, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x48, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x49, PacketTypesIn.ResetScore }, // Reset Score + { 0x4A, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x4B, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x4C, PacketTypesIn.Respawn }, // Respawn + { 0x4D, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x4E, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x4F, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x50, PacketTypesIn.ServerData }, // Server Data + { 0x51, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x52, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x53, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x54, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x55, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x56, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x57, PacketTypesIn.Camera }, // Set Camera + { 0x58, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x59, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x5A, PacketTypesIn.SetCursorItem }, // Set Cursor Item (new in 1.21.2) + { 0x5B, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x5C, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x5D, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x5E, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x5F, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x60, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x61, PacketTypesIn.SetExperience }, // Set Experience + { 0x62, PacketTypesIn.UpdateHealth }, // Set Health + { 0x63, PacketTypesIn.SetHeldSlot }, // Set Held Slot (new in 1.21.2, replaces HeldItemChange) + { 0x64, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x65, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x66, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory (new in 1.21.2) + { 0x67, PacketTypesIn.Teams }, // Set Player Team + { 0x68, PacketTypesIn.UpdateScore }, // Set Score + { 0x69, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x6A, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x6B, PacketTypesIn.TimeUpdate }, // Set Time + { 0x6C, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x6D, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x6E, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x6F, PacketTypesIn.SoundEffect }, // Sound + { 0x70, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x71, PacketTypesIn.StopSound }, // Stop Sound + { 0x72, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x73, PacketTypesIn.SystemChat }, // System Chat + { 0x74, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x75, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x76, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x77, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x78, PacketTypesIn.SetTickingState }, // Ticking State + { 0x79, PacketTypesIn.StepTick }, // Ticking Step + { 0x7A, PacketTypesIn.Transfer }, // Transfer + { 0x7B, PacketTypesIn.Advancements }, // Update Advancements + { 0x7C, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x7D, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x7E, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x7F, PacketTypesIn.Tags }, // Update Tags + { 0x80, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x81, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x82, PacketTypesIn.ServerLinks } // Server Links + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected + { 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x04, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x05, PacketTypesOut.ChatCommand }, // Chat Command + { 0x06, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x07, PacketTypesOut.ChatMessage }, // Chat + { 0x08, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x09, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0A, PacketTypesOut.ClientStatus }, // Client Command + { 0x0B, PacketTypesOut.ClientTickEnd }, // Client Tick End + { 0x0C, PacketTypesOut.ClientSettings }, // Client Information + { 0x0D, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x0E, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x0F, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x10, PacketTypesOut.ClickWindow }, // Container Click + { 0x11, PacketTypesOut.CloseWindow }, // Container Close + { 0x12, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x13, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x14, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x15, PacketTypesOut.DebugSampleSubscription }, // Debug Sample Subscription + { 0x16, PacketTypesOut.EditBook }, // Edit Book + { 0x17, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x18, PacketTypesOut.InteractEntity }, // Interact + { 0x19, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1A, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1B, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1C, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1D, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x1E, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x1F, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x20, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x21, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x22, PacketTypesOut.PickItem }, // Pick Item From Block (split in 1.21.4) + { 0x23, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity (new in 1.21.4) + { 0x24, PacketTypesOut.PingRequest }, // Ping Request + { 0x25, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x26, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x27, PacketTypesOut.PlayerDigging }, // Player Action + { 0x28, PacketTypesOut.EntityAction }, // Player Command + { 0x29, PacketTypesOut.SteerVehicle }, // Player Input + { 0x2A, PacketTypesOut.PlayerLoaded }, // Player Loaded (new in 1.21.4) + { 0x2B, PacketTypesOut.Pong }, // Pong + { 0x2C, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2D, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x2E, PacketTypesOut.NameItem }, // Rename Item + { 0x2F, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x30, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x31, PacketTypesOut.SelectTrade }, // Select Trade + { 0x32, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x33, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x34, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x35, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x36, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x37, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x38, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x39, PacketTypesOut.UpdateSign }, // Sign Update + { 0x3A, PacketTypesOut.Animation }, // Swing + { 0x3B, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x3C, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x3D, PacketTypesOut.UseItem }, // Use Item + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks } + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1215.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1215.cs new file mode 100644 index 00000000..0e8f7a93 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1215.cs @@ -0,0 +1,247 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1215 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.EntityAnimation }, // Animate (was 0x03 in 1.21.4; AddExperienceOrb removed) + { 0x03, PacketTypesIn.Statistics }, // Award Stats + { 0x04, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x05, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x06, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x07, PacketTypesIn.BlockAction }, // Block Event + { 0x08, PacketTypesIn.BlockChange }, // Block Update + { 0x09, PacketTypesIn.BossBar }, // Boss Event + { 0x0A, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0B, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0C, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0D, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0E, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x0F, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x10, PacketTypesIn.DeclareCommands }, // Commands + { 0x11, PacketTypesIn.CloseWindow }, // Container Close + { 0x12, PacketTypesIn.WindowItems }, // Container Set Content + { 0x13, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x14, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x15, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x16, PacketTypesIn.SetCooldown }, // Cooldown + { 0x17, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x18, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x19, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1A, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1B, PacketTypesIn.HideMessage }, // Delete Chat + { 0x1C, PacketTypesIn.Disconnect }, // Disconnect + { 0x1D, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x1E, PacketTypesIn.EntityStatus }, // Entity Event + { 0x1F, PacketTypesIn.EntityPositionSync }, // Entity Position Sync + { 0x20, PacketTypesIn.Explosion }, // Explode + { 0x21, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x22, PacketTypesIn.ChangeGameState }, // Game Event + { 0x23, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open + { 0x24, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x25, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x26, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x27, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x28, PacketTypesIn.Effect }, // Level Event + { 0x29, PacketTypesIn.Particle }, // Level Particles + { 0x2A, PacketTypesIn.UpdateLight }, // Light Update + { 0x2B, PacketTypesIn.JoinGame }, // Login + { 0x2C, PacketTypesIn.MapData }, // Map Item Data + { 0x2D, PacketTypesIn.TradeList }, // Merchant Offers + { 0x2E, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x2F, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x30, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track + { 0x31, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x32, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x33, PacketTypesIn.OpenBook }, // Open Book + { 0x34, PacketTypesIn.OpenWindow }, // Open Screen + { 0x35, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x36, PacketTypesIn.Ping }, // Ping + { 0x37, PacketTypesIn.PingResponse }, // Pong Response + { 0x38, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x39, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x3A, PacketTypesIn.ChatMessage }, // Player Chat + { 0x3B, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x3C, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x3D, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x3E, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x3F, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x40, PacketTypesIn.FacePlayer }, // Player Look At + { 0x41, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x42, PacketTypesIn.PlayerRotation }, // Player Rotation + { 0x43, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add + { 0x44, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove + { 0x45, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings + { 0x46, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x47, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x48, PacketTypesIn.ResetScore }, // Reset Score + { 0x49, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x4A, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x4B, PacketTypesIn.Respawn }, // Respawn + { 0x4C, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x4D, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x4E, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x4F, PacketTypesIn.ServerData }, // Server Data + { 0x50, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x51, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x52, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x53, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x54, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x55, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x56, PacketTypesIn.Camera }, // Set Camera + { 0x57, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x58, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x59, PacketTypesIn.SetCursorItem }, // Set Cursor Item + { 0x5A, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x5B, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x5C, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x5D, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x5E, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x5F, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x60, PacketTypesIn.SetExperience }, // Set Experience + { 0x61, PacketTypesIn.UpdateHealth }, // Set Health + { 0x62, PacketTypesIn.SetHeldSlot }, // Set Held Slot + { 0x63, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x64, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x65, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory + { 0x66, PacketTypesIn.Teams }, // Set Player Team + { 0x67, PacketTypesIn.UpdateScore }, // Set Score + { 0x68, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x69, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x6A, PacketTypesIn.TimeUpdate }, // Set Time + { 0x6B, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x6C, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x6D, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x6E, PacketTypesIn.SoundEffect }, // Sound + { 0x6F, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x70, PacketTypesIn.StopSound }, // Stop Sound + { 0x71, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x72, PacketTypesIn.SystemChat }, // System Chat + { 0x73, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x74, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x75, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x76, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x77, PacketTypesIn.TestInstanceBlockStatus }, // Test Instance Block Status (new in 1.21.5) + { 0x78, PacketTypesIn.SetTickingState }, // Ticking State + { 0x79, PacketTypesIn.StepTick }, // Ticking Step + { 0x7A, PacketTypesIn.Transfer }, // Transfer + { 0x7B, PacketTypesIn.Advancements }, // Update Advancements + { 0x7C, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x7D, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x7E, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x7F, PacketTypesIn.Tags }, // Update Tags + { 0x80, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x81, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x82, PacketTypesIn.ServerLinks } // Server Links + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected + { 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x04, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x05, PacketTypesOut.ChatCommand }, // Chat Command + { 0x06, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x07, PacketTypesOut.ChatMessage }, // Chat + { 0x08, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x09, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0A, PacketTypesOut.ClientStatus }, // Client Command + { 0x0B, PacketTypesOut.ClientTickEnd }, // Client Tick End + { 0x0C, PacketTypesOut.ClientSettings }, // Client Information + { 0x0D, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x0E, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x0F, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x10, PacketTypesOut.ClickWindow }, // Container Click + { 0x11, PacketTypesOut.CloseWindow }, // Container Close + { 0x12, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x13, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x14, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x15, PacketTypesOut.DebugSampleSubscription }, // Debug Sample Subscription + { 0x16, PacketTypesOut.EditBook }, // Edit Book + { 0x17, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x18, PacketTypesOut.InteractEntity }, // Interact + { 0x19, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1A, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1B, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1C, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1D, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x1E, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x1F, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x20, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x21, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x22, PacketTypesOut.PickItem }, // Pick Item From Block + { 0x23, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity + { 0x24, PacketTypesOut.PingRequest }, // Ping Request + { 0x25, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x26, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x27, PacketTypesOut.PlayerDigging }, // Player Action + { 0x28, PacketTypesOut.EntityAction }, // Player Command + { 0x29, PacketTypesOut.SteerVehicle }, // Player Input + { 0x2A, PacketTypesOut.PlayerLoaded }, // Player Loaded + { 0x2B, PacketTypesOut.Pong }, // Pong + { 0x2C, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2D, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x2E, PacketTypesOut.NameItem }, // Rename Item + { 0x2F, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x30, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x31, PacketTypesOut.SelectTrade }, // Select Trade + { 0x32, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x33, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x34, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x35, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x36, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x37, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x38, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x39, PacketTypesOut.SetTestBlock }, // Set Test Block (new in 1.21.5) + { 0x3A, PacketTypesOut.UpdateSign }, // Sign Update + { 0x3B, PacketTypesOut.Animation }, // Swing + { 0x3C, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x3D, PacketTypesOut.TestInstanceBlockAction }, // Test Instance Block Action (new in 1.21.5) + { 0x3E, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x3F, PacketTypesOut.UseItem }, // Use Item + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks } + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1216.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1216.cs new file mode 100644 index 00000000..3f1520f7 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1216.cs @@ -0,0 +1,255 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1216 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.EntityAnimation }, // Animate + { 0x03, PacketTypesIn.Statistics }, // Award Stats + { 0x04, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x05, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x06, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x07, PacketTypesIn.BlockAction }, // Block Event + { 0x08, PacketTypesIn.BlockChange }, // Block Update + { 0x09, PacketTypesIn.BossBar }, // Boss Event + { 0x0A, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0B, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0C, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0D, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0E, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x0F, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x10, PacketTypesIn.DeclareCommands }, // Commands + { 0x11, PacketTypesIn.CloseWindow }, // Container Close + { 0x12, PacketTypesIn.WindowItems }, // Container Set Content + { 0x13, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x14, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x15, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x16, PacketTypesIn.SetCooldown }, // Cooldown + { 0x17, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x18, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x19, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1A, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1B, PacketTypesIn.HideMessage }, // Delete Chat + { 0x1C, PacketTypesIn.Disconnect }, // Disconnect + { 0x1D, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x1E, PacketTypesIn.EntityStatus }, // Entity Event + { 0x1F, PacketTypesIn.EntityPositionSync }, // Entity Position Sync + { 0x20, PacketTypesIn.Explosion }, // Explode + { 0x21, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x22, PacketTypesIn.ChangeGameState }, // Game Event + { 0x23, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open + { 0x24, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x25, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x26, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x27, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x28, PacketTypesIn.Effect }, // Level Event + { 0x29, PacketTypesIn.Particle }, // Level Particles + { 0x2A, PacketTypesIn.UpdateLight }, // Light Update + { 0x2B, PacketTypesIn.JoinGame }, // Login + { 0x2C, PacketTypesIn.MapData }, // Map Item Data + { 0x2D, PacketTypesIn.TradeList }, // Merchant Offers + { 0x2E, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x2F, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x30, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track + { 0x31, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x32, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x33, PacketTypesIn.OpenBook }, // Open Book + { 0x34, PacketTypesIn.OpenWindow }, // Open Screen + { 0x35, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x36, PacketTypesIn.Ping }, // Ping + { 0x37, PacketTypesIn.PingResponse }, // Pong Response + { 0x38, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x39, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x3A, PacketTypesIn.ChatMessage }, // Player Chat + { 0x3B, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x3C, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x3D, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x3E, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x3F, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x40, PacketTypesIn.FacePlayer }, // Player Look At + { 0x41, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x42, PacketTypesIn.PlayerRotation }, // Player Rotation + { 0x43, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add + { 0x44, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove + { 0x45, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings + { 0x46, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x47, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x48, PacketTypesIn.ResetScore }, // Reset Score + { 0x49, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x4A, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x4B, PacketTypesIn.Respawn }, // Respawn + { 0x4C, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x4D, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x4E, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x4F, PacketTypesIn.ServerData }, // Server Data + { 0x50, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x51, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x52, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x53, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x54, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x55, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x56, PacketTypesIn.Camera }, // Set Camera + { 0x57, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x58, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x59, PacketTypesIn.SetCursorItem }, // Set Cursor Item + { 0x5A, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x5B, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x5C, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x5D, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x5E, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x5F, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x60, PacketTypesIn.SetExperience }, // Set Experience + { 0x61, PacketTypesIn.UpdateHealth }, // Set Health + { 0x62, PacketTypesIn.SetHeldSlot }, // Set Held Slot + { 0x63, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x64, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x65, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory + { 0x66, PacketTypesIn.Teams }, // Set Player Team + { 0x67, PacketTypesIn.UpdateScore }, // Set Score + { 0x68, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x69, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x6A, PacketTypesIn.TimeUpdate }, // Set Time + { 0x6B, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x6C, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x6D, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x6E, PacketTypesIn.SoundEffect }, // Sound + { 0x6F, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x70, PacketTypesIn.StopSound }, // Stop Sound + { 0x71, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x72, PacketTypesIn.SystemChat }, // System Chat + { 0x73, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x74, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x75, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x76, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x77, PacketTypesIn.TestInstanceBlockStatus }, // Test Instance Block Status + { 0x78, PacketTypesIn.SetTickingState }, // Ticking State + { 0x79, PacketTypesIn.StepTick }, // Ticking Step + { 0x7A, PacketTypesIn.Transfer }, // Transfer + { 0x7B, PacketTypesIn.Advancements }, // Update Advancements + { 0x7C, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x7D, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x7E, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x7F, PacketTypesIn.Tags }, // Update Tags + { 0x80, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x81, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x82, PacketTypesIn.ServerLinks }, // Server Links + { 0x83, PacketTypesIn.Waypoint }, // Waypoint (new in 1.21.6) + { 0x84, PacketTypesIn.ClearDialog }, // Clear Dialog (new in 1.21.6) + { 0x85, PacketTypesIn.ShowDialog } // Show Dialog (new in 1.21.6) + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected + { 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x04, PacketTypesOut.ChangeGameMode }, // Change Game Mode (new in 1.21.6) + { 0x05, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x06, PacketTypesOut.ChatCommand }, // Chat Command + { 0x07, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x08, PacketTypesOut.ChatMessage }, // Chat + { 0x09, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x0A, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0B, PacketTypesOut.ClientStatus }, // Client Command + { 0x0C, PacketTypesOut.ClientTickEnd }, // Client Tick End + { 0x0D, PacketTypesOut.ClientSettings }, // Client Information + { 0x0E, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x0F, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x10, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x11, PacketTypesOut.ClickWindow }, // Container Click + { 0x12, PacketTypesOut.CloseWindow }, // Container Close + { 0x13, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x14, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x15, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x16, PacketTypesOut.DebugSampleSubscription }, // Debug Sample Subscription + { 0x17, PacketTypesOut.EditBook }, // Edit Book + { 0x18, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x19, PacketTypesOut.InteractEntity }, // Interact + { 0x1A, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1B, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1C, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1D, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1E, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x1F, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x20, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x21, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x22, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x23, PacketTypesOut.PickItem }, // Pick Item From Block + { 0x24, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity + { 0x25, PacketTypesOut.PingRequest }, // Ping Request + { 0x26, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x27, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x28, PacketTypesOut.PlayerDigging }, // Player Action + { 0x29, PacketTypesOut.EntityAction }, // Player Command + { 0x2A, PacketTypesOut.SteerVehicle }, // Player Input + { 0x2B, PacketTypesOut.PlayerLoaded }, // Player Loaded + { 0x2C, PacketTypesOut.Pong }, // Pong + { 0x2D, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2E, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x2F, PacketTypesOut.NameItem }, // Rename Item + { 0x30, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x31, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x32, PacketTypesOut.SelectTrade }, // Select Trade + { 0x33, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x34, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x35, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x36, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x37, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x38, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x39, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x3A, PacketTypesOut.SetTestBlock }, // Set Test Block + { 0x3B, PacketTypesOut.UpdateSign }, // Sign Update + { 0x3C, PacketTypesOut.Animation }, // Swing + { 0x3D, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x3E, PacketTypesOut.TestInstanceBlockAction }, // Test Instance Block Action + { 0x3F, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x40, PacketTypesOut.UseItem }, // Use Item + { 0x41, PacketTypesOut.CustomClickAction } // Custom Click Action (new in 1.21.6) + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks }, + { 0x11, ConfigurationPacketTypesIn.ClearDialog }, // New in 1.21.6 + { 0x12, ConfigurationPacketTypesIn.ShowDialog } // New in 1.21.6 + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks }, + { 0x08, ConfigurationPacketTypesOut.CustomClickAction } // New in 1.21.6 + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1219.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1219.cs new file mode 100644 index 00000000..5aab09bc --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1219.cs @@ -0,0 +1,262 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette1219 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.EntityAnimation }, // Animate + { 0x03, PacketTypesIn.Statistics }, // Award Stats + { 0x04, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x05, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x06, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x07, PacketTypesIn.BlockAction }, // Block Event + { 0x08, PacketTypesIn.BlockChange }, // Block Update + { 0x09, PacketTypesIn.BossBar }, // Boss Event + { 0x0A, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0B, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0C, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0D, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0E, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x0F, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x10, PacketTypesIn.DeclareCommands }, // Commands + { 0x11, PacketTypesIn.CloseWindow }, // Container Close + { 0x12, PacketTypesIn.WindowItems }, // Container Set Content + { 0x13, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x14, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x15, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x16, PacketTypesIn.SetCooldown }, // Cooldown + { 0x17, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x18, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x19, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1A, PacketTypesIn.DebugBlockValue }, // Debug Block Value (new in 1.21.9) + { 0x1B, PacketTypesIn.DebugChunkValue }, // Debug Chunk Value (new in 1.21.9) + { 0x1C, PacketTypesIn.DebugEntityValue }, // Debug Entity Value (new in 1.21.9) + { 0x1D, PacketTypesIn.DebugEvent }, // Debug Event (new in 1.21.9) + { 0x1E, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1F, PacketTypesIn.HideMessage }, // Delete Chat + { 0x20, PacketTypesIn.Disconnect }, // Disconnect + { 0x21, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x22, PacketTypesIn.EntityStatus }, // Entity Event + { 0x23, PacketTypesIn.EntityPositionSync }, // Entity Position Sync + { 0x24, PacketTypesIn.Explosion }, // Explode + { 0x25, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x26, PacketTypesIn.ChangeGameState }, // Game Event + { 0x27, PacketTypesIn.GameTestHighlightPos }, // Game Test Highlight Pos (new in 1.21.9) + { 0x28, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open + { 0x29, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x2A, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x2B, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x2C, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x2D, PacketTypesIn.Effect }, // Level Event + { 0x2E, PacketTypesIn.Particle }, // Level Particles + { 0x2F, PacketTypesIn.UpdateLight }, // Light Update + { 0x30, PacketTypesIn.JoinGame }, // Login + { 0x31, PacketTypesIn.MapData }, // Map Item Data + { 0x32, PacketTypesIn.TradeList }, // Merchant Offers + { 0x33, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x34, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x35, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track + { 0x36, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x37, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x38, PacketTypesIn.OpenBook }, // Open Book + { 0x39, PacketTypesIn.OpenWindow }, // Open Screen + { 0x3A, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x3B, PacketTypesIn.Ping }, // Ping + { 0x3C, PacketTypesIn.PingResponse }, // Pong Response + { 0x3D, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x3E, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x3F, PacketTypesIn.ChatMessage }, // Player Chat + { 0x40, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x41, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x42, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x43, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x44, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x45, PacketTypesIn.FacePlayer }, // Player Look At + { 0x46, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x47, PacketTypesIn.PlayerRotation }, // Player Rotation + { 0x48, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add + { 0x49, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove + { 0x4A, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings + { 0x4B, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x4C, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x4D, PacketTypesIn.ResetScore }, // Reset Score + { 0x4E, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x4F, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x50, PacketTypesIn.Respawn }, // Respawn + { 0x51, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x52, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x53, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x54, PacketTypesIn.ServerData }, // Server Data + { 0x55, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x56, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x57, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x58, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x59, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x5A, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x5B, PacketTypesIn.Camera }, // Set Camera + { 0x5C, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x5D, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x5E, PacketTypesIn.SetCursorItem }, // Set Cursor Item + { 0x5F, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x60, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x61, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x62, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x63, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x64, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x65, PacketTypesIn.SetExperience }, // Set Experience + { 0x66, PacketTypesIn.UpdateHealth }, // Set Health + { 0x67, PacketTypesIn.SetHeldSlot }, // Set Held Slot + { 0x68, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x69, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x6A, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory + { 0x6B, PacketTypesIn.Teams }, // Set Player Team + { 0x6C, PacketTypesIn.UpdateScore }, // Set Score + { 0x6D, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x6E, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x6F, PacketTypesIn.TimeUpdate }, // Set Time + { 0x70, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x71, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x72, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x73, PacketTypesIn.SoundEffect }, // Sound + { 0x74, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x75, PacketTypesIn.StopSound }, // Stop Sound + { 0x76, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x77, PacketTypesIn.SystemChat }, // System Chat + { 0x78, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x79, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x7A, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x7B, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x7C, PacketTypesIn.TestInstanceBlockStatus }, // Test Instance Block Status + { 0x7D, PacketTypesIn.SetTickingState }, // Ticking State + { 0x7E, PacketTypesIn.StepTick }, // Ticking Step + { 0x7F, PacketTypesIn.Transfer }, // Transfer + { 0x80, PacketTypesIn.Advancements }, // Update Advancements + { 0x81, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x82, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x83, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x84, PacketTypesIn.Tags }, // Update Tags + { 0x85, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x86, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x87, PacketTypesIn.ServerLinks }, // Server Links + { 0x88, PacketTypesIn.Waypoint }, // Waypoint + { 0x89, PacketTypesIn.ClearDialog }, // Clear Dialog + { 0x8A, PacketTypesIn.ShowDialog } // Show Dialog + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected + { 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x04, PacketTypesOut.ChangeGameMode }, // Change Game Mode + { 0x05, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x06, PacketTypesOut.ChatCommand }, // Chat Command + { 0x07, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x08, PacketTypesOut.ChatMessage }, // Chat + { 0x09, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x0A, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0B, PacketTypesOut.ClientStatus }, // Client Command + { 0x0C, PacketTypesOut.ClientTickEnd }, // Client Tick End + { 0x0D, PacketTypesOut.ClientSettings }, // Client Information + { 0x0E, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x0F, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x10, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x11, PacketTypesOut.ClickWindow }, // Container Click + { 0x12, PacketTypesOut.CloseWindow }, // Container Close + { 0x13, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x14, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x15, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x16, PacketTypesOut.DebugSampleSubscription }, // Debug Subscription Request + { 0x17, PacketTypesOut.EditBook }, // Edit Book + { 0x18, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x19, PacketTypesOut.InteractEntity }, // Interact + { 0x1A, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1B, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1C, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1D, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1E, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x1F, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x20, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x21, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x22, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x23, PacketTypesOut.PickItem }, // Pick Item From Block + { 0x24, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity + { 0x25, PacketTypesOut.PingRequest }, // Ping Request + { 0x26, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x27, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x28, PacketTypesOut.PlayerDigging }, // Player Action + { 0x29, PacketTypesOut.EntityAction }, // Player Command + { 0x2A, PacketTypesOut.SteerVehicle }, // Player Input + { 0x2B, PacketTypesOut.PlayerLoaded }, // Player Loaded + { 0x2C, PacketTypesOut.Pong }, // Pong + { 0x2D, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2E, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x2F, PacketTypesOut.NameItem }, // Rename Item + { 0x30, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x31, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x32, PacketTypesOut.SelectTrade }, // Select Trade + { 0x33, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x34, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x35, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x36, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x37, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x38, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x39, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x3A, PacketTypesOut.SetTestBlock }, // Set Test Block + { 0x3B, PacketTypesOut.UpdateSign }, // Sign Update + { 0x3C, PacketTypesOut.Animation }, // Swing + { 0x3D, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x3E, PacketTypesOut.TestInstanceBlockAction }, // Test Instance Block Action + { 0x3F, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x40, PacketTypesOut.UseItem }, // Use Item + { 0x41, PacketTypesOut.CustomClickAction } // Custom Click Action + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks }, + { 0x11, ConfigurationPacketTypesIn.ClearDialog }, + { 0x12, ConfigurationPacketTypesIn.ShowDialog }, + { 0x13, ConfigurationPacketTypesIn.CodeOfConduct } // New in 1.21.9 + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks }, + { 0x08, ConfigurationPacketTypesOut.CustomClickAction }, + { 0x09, ConfigurationPacketTypesOut.AcceptCodeOfConduct } // New in 1.21.9 + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette17.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette17.cs index bd337612..ecbdf4a9 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette17.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette17.cs @@ -114,7 +114,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes protected override Dictionary GetListIn() => typeIn; protected override Dictionary GetListOut() => typeOut; - + protected override Dictionary GetConfigurationListIn() => new(); protected override Dictionary GetConfigurationListOut() => new(); } diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette18.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette18.cs index b7db312d..26d43f9d 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette18.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette18.cs @@ -4,7 +4,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { public class PacketPalette18 : PacketTypePalette { - private Dictionary typeIn = new Dictionary() + private Dictionary typeIn = new() { { 0x00, PacketTypesIn.KeepAlive }, { 0x01, PacketTypesIn.JoinGame }, @@ -80,7 +80,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x49, PacketTypesIn.UpdateEntityNBT } }; - private Dictionary typeOut = new Dictionary() + private Dictionary typeOut = new() { { 0x00, PacketTypesOut.TeleportConfirm }, { 0x01, PacketTypesOut.Unknown }, diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs new file mode 100644 index 00000000..ebffc31a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes +{ + public class PacketPalette19 : PacketTypePalette + { + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.SpawnEntity }, + { 0x01, PacketTypesIn.SpawnExperienceOrb }, + { 0x02, PacketTypesIn.SpawnWeatherEntity }, + { 0x03, PacketTypesIn.SpawnLivingEntity }, + { 0x04, PacketTypesIn.SpawnPainting }, + { 0x05, PacketTypesIn.SpawnPlayer }, + { 0x06, PacketTypesIn.EntityAnimation }, + { 0x07, PacketTypesIn.Statistics }, + { 0x08, PacketTypesIn.BlockBreakAnimation }, + { 0x09, PacketTypesIn.BlockEntityData }, + { 0x0A, PacketTypesIn.BlockAction }, + { 0x0B, PacketTypesIn.BlockChange }, + { 0x0C, PacketTypesIn.BossBar }, + { 0x0D, PacketTypesIn.ServerDifficulty }, + { 0x0E, PacketTypesIn.TabComplete }, + { 0x0F, PacketTypesIn.ChatMessage }, + { 0x10, PacketTypesIn.MultiBlockChange }, + { 0x11, PacketTypesIn.WindowConfirmation }, + { 0x12, PacketTypesIn.CloseWindow }, + { 0x13, PacketTypesIn.OpenWindow }, + { 0x14, PacketTypesIn.WindowItems }, + { 0x15, PacketTypesIn.WindowProperty }, + { 0x16, PacketTypesIn.SetSlot }, + { 0x17, PacketTypesIn.SetCooldown }, + { 0x18, PacketTypesIn.PluginMessage }, + { 0x19, PacketTypesIn.NamedSoundEffect }, + { 0x1A, PacketTypesIn.Disconnect }, + { 0x1B, PacketTypesIn.EntityStatus }, + { 0x1C, PacketTypesIn.Explosion }, + { 0x1D, PacketTypesIn.UnloadChunk }, + { 0x1E, PacketTypesIn.ChangeGameState }, + { 0x1F, PacketTypesIn.KeepAlive }, + { 0x20, PacketTypesIn.ChunkData }, + { 0x21, PacketTypesIn.Effect }, + { 0x22, PacketTypesIn.Particle }, + { 0x23, PacketTypesIn.JoinGame }, + { 0x24, PacketTypesIn.MapData }, + { 0x25, PacketTypesIn.EntityPosition }, + { 0x26, PacketTypesIn.EntityPositionAndRotation }, + { 0x27, PacketTypesIn.EntityRotation }, + { 0x28, PacketTypesIn.EntityMovement }, + { 0x29, PacketTypesIn.VehicleMove }, + { 0x2A, PacketTypesIn.OpenSignEditor }, + { 0x2B, PacketTypesIn.PlayerAbilities }, + { 0x2C, PacketTypesIn.CombatEvent }, + { 0x2D, PacketTypesIn.PlayerInfo }, + { 0x2E, PacketTypesIn.PlayerPositionAndLook }, + { 0x2F, PacketTypesIn.UseBed }, + { 0x30, PacketTypesIn.DestroyEntities }, + { 0x31, PacketTypesIn.RemoveEntityEffect }, + { 0x32, PacketTypesIn.ResourcePackSend }, + { 0x33, PacketTypesIn.Respawn }, + { 0x34, PacketTypesIn.EntityHeadLook }, + { 0x35, PacketTypesIn.WorldBorder }, + { 0x36, PacketTypesIn.Camera }, + { 0x37, PacketTypesIn.HeldItemChange }, + { 0x38, PacketTypesIn.DisplayScoreboard }, + { 0x39, PacketTypesIn.EntityMetadata }, + { 0x3A, PacketTypesIn.AttachEntity }, + { 0x3B, PacketTypesIn.EntityVelocity }, + { 0x3C, PacketTypesIn.EntityEquipment }, + { 0x3D, PacketTypesIn.SetExperience }, + { 0x3E, PacketTypesIn.UpdateHealth }, + { 0x3F, PacketTypesIn.ScoreboardObjective }, + { 0x40, PacketTypesIn.SetPassengers }, + { 0x41, PacketTypesIn.Teams }, + { 0x42, PacketTypesIn.UpdateScore }, + { 0x43, PacketTypesIn.SpawnPosition }, + { 0x44, PacketTypesIn.TimeUpdate }, + { 0x45, PacketTypesIn.Title }, + { 0x46, PacketTypesIn.UpdateSign }, + { 0x47, PacketTypesIn.SoundEffect }, + { 0x48, PacketTypesIn.PlayerListHeaderAndFooter }, + { 0x49, PacketTypesIn.CollectItem }, + { 0x4A, PacketTypesIn.EntityTeleport }, + { 0x4B, PacketTypesIn.EntityProperties }, + { 0x4C, PacketTypesIn.EntityEffect }, + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, + { 0x01, PacketTypesOut.TabComplete }, + { 0x02, PacketTypesOut.ChatMessage }, + { 0x03, PacketTypesOut.ClientStatus }, + { 0x04, PacketTypesOut.ClientSettings }, + { 0x05, PacketTypesOut.WindowConfirmation }, + { 0x06, PacketTypesOut.EnchantItem }, + { 0x07, PacketTypesOut.ClickWindow }, + { 0x08, PacketTypesOut.CloseWindow }, + { 0x09, PacketTypesOut.PluginMessage }, + { 0x0A, PacketTypesOut.InteractEntity }, + { 0x0B, PacketTypesOut.KeepAlive }, + { 0x0C, PacketTypesOut.PlayerPosition }, + { 0x0D, PacketTypesOut.PlayerPositionAndRotation }, + { 0x0E, PacketTypesOut.PlayerRotation }, + { 0x0F, PacketTypesOut.PlayerMovement }, + { 0x10, PacketTypesOut.VehicleMove }, + { 0x11, PacketTypesOut.SteerBoat }, + { 0x12, PacketTypesOut.PlayerAbilities }, + { 0x13, PacketTypesOut.PlayerDigging }, + { 0x14, PacketTypesOut.EntityAction }, + { 0x15, PacketTypesOut.SteerVehicle }, + { 0x16, PacketTypesOut.ResourcePackStatus }, + { 0x17, PacketTypesOut.HeldItemChange }, + { 0x18, PacketTypesOut.CreativeInventoryAction }, + { 0x19, PacketTypesOut.UpdateSign }, + { 0x1A, PacketTypesOut.Animation }, + { 0x1B, PacketTypesOut.Spectate }, + { 0x1C, PacketTypesOut.PlayerBlockPlacement }, + { 0x1D, PacketTypesOut.UseItem }, + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => new(); + protected override Dictionary GetConfigurationListOut() => new(); + } +} diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette261.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette261.cs new file mode 100644 index 00000000..0f58c527 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette261.cs @@ -0,0 +1,266 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes; + +public class PacketPalette261 : PacketTypePalette +{ + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.Bundle }, // Bundle delimiter + { 0x01, PacketTypesIn.SpawnEntity }, // Add Entity + { 0x02, PacketTypesIn.EntityAnimation }, // Animate + { 0x03, PacketTypesIn.Statistics }, // Award Stats + { 0x04, PacketTypesIn.BlockChangedAck }, // Block Changed Ack + { 0x05, PacketTypesIn.BlockBreakAnimation }, // Block Destruction + { 0x06, PacketTypesIn.BlockEntityData }, // Block Entity Data + { 0x07, PacketTypesIn.BlockAction }, // Block Event + { 0x08, PacketTypesIn.BlockChange }, // Block Update + { 0x09, PacketTypesIn.BossBar }, // Boss Event + { 0x0A, PacketTypesIn.ServerDifficulty }, // Change Difficulty + { 0x0B, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished + { 0x0C, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start + { 0x0D, PacketTypesIn.ChunksBiomes }, // Chunks Biomes + { 0x0E, PacketTypesIn.ClearTiles }, // Clear Titles + { 0x0F, PacketTypesIn.TabComplete }, // Command Suggestions + { 0x10, PacketTypesIn.DeclareCommands }, // Commands + { 0x11, PacketTypesIn.CloseWindow }, // Container Close + { 0x12, PacketTypesIn.WindowItems }, // Container Set Content + { 0x13, PacketTypesIn.WindowProperty }, // Container Set Data + { 0x14, PacketTypesIn.SetSlot }, // Container Set Slot + { 0x15, PacketTypesIn.CookieRequest }, // Cookie Request + { 0x16, PacketTypesIn.SetCooldown }, // Cooldown + { 0x17, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions + { 0x18, PacketTypesIn.PluginMessage }, // Custom Payload + { 0x19, PacketTypesIn.DamageEvent }, // Damage Event + { 0x1A, PacketTypesIn.DebugBlockValue }, // Debug Block Value + { 0x1B, PacketTypesIn.DebugChunkValue }, // Debug Chunk Value + { 0x1C, PacketTypesIn.DebugEntityValue }, // Debug Entity Value + { 0x1D, PacketTypesIn.DebugEvent }, // Debug Event + { 0x1E, PacketTypesIn.DebugSample }, // Debug Sample + { 0x1F, PacketTypesIn.HideMessage }, // Delete Chat + { 0x20, PacketTypesIn.Disconnect }, // Disconnect + { 0x21, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat + { 0x22, PacketTypesIn.EntityStatus }, // Entity Event + { 0x23, PacketTypesIn.EntityPositionSync }, // Entity Position Sync + { 0x24, PacketTypesIn.Explosion }, // Explode + { 0x25, PacketTypesIn.UnloadChunk }, // Forget Level Chunk + { 0x26, PacketTypesIn.ChangeGameState }, // Game Event + { 0x27, PacketTypesIn.GameRuleValues }, // Game Rule Values (new in 26.1) + { 0x28, PacketTypesIn.GameTestHighlightPos }, // Game Test Highlight Pos + { 0x29, PacketTypesIn.OpenHorseWindow }, // Mount Screen Open (renamed from Horse Screen Open) + { 0x2A, PacketTypesIn.HurtAnimation }, // Hurt Animation + { 0x2B, PacketTypesIn.InitializeWorldBorder }, // Initialize Border + { 0x2C, PacketTypesIn.KeepAlive }, // Keep Alive + { 0x2D, PacketTypesIn.ChunkData }, // Level Chunk With Light + { 0x2E, PacketTypesIn.Effect }, // Level Event + { 0x2F, PacketTypesIn.Particle }, // Level Particles + { 0x30, PacketTypesIn.UpdateLight }, // Light Update + { 0x31, PacketTypesIn.JoinGame }, // Login + { 0x32, PacketTypesIn.LowDiskSpaceWarning }, // Low Disk Space Warning (new in 26.1) + { 0x33, PacketTypesIn.MapData }, // Map Item Data + { 0x34, PacketTypesIn.TradeList }, // Merchant Offers + { 0x35, PacketTypesIn.EntityPosition }, // Move Entity Pos + { 0x36, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot + { 0x37, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track + { 0x38, PacketTypesIn.EntityRotation }, // Move Entity Rot + { 0x39, PacketTypesIn.VehicleMove }, // Move Vehicle + { 0x3A, PacketTypesIn.OpenBook }, // Open Book + { 0x3B, PacketTypesIn.OpenWindow }, // Open Screen + { 0x3C, PacketTypesIn.OpenSignEditor }, // Open Sign Editor + { 0x3D, PacketTypesIn.Ping }, // Ping + { 0x3E, PacketTypesIn.PingResponse }, // Pong Response + { 0x3F, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe + { 0x40, PacketTypesIn.PlayerAbilities }, // Player Abilities + { 0x41, PacketTypesIn.ChatMessage }, // Player Chat + { 0x42, PacketTypesIn.EndCombatEvent }, // Player Combat End + { 0x43, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter + { 0x44, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill + { 0x45, PacketTypesIn.PlayerRemove }, // Player Info Remove + { 0x46, PacketTypesIn.PlayerInfo }, // Player Info Update + { 0x47, PacketTypesIn.FacePlayer }, // Player Look At + { 0x48, PacketTypesIn.PlayerPositionAndLook }, // Player Position + { 0x49, PacketTypesIn.PlayerRotation }, // Player Rotation + { 0x4A, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add + { 0x4B, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove + { 0x4C, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings + { 0x4D, PacketTypesIn.DestroyEntities }, // Remove Entities + { 0x4E, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect + { 0x4F, PacketTypesIn.ResetScore }, // Reset Score + { 0x50, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop + { 0x51, PacketTypesIn.ResourcePackSend }, // Resource Pack Push + { 0x52, PacketTypesIn.Respawn }, // Respawn + { 0x53, PacketTypesIn.EntityHeadLook }, // Rotate Head + { 0x54, PacketTypesIn.MultiBlockChange }, // Section Blocks Update + { 0x55, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab + { 0x56, PacketTypesIn.ServerData }, // Server Data + { 0x57, PacketTypesIn.ActionBar }, // Set Action Bar Text + { 0x58, PacketTypesIn.WorldBorderCenter }, // Set Border Center + { 0x59, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size + { 0x5A, PacketTypesIn.WorldBorderSize }, // Set Border Size + { 0x5B, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay + { 0x5C, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance + { 0x5D, PacketTypesIn.Camera }, // Set Camera + { 0x5E, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center + { 0x5F, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius + { 0x60, PacketTypesIn.SetCursorItem }, // Set Cursor Item + { 0x61, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position + { 0x62, PacketTypesIn.DisplayScoreboard }, // Set Display Objective + { 0x63, PacketTypesIn.EntityMetadata }, // Set Entity Data + { 0x64, PacketTypesIn.AttachEntity }, // Set Entity Link + { 0x65, PacketTypesIn.EntityVelocity }, // Set Entity Motion + { 0x66, PacketTypesIn.EntityEquipment }, // Set Equipment + { 0x67, PacketTypesIn.SetExperience }, // Set Experience + { 0x68, PacketTypesIn.UpdateHealth }, // Set Health + { 0x69, PacketTypesIn.SetHeldSlot }, // Set Held Slot + { 0x6A, PacketTypesIn.ScoreboardObjective }, // Set Objective + { 0x6B, PacketTypesIn.SetPassengers }, // Set Passengers + { 0x6C, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory + { 0x6D, PacketTypesIn.Teams }, // Set Player Team + { 0x6E, PacketTypesIn.UpdateScore }, // Set Score + { 0x6F, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance + { 0x70, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text + { 0x71, PacketTypesIn.TimeUpdate }, // Set Time + { 0x72, PacketTypesIn.SetTitleText }, // Set Title Text + { 0x73, PacketTypesIn.SetTitleTime }, // Set Titles Animation + { 0x74, PacketTypesIn.EntitySoundEffect }, // Sound Entity + { 0x75, PacketTypesIn.SoundEffect }, // Sound + { 0x76, PacketTypesIn.StartConfiguration }, // Start Configuration + { 0x77, PacketTypesIn.StopSound }, // Stop Sound + { 0x78, PacketTypesIn.StoreCookie }, // Store Cookie + { 0x79, PacketTypesIn.SystemChat }, // System Chat + { 0x7A, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List + { 0x7B, PacketTypesIn.NBTQueryResponse }, // Tag Query + { 0x7C, PacketTypesIn.CollectItem }, // Take Item Entity + { 0x7D, PacketTypesIn.EntityTeleport }, // Teleport Entity + { 0x7E, PacketTypesIn.TestInstanceBlockStatus }, // Test Instance Block Status + { 0x7F, PacketTypesIn.SetTickingState }, // Ticking State + { 0x80, PacketTypesIn.StepTick }, // Ticking Step + { 0x81, PacketTypesIn.Transfer }, // Transfer + { 0x82, PacketTypesIn.Advancements }, // Update Advancements + { 0x83, PacketTypesIn.EntityProperties }, // Update Attributes + { 0x84, PacketTypesIn.EntityEffect }, // Update Mob Effect + { 0x85, PacketTypesIn.DeclareRecipes }, // Update Recipes + { 0x86, PacketTypesIn.Tags }, // Update Tags + { 0x87, PacketTypesIn.ProjectilePower }, // Projectile Power + { 0x88, PacketTypesIn.CustomReportDetails }, // Custom Report Details + { 0x89, PacketTypesIn.ServerLinks }, // Server Links + { 0x8A, PacketTypesIn.Waypoint }, // Waypoint + { 0x8B, PacketTypesIn.ClearDialog }, // Clear Dialog + { 0x8C, PacketTypesIn.ShowDialog } // Show Dialog + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation + { 0x01, PacketTypesOut.Attack }, // Attack (new in 26.1) + { 0x02, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query + { 0x03, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected + { 0x04, PacketTypesOut.SetDifficulty }, // Change Difficulty + { 0x05, PacketTypesOut.ChangeGameMode }, // Change Game Mode + { 0x06, PacketTypesOut.MessageAcknowledgment }, // Chat Ack + { 0x07, PacketTypesOut.ChatCommand }, // Chat Command + { 0x08, PacketTypesOut.SignedChatCommand }, // Chat Command Signed + { 0x09, PacketTypesOut.ChatMessage }, // Chat + { 0x0A, PacketTypesOut.PlayerSession }, // Chat Session Update + { 0x0B, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received + { 0x0C, PacketTypesOut.ClientStatus }, // Client Command + { 0x0D, PacketTypesOut.ClientTickEnd }, // Client Tick End + { 0x0E, PacketTypesOut.ClientSettings }, // Client Information + { 0x0F, PacketTypesOut.TabComplete }, // Command Suggestion + { 0x10, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged + { 0x11, PacketTypesOut.ClickWindowButton }, // Container Button Click + { 0x12, PacketTypesOut.ClickWindow }, // Container Click + { 0x13, PacketTypesOut.CloseWindow }, // Container Close + { 0x14, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed + { 0x15, PacketTypesOut.CookieResponse }, // Cookie Response + { 0x16, PacketTypesOut.PluginMessage }, // Custom Payload + { 0x17, PacketTypesOut.DebugSampleSubscription }, // Debug Subscription Request (renamed) + { 0x18, PacketTypesOut.EditBook }, // Edit Book + { 0x19, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query + { 0x1A, PacketTypesOut.InteractEntity }, // Interact + { 0x1B, PacketTypesOut.GenerateStructure }, // Jigsaw Generate + { 0x1C, PacketTypesOut.KeepAlive }, // Keep Alive + { 0x1D, PacketTypesOut.LockDifficulty }, // Lock Difficulty + { 0x1E, PacketTypesOut.PlayerPosition }, // Move Player Pos + { 0x1F, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot + { 0x20, PacketTypesOut.PlayerRotation }, // Move Player Rot + { 0x21, PacketTypesOut.PlayerMovement }, // Move Player Status Only + { 0x22, PacketTypesOut.VehicleMove }, // Move Vehicle + { 0x23, PacketTypesOut.SteerBoat }, // Paddle Boat + { 0x24, PacketTypesOut.PickItem }, // Pick Item From Block + { 0x25, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity + { 0x26, PacketTypesOut.PingRequest }, // Ping Request + { 0x27, PacketTypesOut.CraftRecipeRequest }, // Place Recipe + { 0x28, PacketTypesOut.PlayerAbilities }, // Player Abilities + { 0x29, PacketTypesOut.PlayerDigging }, // Player Action + { 0x2A, PacketTypesOut.EntityAction }, // Player Command + { 0x2B, PacketTypesOut.SteerVehicle }, // Player Input + { 0x2C, PacketTypesOut.PlayerLoaded }, // Player Loaded + { 0x2D, PacketTypesOut.Pong }, // Pong + { 0x2E, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings + { 0x2F, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe + { 0x30, PacketTypesOut.NameItem }, // Rename Item + { 0x31, PacketTypesOut.ResourcePackStatus }, // Resource Pack + { 0x32, PacketTypesOut.AdvancementTab }, // Seen Advancements + { 0x33, PacketTypesOut.SelectTrade }, // Select Trade + { 0x34, PacketTypesOut.SetBeaconEffect }, // Set Beacon + { 0x35, PacketTypesOut.HeldItemChange }, // Set Carried Item + { 0x36, PacketTypesOut.UpdateCommandBlock }, // Set Command Block + { 0x37, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart + { 0x38, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot + { 0x39, PacketTypesOut.SetGameRule }, // Set Game Rule (new in 26.1) + { 0x3A, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block + { 0x3B, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block + { 0x3C, PacketTypesOut.SetTestBlock }, // Set Test Block + { 0x3D, PacketTypesOut.UpdateSign }, // Sign Update + { 0x3F, PacketTypesOut.Animation }, // Swing + { 0x40, PacketTypesOut.Spectate }, // Teleport To Entity + { 0x41, PacketTypesOut.TestInstanceBlockAction }, // Test Instance Block Action + { 0x42, PacketTypesOut.PlayerBlockPlacement }, // Use Item On + { 0x43, PacketTypesOut.UseItem }, // Use Item + { 0x44, PacketTypesOut.CustomClickAction } // Custom Click Action + }; + + private readonly Dictionary configurationTypesIn = new() + { + { 0x00, ConfigurationPacketTypesIn.CookieRequest }, + { 0x01, ConfigurationPacketTypesIn.PluginMessage }, + { 0x02, ConfigurationPacketTypesIn.Disconnect }, + { 0x03, ConfigurationPacketTypesIn.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesIn.KeepAlive }, + { 0x05, ConfigurationPacketTypesIn.Ping }, + { 0x06, ConfigurationPacketTypesIn.ResetChat }, + { 0x07, ConfigurationPacketTypesIn.RegistryData }, + { 0x08, ConfigurationPacketTypesIn.RemoveResourcePack }, + { 0x09, ConfigurationPacketTypesIn.ResourcePack }, + { 0x0A, ConfigurationPacketTypesIn.StoreCookie }, + { 0x0B, ConfigurationPacketTypesIn.Transfer }, + { 0x0C, ConfigurationPacketTypesIn.FeatureFlags }, + { 0x0D, ConfigurationPacketTypesIn.UpdateTags }, + { 0x0E, ConfigurationPacketTypesIn.KnownDataPacks }, + { 0x0F, ConfigurationPacketTypesIn.CustomReportDetails }, + { 0x10, ConfigurationPacketTypesIn.ServerLinks }, + { 0x11, ConfigurationPacketTypesIn.ClearDialog }, + { 0x12, ConfigurationPacketTypesIn.ShowDialog }, + { 0x13, ConfigurationPacketTypesIn.CodeOfConduct } + }; + + private readonly Dictionary configurationTypesOut = new() + { + { 0x00, ConfigurationPacketTypesOut.ClientInformation }, + { 0x01, ConfigurationPacketTypesOut.CookieResponse }, + { 0x02, ConfigurationPacketTypesOut.PluginMessage }, + { 0x03, ConfigurationPacketTypesOut.FinishConfiguration }, + { 0x04, ConfigurationPacketTypesOut.KeepAlive }, + { 0x05, ConfigurationPacketTypesOut.Pong }, + { 0x06, ConfigurationPacketTypesOut.ResourcePackResponse }, + { 0x07, ConfigurationPacketTypesOut.KnownDataPacks }, + { 0x08, ConfigurationPacketTypesOut.CustomClickAction }, + { 0x09, ConfigurationPacketTypesOut.AcceptCodeOfConduct } + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => configurationTypesIn!; + protected override Dictionary GetConfigurationListOut() => configurationTypesOut!; +} diff --git a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs index e012f853..82ee6e69 100644 --- a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs +++ b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs @@ -1,4 +1,4 @@ -using System; +using System; using MinecraftClient.Protocol.Handlers.PacketPalettes; namespace MinecraftClient.Protocol.Handlers @@ -48,9 +48,15 @@ namespace MinecraftClient.Protocol.Handlers { PacketTypePalette p = protocol switch { - > Protocol18Handler.MC_1_20_4_Version => throw new NotImplementedException(Translations + > Protocol18Handler.MC_26_1_Version => throw new NotImplementedException(Translations .exception_palette_packet), + >= Protocol18Handler.MC_26_1_Version => new PacketPalette261(), + <= Protocol18Handler.MC_1_21_11_Version and > Protocol18Handler.MC_1_21_7_Version => new PacketPalette1219(), + <= Protocol18Handler.MC_1_21_7_Version and > Protocol18Handler.MC_1_21_5_Version => new PacketPalette1216(), + <= Protocol18Handler.MC_1_21_5_Version and > Protocol18Handler.MC_1_21_4_Version => new PacketPalette1215(), + <= Protocol18Handler.MC_1_21_4_Version and > Protocol18Handler.MC_1_21_2_Version => new PacketPalette1214(), <= Protocol18Handler.MC_1_8_Version => new PacketPalette17(), + <= Protocol18Handler.MC_1_9_2_Version => new PacketPalette19(), <= Protocol18Handler.MC_1_11_2_Version => new PacketPalette110(), <= Protocol18Handler.MC_1_12_Version => new PacketPalette112(), <= Protocol18Handler.MC_1_12_2_Version => new PacketPalette1122(), @@ -67,7 +73,10 @@ namespace MinecraftClient.Protocol.Handlers <= Protocol18Handler.MC_1_19_4_Version => new PacketPalette1194(), <= Protocol18Handler.MC_1_20_Version => new PacketPalette1194(), <= Protocol18Handler.MC_1_20_2_Version => new PacketPalette1202(), - _ => new PacketPalette1204() + <= Protocol18Handler.MC_1_20_4_Version => new PacketPalette1204(), + <= Protocol18Handler.MC_1_20_6_Version => new PacketPalette1206(), + <= Protocol18Handler.MC_1_21_Version => new PacketPalette121(), + _ => new PacketPalette1212() }; p.SetForgeEnabled(forgeEnabled); diff --git a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs index b4b8debc..8aee524a 100644 --- a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs +++ b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Protocol.Handlers +namespace MinecraftClient.Protocol.Handlers { /// /// Incoming packet types @@ -29,9 +29,12 @@ CloseWindow, // CollectItem, // CombatEvent, // + CookieRequest, // Added in 1.20.6 CraftRecipeResponse, // + CustomReportDetails, // Added in 1.21 (Not used) DamageEvent, // Added in 1.19.4 DeathCombatEvent, // + DebugSample, // Added in 1.20.6 DeclareCommands, // DeclareRecipes, // DestroyEntities, // @@ -48,6 +51,7 @@ EntityMovement, // EntityPosition, // EntityPositionAndRotation, // + EntityPositionSync, // Added in 1.21.2 EntityProperties, // EntityRotation, // EntitySoundEffect, // @@ -55,6 +59,7 @@ EntityTeleport, // EntityVelocity, // Explosion, // + MoveMinecartAlongTrack, // Added in 1.21.2 FacePlayer, // FeatureFlags, // Added in 1.19.3 HeldItemChange, // @@ -81,22 +86,31 @@ PlayerListHeaderAndFooter, // PlayerRemove, // Added in 1.19.3 (Not used) PlayerPositionAndLook, // + PlayerRotation, // Added in 1.21.2 PluginMessage, // ProfilelessChatMessage, // Added in 1.19.3 + ProjectilePower, // Added in 1.20.6 RemoveEntityEffect, // RemoveResourcePack, // Added in 1.20.3 ResetScore, // Added in 1.20.3 ResourcePackSend, // Respawn, // + RecipeBookAdd, // Added in 1.21.2 (replaces UnlockRecipes) + RecipeBookRemove, // Added in 1.21.2 + RecipeBookSettings, // Added in 1.21.2 ScoreboardObjective, // SelectAdvancementTab, // ServerData, // Added in 1.19 ServerDifficulty, // + ServerLinks, // Added in 1.21 (Not used) SetCompression, // For 1.8 or below SetCooldown, // + SetCursorItem, // Added in 1.21.2 SetDisplayChatPreview, // Added in 1.19 SetExperience, // + SetHeldSlot, // Added in 1.21.2 (replaces HeldItemChange clientbound) SetPassengers, // + SetPlayerInventory, // Added in 1.21.2 SetSlot, // SetTickingState, // Added in 1.20.3 StepTick, // Added in 1.20.3 @@ -115,13 +129,16 @@ StartConfiguration, // Added in 1.20.2 Statistics, // StopSound, // + StoreCookie, // Added in 1.20.6 SystemChat, // Added in 1.19 TabComplete, // Tags, // Teams, // + TestInstanceBlockStatus, // Added in 1.21.5 TimeUpdate, // Title, // TradeList, // + Transfer, // Added in 1.20.6 Unknown, // For old version packet that have been removed and not used by mcc UnloadChunk, // UnlockRecipes, // @@ -129,7 +146,7 @@ UpdateHealth, // UpdateLight, // UpdateScore, // - UpdateSign, // For 1.8 or below + UpdateSign, // For 1.8 or below, and 1.9-1.9.2 UpdateSimulationDistance, // UpdateViewDistance, // UpdateViewPosition, // @@ -144,5 +161,15 @@ WorldBorderSize, // WorldBorderWarningDelay, // WorldBorderWarningReach, // + Waypoint, // Added in 1.21.6 + ClearDialog, // Added in 1.21.6 + ShowDialog, // Added in 1.21.6 + DebugBlockValue, // Added in 1.21.9 + DebugChunkValue, // Added in 1.21.9 + DebugEntityValue, // Added in 1.21.9 + DebugEvent, // Added in 1.21.9 + GameTestHighlightPos, // Added in 1.21.9 + GameRuleValues, // Added in 26.1 + LowDiskSpaceWarning, // Added in 26.1 } } diff --git a/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs b/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs index 61221968..99704ae8 100644 --- a/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs +++ b/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Protocol.Handlers +namespace MinecraftClient.Protocol.Handlers { /// /// Outgoing packet types @@ -8,6 +8,7 @@ AcknowledgeConfiguration, // Added in 1.20.2 AdvancementTab, // Animation, // + BundleItemSelected, // Added in 1.21.2 ChangeContainerSlotState, // Added in 1.20.3 ChatCommand, // Added in 1.19 ChatMessage, // @@ -17,9 +18,12 @@ ClickWindowButton, // ClientSettings, // ClientStatus, // + ClientTickEnd, // Added in 1.21.2 CloseWindow, // CraftRecipeRequest, // CreativeInventoryAction, // + CookieResponse, // Added in 1.20.6 + DebugSampleSubscription, // Added in 1.20.6 EditBook, // EnchantItem, // For 1.13.2 or below EntityAction, // @@ -28,14 +32,17 @@ HeldItemChange, // InteractEntity, // KeepAlive, // + KnownDataPacks, // Added in 1.20.6 LockDifficulty, // MessageAcknowledgment, // Added in 1.19.1 (1.19.2) NameItem, // PickItem, // + PickItemFromEntity, // Added in 1.21.4 (split from PickItem) PingRequest, // Added in 1.20.2 PlayerAbilities, // PlayerBlockPlacement, // PlayerDigging, // + PlayerLoaded, // Added in 1.21.4 PlayerMovement, // PlayerPosition, // PlayerPositionAndRotation, // @@ -52,6 +59,7 @@ SetDifficulty, // SetDisplayedRecipe, // Added in 1.16.2 SetRecipeBookState, // Added in 1.16.2 + SignedChatCommand, // Added in 1.20.6 Spectate, // SteerBoat, // SteerVehicle, // @@ -63,8 +71,14 @@ UpdateJigsawBlock, // UpdateSign, // UpdateStructureBlock, // + SetTestBlock, // Added in 1.21.5 + TestInstanceBlockAction, // Added in 1.21.5 UseItem, // VehicleMove, // WindowConfirmation, // + ChangeGameMode, // Added in 1.21.6 + CustomClickAction, // Added in 1.21.6 + Attack, // Added in 26.1 + SetGameRule, // Added in 26.1 } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index e4faf0bf..f7bd3d4a 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net.Sockets; @@ -70,24 +71,41 @@ namespace MinecraftClient.Protocol.Handlers private void Updater(object? o) { - if (((CancellationToken)o!).IsCancellationRequested) + var cancelToken = (CancellationToken)o!; + + if (cancelToken.IsCancellationRequested) return; try { - while (!((CancellationToken)o!).IsCancellationRequested) + Stopwatch stopWatch = Stopwatch.StartNew(); + long nextUpdateDue = 0; + + while (!cancelToken.IsCancellationRequested) { - do + cancelToken.ThrowIfCancellationRequested(); + + long elapsedMilliseconds = stopWatch.ElapsedMilliseconds; + while (elapsedMilliseconds >= nextUpdateDue) { - Thread.Sleep(100); - } while (Update()); + if (!Update()) + return; + + nextUpdateDue += ClientTickIntervalMilliseconds; + elapsedMilliseconds = stopWatch.ElapsedMilliseconds; + } + + long sleepLength = nextUpdateDue - stopWatch.ElapsedMilliseconds; + if (sleepLength > 1) + Thread.Sleep((int)Math.Min(sleepLength, ClientTickIntervalMilliseconds)); } } catch (System.IO.IOException) { } catch (SocketException) { } catch (ObjectDisposedException) { } + catch (OperationCanceledException) { } - if (((CancellationToken)o!).IsCancellationRequested) + if (cancelToken.IsCancellationRequested) return; handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, ""); @@ -114,6 +132,7 @@ namespace MinecraftClient.Protocol.Handlers byte[] keepalive = new byte[5] { 0, 0, 0, 0, 0 }; Receive(keepalive, 1, 4, SocketFlags.None); handler.OnServerKeepAlive(); + handler.SetCanSendMessage(true); Send(keepalive); break; case 0x01: ReadData(4); ReadNextString(); ReadData(5); break; case 0x02: ReadData(1); ReadNextString(); ReadNextString(); ReadData(4); break; @@ -190,7 +209,8 @@ namespace MinecraftClient.Protocol.Handlers case 0xC9: string name = ReadNextString(); bool online = ReadNextByte() != 0x00; ReadData(2); Guid FakeUUID = new(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) { handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); } else { handler.OnPlayerLeave(FakeUUID); } + if (online) handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); + else handler.OnPlayerLeave(FakeUUID); break; case 0xCA: if (protocolversion >= 72) { ReadData(9); } else ReadData(3); break; case 0xCB: @@ -233,14 +253,29 @@ namespace MinecraftClient.Protocol.Handlers /// Net read thread ID public int GetNetMainThreadId() { - return netRead != null ? netRead.Item1.ManagedThreadId : -1; + return netRead is not null ? netRead.Item1.ManagedThreadId : -1; + } + + public bool SendCookieResponse(string name, byte[]? data) + { + throw new NotImplementedException(); + } + + public bool SendKnownDataPacks(List<(string, string, string)> knownDataPacks) + { + throw new NotImplementedException(); + } + + public bool SendCustomClickAction(string id, Dictionary? payload) + { + return false; } public void Dispose() { try { - if (netRead != null) + if (netRead is not null) { netRead.Item2.Cancel(); c.Close(); @@ -522,13 +557,13 @@ namespace MinecraftClient.Protocol.Handlers if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + Translations.debug_crypto, acceptnewlines: true); - if (serverIDhash != "-") + if (serverIDhash != "-" && !string.IsNullOrWhiteSpace(sessionID)) { ConsoleIO.WriteLine(Translations.mcc_session); string serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey); bool needCheckSession = true; - if (session.ServerPublicKey != null && session.SessionPreCheckTask != null + if (session.ServerPublicKey is not null && session.SessionPreCheckTask is not null && serverIDhash == session.ServerIDhash && Enumerable.SequenceEqual(serverPublicKey, session.ServerPublicKey)) { session.SessionPreCheckTask.Wait(); @@ -590,7 +625,7 @@ namespace MinecraftClient.Protocol.Handlers } } - public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) + public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false) { if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session)) { @@ -668,7 +703,8 @@ namespace MinecraftClient.Protocol.Handlers public int GetMaxChatMessageLength() { - return 100; + int configOverride = Settings.MainConfigHelper.Config.Advanced.MaxChatMessageLength; + return configOverride > 0 ? configOverride : 100; } public int GetProtocolVersion() @@ -727,7 +763,7 @@ namespace MinecraftClient.Protocol.Handlers return false; //Currently not implemented } - public bool SendLocationUpdate(Location location, bool onGround, float? yaw, float? pitch) + public bool SendLocationUpdate(Location location, bool onGround, bool horizontalCollision, float? yaw, float? pitch) { return false; //Currently not implemented } @@ -782,6 +818,16 @@ namespace MinecraftClient.Protocol.Handlers return false; //Currently not implemented } + public bool SendPlaceRecipe(int windowId, string recipeId, bool makeAll) + { + return false; //MC 1.8-1.12.1 recipe book not supported + } + + public bool SendEditBook(Item currentBook, IReadOnlyList pages, string? title, string author, int selectedHotbarSlot) + { + return false; //MC 1.4.6-1.6.4 book editing is not supported + } + public bool SendCloseWindow(int windowId) { return false; //Currently not implemented @@ -910,7 +956,7 @@ namespace MinecraftClient.Protocol.Handlers { return false; //Currently not implemented } - + public bool SendRenameItem(string itemName) { return false; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 6b0fa3db..90786f5b 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -10,6 +10,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using MinecraftClient.Crypto; +using MinecraftClient.Dialogs; using MinecraftClient.Inventory; using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Logger; @@ -19,6 +20,7 @@ using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.packet.s2c; using MinecraftClient.Protocol.Handlers.PacketPalettes; +using MinecraftClient.Protocol.Dialogs; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; @@ -45,6 +47,7 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_8_Version = 47; internal const int MC_1_9_Version = 107; internal const int MC_1_9_1_Version = 108; + internal const int MC_1_9_2_Version = 109; internal const int MC_1_10_Version = 210; internal const int MC_1_11_Version = 315; internal const int MC_1_11_2_Version = 316; @@ -71,22 +74,39 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_20_Version = 763; internal const int MC_1_20_2_Version = 764; internal const int MC_1_20_4_Version = 765; + internal const int MC_1_20_6_Version = 766; + internal const int MC_1_21_Version = 767; + internal const int MC_1_21_2_Version = 768; + internal const int MC_1_21_4_Version = 769; + internal const int MC_1_21_5_Version = 770; + internal const int MC_1_21_6_Version = 771; + internal const int MC_1_21_7_Version = 772; + internal const int MC_1_21_9_Version = 773; + internal const int MC_1_21_11_Version = 774; + internal const int MC_26_1_Version = 775; private int compression_treshold = -1; private int autocomplete_transaction_id = 0; private readonly Dictionary window_actions = new(); private CurrentState currentState = CurrentState.Login; private readonly int protocolVersion; + private readonly int rawProtocolVersion; private int currentDimension; private bool isOnlineMode = false; private readonly BlockingCollection>> packetQueue = new(); + private readonly Dictionary legacyAchievementProgress = new(StringComparer.Ordinal); private float LastYaw, LastPitch; + private double lastSentX, lastSentY, lastSentZ; + private float lastSentYaw, lastSentPitch; + private bool lastSentOnGround; + private bool lastSentHorizontalCollision; + private int positionReminder; private long chunkBatchStartTime; private double aggregatedNanosPerChunk = 2000000.0; private int oldSamplesWeight = 1; private bool receiveDeclareCommands = false, receivePlayerInfo = false; - private object MessageSigningLock = new(); + private readonly Lock MessageSigningLock = new(); private Guid chatUuid = Guid.NewGuid(); private int pendingAcknowledgments = 0, messageIndex = 0; private LastSeenMessagesCollector lastSeenMessagesCollector; @@ -100,43 +120,46 @@ namespace MinecraftClient.Protocol.Handlers readonly PacketTypePalette packetPalette; readonly SocketWrapper socketWrapper; readonly DataTypes dataTypes; + readonly DialogNbtParser dialogNbtParser = new(); Tuple? netMain = null; // main thread Tuple? netReader = null; // reader thread readonly ILogger log; readonly RandomNumberGenerator randomGen; + private bool legacyAchievementsInitialized; public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, - ForgeInfo? forgeInfo) + ForgeInfo? forgeInfo, int rawProtocolVersion = 0) { ConsoleIO.SetAutoCompleteEngine(this); ChatParser.InitTranslations(); socketWrapper = new SocketWrapper(Client); dataTypes = new DataTypes(protocolVersion); this.protocolVersion = protocolVersion; + this.rawProtocolVersion = rawProtocolVersion != 0 ? rawProtocolVersion : protocolVersion; this.handler = handler; pForge = new Protocol18Forge(forgeInfo, protocolVersion, dataTypes, this, handler); pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler); - packetPalette = new PacketTypeHandler(protocolVersion, forgeInfo != null).GetTypeHandler(); + packetPalette = new PacketTypeHandler(protocolVersion, forgeInfo is not null).GetTypeHandler(); log = handler.GetLogger(); randomGen = RandomNumberGenerator.Create(); lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); chunkBatchStartTime = GetNanos(); - if (handler.GetTerrainEnabled() && protocolVersion > MC_1_20_4_Version) + if (handler.GetTerrainEnabled() && protocolVersion > MC_26_1_Version) { log.Error($"§c{Translations.extra_terrainandmovement_disabled}"); handler.SetTerrainEnabled(false); } if (handler.GetInventoryEnabled() && - protocolVersion is < MC_1_8_Version or > MC_1_20_4_Version) + protocolVersion is < MC_1_8_Version or > MC_26_1_Version) { log.Error($"§c{Translations.extra_inventory_disabled}"); handler.SetInventoryEnabled(false); } if (handler.GetEntityHandlingEnabled() && - protocolVersion is < MC_1_8_Version or > MC_1_20_4_Version) + protocolVersion is < MC_1_8_Version or > MC_26_1_Version) { log.Error($"§c{Translations.extra_entity_disabled}"); handler.SetEntityHandlingEnabled(false); @@ -145,8 +168,15 @@ namespace MinecraftClient.Protocol.Handlers Block.Palette = protocolVersion switch { // Block palette - > MC_1_20_4_Version when handler.GetTerrainEnabled() => + > MC_26_1_Version when handler.GetTerrainEnabled() => throw new NotImplementedException(Translations.exception_palette_block), + >= MC_26_1_Version => new Palette261(), + >= MC_1_21_9_Version => new Palette1219(), + >= MC_1_21_6_Version => new Palette1216(), // 1.21.7/1.21.8 blocks unchanged, reuse 1216 + >= MC_1_21_5_Version => new Palette1215(), + >= MC_1_21_4_Version => new Palette1214(), + >= MC_1_21_2_Version => new Palette1212(), + >= MC_1_20_6_Version => new Palette1206(), >= MC_1_20_4_Version => new Palette1204(), >= MC_1_20_Version => new Palette120(), MC_1_19_4_Version => new Palette1194(), @@ -163,8 +193,16 @@ namespace MinecraftClient.Protocol.Handlers entityPalette = protocolVersion switch { // Entity palette - > MC_1_20_4_Version when handler.GetEntityHandlingEnabled() => + > MC_26_1_Version when handler.GetEntityHandlingEnabled() => throw new NotImplementedException(Translations.exception_palette_entity), + >= MC_26_1_Version => new EntityPalette261(), + >= MC_1_21_11_Version => new EntityPalette12111(), + >= MC_1_21_9_Version => new EntityPalette1219(), + >= MC_1_21_6_Version => new EntityPalette1216(), // 1.21.7/1.21.8 entities unchanged, reuse 1216 + >= MC_1_21_5_Version => new EntityPalette1215(), + >= MC_1_21_4_Version => new EntityPalette1214(), + >= MC_1_21_2_Version => new EntityPalette1212(), + >= MC_1_20_6_Version => new EntityPalette1206(), >= MC_1_20_4_Version => new EntityPalette1204(), >= MC_1_20_Version => new EntityPalette120(), MC_1_19_4_Version => new EntityPalette1194(), @@ -177,6 +215,7 @@ namespace MinecraftClient.Protocol.Handlers >= MC_1_14_Version => new EntityPalette114(), >= MC_1_13_Version => new EntityPalette113(), >= MC_1_12_Version => new EntityPalette112(), + >= MC_1_11_Version => new EntityPalette112(), _ => new EntityPalette18() }; @@ -185,8 +224,18 @@ namespace MinecraftClient.Protocol.Handlers itemPalette = protocolVersion switch { // Item palette - > MC_1_20_4_Version when handler.GetInventoryEnabled() => + > MC_26_1_Version when handler.GetInventoryEnabled() => throw new NotImplementedException(Translations.exception_palette_item), + >= MC_26_1_Version => new ItemPalette261(), + >= MC_1_21_11_Version => new ItemPalette12111(), + >= MC_1_21_9_Version => new ItemPalette1219(), + >= MC_1_21_7_Version => new ItemPalette1217(), + >= MC_1_21_6_Version => new ItemPalette1216(), + >= MC_1_21_5_Version => new ItemPalette1215(), + >= MC_1_21_4_Version => new ItemPalette1214(), + >= MC_1_21_2_Version => new ItemPalette1212(), + >= MC_1_21_Version => new ItemPalette121(), + >= MC_1_20_6_Version => new ItemPalette1206(), >= MC_1_20_4_Version => new ItemPalette1204(), >= MC_1_20_Version => new ItemPalette120(), MC_1_19_4_Version => new ItemPalette1194(), @@ -197,6 +246,9 @@ namespace MinecraftClient.Protocol.Handlers >= MC_1_16_2_Version => new ItemPalette1162(), >= MC_1_16_1_Version => new ItemPalette1161(), >= MC_1_15_Version => new ItemPalette115(), + >= MC_1_14_Version => new ItemPalette114(), + >= MC_1_13_2_Version => new ItemPalette1132(), + >= MC_1_13_Version => new ItemPalette113(), >= MC_1_12_Version => new ItemPalette112(), >= MC_1_11_Version => new ItemPalette111(), >= MC_1_10_Version => new ItemPalette110(), @@ -239,49 +291,64 @@ namespace MinecraftClient.Protocol.Handlers private void Updater(object? o) { var cancelToken = (CancellationToken)o!; + var exitReason = Translations.debug_packet_loop_reason_queue_completed; if (cancelToken.IsCancellationRequested) return; try { - Stopwatch stopWatch = new(); + Stopwatch stopWatch = Stopwatch.StartNew(); + long nextUpdateDue = 0; while (!packetQueue.IsAddingCompleted) { cancelToken.ThrowIfCancellationRequested(); - handler.OnUpdate(); - stopWatch.Restart(); + long elapsedMilliseconds = stopWatch.ElapsedMilliseconds; + while (elapsedMilliseconds >= nextUpdateDue) + { + handler.OnUpdate(); + nextUpdateDue += ClientTickIntervalMilliseconds; + elapsedMilliseconds = stopWatch.ElapsedMilliseconds; + } - while (packetQueue.TryTake(out var packetInfo)) + if (packetQueue.TryTake(out var packetInfo, 1)) { var (packetId, packetData) = packetInfo; HandlePacket(packetId, packetData); - - if (stopWatch.Elapsed.Milliseconds < 100) continue; - - handler.OnUpdate(); - stopWatch.Restart(); + continue; } - var sleepLength = 100 - stopWatch.Elapsed.Milliseconds; - if (sleepLength > 0) - Thread.Sleep(sleepLength); + long sleepLength = nextUpdateDue - stopWatch.ElapsedMilliseconds; + if (sleepLength > 1) + Thread.Sleep((int)Math.Min(sleepLength, ClientTickIntervalMilliseconds)); } } catch (ObjectDisposedException) { + exitReason = nameof(ObjectDisposedException); } catch (OperationCanceledException) { + exitReason = nameof(OperationCanceledException); } catch (NullReferenceException) { + exitReason = nameof(NullReferenceException); + } + catch (SocketException) + { + exitReason = nameof(SocketException); + } + catch (System.IO.IOException) + { + exitReason = nameof(System.IO.IOException); } if (cancelToken.IsCancellationRequested) return; + LogNetworkLoopExit(nameof(Updater), exitReason); handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, ""); } @@ -291,13 +358,17 @@ namespace MinecraftClient.Protocol.Handlers internal void PacketReader(object? o) { var cancelToken = (CancellationToken)o!; + var exitReason = Translations.debug_packet_loop_reason_socket_closed; while (socketWrapper.IsConnected() && !cancelToken.IsCancellationRequested) { try { while (socketWrapper.HasDataAvailable()) { - packetQueue.Add(ReadNextPacket(), cancelToken); + var packet = ReadNextPacket(); + if (packet.Item1 == -1) + continue; + packetQueue.Add(packet, cancelToken); if (cancelToken.IsCancellationRequested) break; @@ -305,31 +376,43 @@ namespace MinecraftClient.Protocol.Handlers } catch (OperationCanceledException) { + exitReason = nameof(OperationCanceledException); break; } catch (System.IO.IOException) { + exitReason = nameof(System.IO.IOException); break; } catch (SocketException) { + exitReason = nameof(SocketException); break; } catch (NullReferenceException) { + exitReason = nameof(NullReferenceException); break; } - catch (Ionic.Zlib.ZlibException) + catch (System.IO.InvalidDataException) { + exitReason = nameof(System.IO.InvalidDataException); break; } if (cancelToken.IsCancellationRequested) + { + exitReason = Translations.debug_packet_loop_reason_cancelled; break; + } Thread.Sleep(10); } + if (cancelToken.IsCancellationRequested) + exitReason = Translations.debug_packet_loop_reason_cancelled; + + LogNetworkLoopExit(nameof(PacketReader), exitReason); packetQueue.CompleteAdding(); } @@ -341,22 +424,34 @@ namespace MinecraftClient.Protocol.Handlers internal Tuple> ReadNextPacket() { var size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size - Queue packetData = new(socketWrapper.ReadDataRAW(size)); //Packet contents + var rawBytes = socketWrapper.ReadDataRAW(size); + Queue packetData = new(rawBytes); //Packet contents + var compressed = false; + var sizeUncompressed = 0; //Handle packet decompression if (protocolVersion >= MC_1_8_Version && compression_treshold >= 0) { - var sizeUncompressed = dataTypes.ReadNextVarInt(packetData); + sizeUncompressed = dataTypes.ReadNextVarInt(packetData); if (sizeUncompressed != 0) // != 0 means compressed, let's decompress { var toDecompress = packetData.ToArray(); var uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed); packetData = new Queue(uncompressed); + compressed = true; } } + if (packetData.Count == 0) + { + var rawHex = rawBytes.Length > 0 ? BitConverter.ToString(rawBytes).Replace("-", " ") : "(empty)"; + log.Debug("Empty packet after decompress: size={0}, sizeUncompressed={1}, protocol={2}, state={3}, rawBytes=[{4}]", size, sizeUncompressed, protocolVersion, currentState, rawHex); + return new(-1, packetData); + } + var packetId = dataTypes.ReadNextVarInt(packetData); // Packet ID + LogIncomingPacket(packetId, packetData.Count, size, compressed, sizeUncompressed); if (handler.GetNetworkPacketCaptureEnabled()) handler.OnNetworkPacket(packetId, packetData.ToList(), currentState == CurrentState.Login, true); @@ -396,7 +491,15 @@ namespace MinecraftClient.Protocol.Handlers List responseData = new(); var understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); SendLoginPluginResponse(messageId, understood, responseData.ToArray()); - return understood; + break; + + // Cookie Request + case 0x05: + var cookieName = dataTypes.ReadNextString(packetData); + var cookieData = null as byte[]; + McClient.Instance?.GetCookie(cookieName, out cookieData); + SendCookieResponse(cookieName, cookieData); + break; // Ignore other packets at this stage default: @@ -407,15 +510,34 @@ namespace MinecraftClient.Protocol.Handlers // https://wiki.vg/Protocol#Configuration case CurrentState.Configuration: - switch (packetPalette.GetIncomingConfigurationTypeById(packetId)) + if (!packetPalette.GetMappingInConfiguration().TryGetValue(packetId, out var configurationPacketType)) { + if (packetPalette.GetMappingIn().ContainsKey(packetId)) + { + SetCurrentState(CurrentState.Play); + return HandlePlayPackets(packetId, packetData); + } + + throw new KeyNotFoundException("Configuration Packet ID of 0x" + packetId.ToString("X2") + + " doesn't exist!"); + } + + switch (configurationPacketType) + { + case ConfigurationPacketTypesIn.CookieRequest: + var cookieName = dataTypes.ReadNextString(packetData); + var cookieData = null as byte[]; + McClient.Instance?.GetCookie(cookieName, out cookieData); + SendCookieResponse(cookieName, cookieData); + break; + case ConfigurationPacketTypesIn.Disconnect: handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, dataTypes.ReadNextChat(packetData)); return false; case ConfigurationPacketTypesIn.FinishConfiguration: - currentState = CurrentState.Play; + SetCurrentState(CurrentState.Play); SendPacket(ConfigurationPacketTypesOut.FinishConfiguration, new List()); break; @@ -428,23 +550,144 @@ namespace MinecraftClient.Protocol.Handlers break; case ConfigurationPacketTypesIn.RegistryData: - var registryCodec = dataTypes.ReadNextNbt(packetData); - ChatParser.ReadChatType(registryCodec); + if (protocolVersion < MC_1_20_6_Version) + { + var registryCodec = dataTypes.ReadNextNbt(packetData); + ChatParser.ReadChatType(registryCodec); - if (handler.GetTerrainEnabled()) - World.StoreDimensionList(registryCodec); + if (handler.GetTerrainEnabled()) + World.StoreDimensionList(registryCodec); + } + else + { + var registryId = dataTypes.ReadNextString(packetData); + var entryCount = dataTypes.ReadNextVarInt(packetData); + + var isChat = registryId == "minecraft:chat_type"; + var isDimension = registryId == "minecraft:dimension_type"; + var isAttribute = registryId == "minecraft:attribute"; + var isEnchantment = registryId == "minecraft:enchantment"; + var isDialog = registryId == "minecraft:dialog"; + + var availableChats = isChat ? new Dictionary() : null; + var dimensionIdMap = isDimension ? new Dictionary() : null; + var attributeIdMap = isAttribute ? new Dictionary() : null; + var enchantmentIdMap = isEnchantment ? new Dictionary() : null; + + for (var i = 0; i < entryCount; i++) + { + var entryId = dataTypes.ReadNextString(packetData); + var hasData = dataTypes.ReadNextBool(packetData); + + Dictionary? nbtData = null; + if (hasData) + nbtData = dataTypes.ReadNextNbt(packetData); + + if (isChat) + availableChats!.Add(i, entryId); + else if (isDimension) + { + dimensionIdMap!.Add(i, entryId); + if (nbtData is not null && handler.GetTerrainEnabled()) + World.StoreOneDimension(entryId, nbtData); + } + else if (isAttribute) + { + var attrName = entryId.StartsWith("minecraft:") + ? entryId.Substring("minecraft:".Length) + : entryId; + attributeIdMap!.Add(i, attrName); + } + else if (isEnchantment) + enchantmentIdMap!.Add(i, entryId); + else if (isDialog && nbtData is not null) + handler.OnDialogRegistryData(i, entryId, dialogNbtParser.Parse(nbtData)); + } + + if (isChat) + ChatParser.ReadChatType(availableChats!); + else if (isDimension) + { + World.SetDimensionIdMap(dimensionIdMap!); + if (!handler.GetTerrainEnabled() || !World.HasAnyDimension()) + World.LoadDefaultDimensions1206Plus(); + } + else if (isAttribute) + World.SetAttributeIdMap(attributeIdMap!); + else if (isEnchantment) + EnchantmentMapping.SetDynamicEnchantmentIdMap(enchantmentIdMap!); + } break; - + case ConfigurationPacketTypesIn.RemoveResourcePack: if (dataTypes.ReadNextBool(packetData)) // Has UUID - dataTypes.ReadNextUUID(packetData); // UUID + ChatParser.RemoveResourcePackTranslations(dataTypes.ReadNextUUID(packetData).ToString("D")); // UUID + else + ChatParser.ClearResourcePackTranslations(); break; case ConfigurationPacketTypesIn.ResourcePack: HandleResourcePackPacket(packetData); break; + case ConfigurationPacketTypesIn.StoreCookie: + var name = dataTypes.ReadNextString(packetData); + var data = dataTypes.ReadNextByteArray(packetData); + McClient.Instance?.SetCookie(name, data); + break; + + case ConfigurationPacketTypesIn.Transfer: + var host = dataTypes.ReadNextString(packetData); + var port = dataTypes.ReadNextVarInt(packetData); + + McClient.Instance?.Transfer(host, port); + break; + + case ConfigurationPacketTypesIn.KnownDataPacks: + var knownPacksCount = dataTypes.ReadNextVarInt(packetData); + List<(string, string, string)> knownDataPacks = new(); + + for (var i = 0; i < knownPacksCount; i++) + { + var nameSpace = dataTypes.ReadNextString(packetData); + var id = dataTypes.ReadNextString(packetData); + var version = dataTypes.ReadNextString(packetData); + knownDataPacks.Add((nameSpace, id, version)); + } + + var vanillaPacks = knownDataPacks + .Where(p => p.Item1 == "minecraft") + .ToList(); + SendKnownDataPacks(vanillaPacks); + break; + + case ConfigurationPacketTypesIn.CustomReportDetails: + var cfgDetailsCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < cfgDetailsCount; i++) + { + dataTypes.ReadNextString(packetData); // Title + dataTypes.ReadNextString(packetData); // Description + } + break; + + case ConfigurationPacketTypesIn.CodeOfConduct: + dataTypes.ReadNextString(packetData); // Code of conduct text + SendPacket(ConfigurationPacketTypesOut.AcceptCodeOfConduct, new List()); + break; + + case ConfigurationPacketTypesIn.ServerLinks: + handler.OnServerLinksUpdated(ReadServerLinks(packetData)); + break; + + case ConfigurationPacketTypesIn.ClearDialog: + handler.OnDialogCleared(); + break; + + case ConfigurationPacketTypesIn.ShowDialog: + HandleShowDialog(packetData, DialogPhase.Configuration); + break; + // Ignore other packets at this stage default: return true; @@ -474,7 +717,7 @@ namespace MinecraftClient.Protocol.Handlers currentState == CurrentState.Login, innerException.GetType()), innerException); - + SentrySdk.AddBreadcrumb(new Breadcrumb("S -> C Packet", "network", new Dictionary() { { "Packet ID", packetId.ToString() }, @@ -503,6 +746,15 @@ namespace MinecraftClient.Protocol.Handlers var url = dataTypes.ReadNextString(packetData); var hash = dataTypes.ReadNextString(packetData); + // Use the server-provided UUID when available, then fall back to the legacy SHA-1 hash, + // and finally the URL so pre-UUID resource packs can still be replaced or cleared locally. + string packIdentifier; + if (uuid != Guid.Empty) + packIdentifier = uuid.ToString("D"); + else if (hash.Length == 40) + packIdentifier = hash; + else + packIdentifier = url; if (protocolVersion >= MC_1_17_Version) { @@ -520,7 +772,7 @@ namespace MinecraftClient.Protocol.Handlers var responseHeader = protocolVersion < MC_1_10_Version // After 1.10, the MC does not include resource pack hash in responses ? dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)) - : Array.Empty(); + : []; var basePacketData = protocolVersion >= MC_1_20_4_Version && uuid != Guid.Empty ? dataTypes.ConcatBytes(responseHeader, DataTypes.GetUUID(uuid)) @@ -540,6 +792,8 @@ namespace MinecraftClient.Protocol.Handlers SendPacket(PacketTypesOut.ResourcePackStatus, acceptedResourcePackData); // Accepted SendPacket(PacketTypesOut.ResourcePackStatus, loadedResourcePackData); // Successfully loaded } + + ChatParser.LoadResourcePackTranslations(packIdentifier, url, hash); } private bool HandlePlayPackets(int packetId, Queue packetData) @@ -557,7 +811,7 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.JoinGame: // Temporary fix - log.Debug("Receive JoinGame"); + log.PacketDebug("Receive JoinGame"); receiveDeclareCommands = receivePlayerInfo = false; @@ -594,7 +848,7 @@ namespace MinecraftClient.Protocol.Handlers { var registryCodec = dataTypes.ReadNextNbt( - packetData); // Registry Codec (Dimension Codec) - 1.16 and above + packetData); // Registry Codec (Dimension Codec) - 1.16 - 1.20.1 if (protocolVersion >= MC_1_19_Version) ChatParser.ReadChatType(registryCodec); if (handler.GetTerrainEnabled()) @@ -615,26 +869,26 @@ namespace MinecraftClient.Protocol.Handlers switch (protocolVersion) { case >= MC_1_16_Version: - { - switch (protocolVersion) { - case >= MC_1_19_Version: - dimensionTypeName = - dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - break; - case >= MC_1_16_2_Version: - dimensionType = - dataTypes.ReadNextNbt( - packetData); // Dimension Type: NBT Tag Compound - break; - default: - dataTypes.ReadNextString(packetData); - break; - } + switch (protocolVersion) + { + case >= MC_1_19_Version: + dimensionTypeName = + dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + break; + case >= MC_1_16_2_Version: + dimensionType = + dataTypes.ReadNextNbt( + packetData); // Dimension Type: NBT Tag Compound + break; + default: + dimensionTypeName = dataTypes.ReadNextString(packetData); + break; + } - currentDimension = 0; - break; - } + currentDimension = 0; + break; + } case >= MC_1_9_1_Version: currentDimension = dataTypes.ReadNextInt(packetData); break; @@ -649,27 +903,27 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below break; case >= MC_1_16_Version: - { - var dimensionName = - dataTypes.ReadNextString( - packetData); // Dimension Name (World Name) - 1.16 and above - - if (handler.GetTerrainEnabled()) { - switch (protocolVersion) - { - case >= MC_1_16_2_Version and <= MC_1_18_2_Version: - World.StoreOneDimension(dimensionName, dimensionType!); - World.SetDimension(dimensionName); - break; - default: - World.SetDimension(dimensionTypeName!); - break; - } - } + var dimensionName = + dataTypes.ReadNextString( + packetData); // Dimension Name (World Name) - 1.16 and above - break; - } + if (handler.GetTerrainEnabled()) + { + switch (protocolVersion) + { + case >= MC_1_16_2_Version and <= MC_1_18_2_Version: + World.StoreOneDimension(dimensionName, dimensionType!); + World.SetDimension(dimensionName); + break; + default: + World.SetDimension(dimensionTypeName!); + break; + } + } + + break; + } } } @@ -713,8 +967,11 @@ namespace MinecraftClient.Protocol.Handlers else { dataTypes.ReadNextBool(packetData); // Do limited crafting - var dimensionTypeName = - dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + + var dimensionTypeName = protocolVersion < MC_1_20_6_Version + ? dataTypes.ReadNextString(packetData) + : World.GetDimensionNameById(dataTypes.ReadNextVarInt(packetData)); + dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above if (handler.GetTerrainEnabled()) @@ -733,14 +990,21 @@ namespace MinecraftClient.Protocol.Handlers } dataTypes.ReadNextVarInt(packetData); // Portal Cooldown + + if (protocolVersion >= MC_1_20_6_Version) + dataTypes.ReadNextBool(packetData); // Enforoces Secure Chat } + + if (protocolVersion >= MC_1_21_4_Version) + SendPacket(PacketTypesOut.PlayerLoaded, new List()); + break; case PacketTypesIn.SpawnPainting: // Just skip, no need for this return true; case PacketTypesIn.DeclareCommands: if (protocolVersion >= MC_1_19_Version) { - log.Debug("Receive DeclareCommands"); + log.PacketDebug("Receive DeclareCommands"); DeclareCommands.Read(dataTypes, packetData, protocolVersion); receiveDeclareCommands = true; if (receivePlayerInfo) @@ -803,7 +1067,7 @@ namespace MinecraftClient.Protocol.Handlers else { var player = handler.GetPlayerInfo(senderUuid); - verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, + verifyResult = player is not null && player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature); } @@ -853,25 +1117,25 @@ namespace MinecraftClient.Protocol.Handlers ? dataTypes.ReadNextString(packetData) : null; - var chatInfo = Json.ParseJson(chatName).Properties; - var senderDisplayName = chatInfo != null && chatInfo.Count > 0 + var chatInfo = Json.ParseJson(chatName)?.AsObject(); + var senderDisplayName = chatInfo is not null && chatInfo.Count > 0 ? (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]) - .StringValue + .GetStringValue() : ""; string? senderTeamName = null; var messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && + if (targetName is not null && (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] - .Properties["text"].StringValue; + senderTeamName = Json.ParseJson(targetName)!["with"]![0]! + ["text"]!.GetStringValue(); if (string.IsNullOrWhiteSpace(senderDisplayName)) { var player = handler.GetPlayerInfo(senderUuid); - if (player != null && (player.DisplayName != null || player is { Name: not null }) && + if (player is not null && (player.DisplayName is not null || player is { Name: not null }) && string.IsNullOrWhiteSpace(senderDisplayName)) { senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); @@ -890,7 +1154,7 @@ namespace MinecraftClient.Protocol.Handlers else { var player = handler.GetPlayerInfo(senderUuid); - if (player == null || !player.IsMessageChainLegal()) + if (player is null || !player.IsMessageChainLegal()) verifyResult = false; else { @@ -914,6 +1178,11 @@ namespace MinecraftClient.Protocol.Handlers // 1.19.3+ // Header section // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write + + // 1.21.5+: globalIndex prepended before sender UUID + if (protocolVersion >= MC_1_21_5_Version) + dataTypes.ReadNextVarInt(packetData); + var senderUuid = dataTypes.ReadNextUUID(packetData); var index = dataTypes.ReadNextVarInt(packetData); // Signature is fixed size of 256 bytes @@ -972,7 +1241,7 @@ namespace MinecraftClient.Protocol.Handlers if (string.IsNullOrWhiteSpace(senderDisplayName)) { var player = handler.GetPlayerInfo(senderUuid); - if (player != null && (player.DisplayName != null || player.Name != null) && + if (player is not null && (player.DisplayName is not null || player.Name is not null) && string.IsNullOrWhiteSpace(senderDisplayName)) { senderDisplayName = player.DisplayName ?? player.Name; @@ -984,7 +1253,7 @@ namespace MinecraftClient.Protocol.Handlers } bool verifyResult; - if (!isOnlineMode || messageSignature == null) + if (!isOnlineMode || messageSignature is null) verifyResult = false; else { @@ -993,7 +1262,7 @@ namespace MinecraftClient.Protocol.Handlers else { var player = handler.GetPlayerInfo(senderUuid); - if (player == null || !player.IsMessageChainLegal()) + if (player is null || !player.IsMessageChainLegal()) verifyResult = false; else { @@ -1031,7 +1300,7 @@ namespace MinecraftClient.Protocol.Handlers chunkBatchStartTime = GetNanos(); break; case PacketTypesIn.StartConfiguration: - currentState = CurrentState.Configuration; + SetCurrentState(CurrentState.Configuration); SendAcknowledgeConfiguration(); break; case PacketTypesIn.HideMessage: @@ -1125,7 +1394,7 @@ namespace MinecraftClient.Protocol.Handlers }; } - // TODO: Write a function to use this data ? But seems not too useful + // Maybe write a function to use this data ? But seems not too useful } break; @@ -1150,7 +1419,7 @@ namespace MinecraftClient.Protocol.Handlers { var player = handler.GetPlayerInfo(senderUuid); - if (player == null || !player.IsMessageChainLegal()) + if (player is null || !player.IsMessageChainLegal()) verifyResult = false; else { @@ -1168,10 +1437,14 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.Respawn: string? dimensionTypeNameRespawn = null; Dictionary? dimensionTypeRespawn = null; + if (protocolVersion >= MC_1_16_Version) { switch (protocolVersion) { + case >= MC_1_20_6_Version: + dimensionTypeNameRespawn = World.GetDimensionNameById(dataTypes.ReadNextVarInt(packetData)); + break; case >= MC_1_19_Version: dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier @@ -1181,7 +1454,7 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound break; default: - dataTypes.ReadNextString(packetData); + dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); break; } @@ -1196,27 +1469,27 @@ namespace MinecraftClient.Protocol.Handlers switch (protocolVersion) { case >= MC_1_16_Version: - { - var dimensionName = - dataTypes.ReadNextString( - packetData); // Dimension Name (World Name) - 1.16 and above - - if (handler.GetTerrainEnabled()) { - switch (protocolVersion) - { - case >= MC_1_16_2_Version and <= MC_1_18_2_Version: - World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); - World.SetDimension(dimensionName); - break; - case >= MC_1_19_Version: - World.SetDimension(dimensionTypeNameRespawn!); - break; - } - } + var dimensionName = + dataTypes.ReadNextString( + packetData); // Dimension Name (World Name) - 1.16 and above - break; - } + if (handler.GetTerrainEnabled()) + { + switch (protocolVersion) + { + case >= MC_1_16_2_Version and <= MC_1_18_2_Version: + World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); + World.SetDimension(dimensionName); + break; + default: + World.SetDimension(dimensionTypeNameRespawn!); + break; + } + } + + break; + } case < MC_1_14_Version: dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below break; @@ -1263,68 +1536,77 @@ namespace MinecraftClient.Protocol.Handlers handler.OnRespawn(); break; case PacketTypesIn.PlayerPositionAndLook: - { - // These always need to be read, since we need the field after them for teleport confirm - var location = new Location( - dataTypes.ReadNextDouble(packetData), // X - dataTypes.ReadNextDouble(packetData), // Y - dataTypes.ReadNextDouble(packetData) // Z - ); - - var yaw = dataTypes.ReadNextFloat(packetData); - var pitch = dataTypes.ReadNextFloat(packetData); - var locMask = dataTypes.ReadNextByte(packetData); - - // entity handling require player pos for distance calculating - if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) { - if (protocolVersion >= MC_1_8_Version) - { - var currentLocation = handler.GetCurrentLocation(); - location.X = (locMask & 1 << 0) != 0 ? currentLocation.X + location.X : location.X; - location.Y = (locMask & 1 << 1) != 0 ? currentLocation.Y + location.Y : location.Y; - location.Z = (locMask & 1 << 2) != 0 ? currentLocation.Z + location.Z : location.Z; - } - } + int teleportId; + Location location; + float yaw, pitch; + int locMask; - if (protocolVersion >= MC_1_9_Version) - { - var teleportId = dataTypes.ReadNextVarInt(packetData); - - if (teleportId < 0) + if (protocolVersion >= MC_1_21_2_Version) { - yaw = LastYaw; - pitch = LastPitch; + teleportId = dataTypes.ReadNextVarInt(packetData); + location = new Location( + dataTypes.ReadNextDouble(packetData), // X + dataTypes.ReadNextDouble(packetData), // Y + dataTypes.ReadNextDouble(packetData) // Z + ); + dataTypes.ReadNextDouble(packetData); // Delta X + dataTypes.ReadNextDouble(packetData); // Delta Y + dataTypes.ReadNextDouble(packetData); // Delta Z + yaw = dataTypes.ReadNextFloat(packetData); + pitch = dataTypes.ReadNextFloat(packetData); + locMask = dataTypes.ReadNextInt(packetData); // Int flags (was Byte before 1.21.2) } else { + location = new Location( + dataTypes.ReadNextDouble(packetData), // X + dataTypes.ReadNextDouble(packetData), // Y + dataTypes.ReadNextDouble(packetData) // Z + ); + yaw = dataTypes.ReadNextFloat(packetData); + pitch = dataTypes.ReadNextFloat(packetData); + locMask = dataTypes.ReadNextByte(packetData); + teleportId = protocolVersion >= MC_1_9_Version + ? dataTypes.ReadNextVarInt(packetData) : -1; + } + + if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) + { + if (protocolVersion >= MC_1_8_Version) + { + var currentLocation = handler.GetCurrentLocation(); + location.X = (locMask & 1 << 0) != 0 ? currentLocation.X + location.X : location.X; + location.Y = (locMask & 1 << 1) != 0 ? currentLocation.Y + location.Y : location.Y; + location.Z = (locMask & 1 << 2) != 0 ? currentLocation.Z + location.Z : location.Z; + } + } + + if (teleportId >= 0) + { + LastYaw = yaw; + LastPitch = pitch; + handler.UpdateLocation(location, yaw, pitch); + SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportId)); + + if (Config.Main.Advanced.TemporaryFixBadpacket) + { + SendLocationUpdate(location, true, false, yaw, pitch, true); + + if (teleportId == 1) + SendLocationUpdate(location, true, false, yaw, pitch, true); + } + } + else + { + handler.UpdateLocation(location, yaw, pitch); LastYaw = yaw; LastPitch = pitch; } - handler.UpdateLocation(location, yaw, pitch); - - // Teleport confirm packet - SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportId)); - - if (Config.Main.Advanced.TemporaryFixBadpacket) - { - SendLocationUpdate(location, true, yaw, pitch, true); - - if (teleportId == 1) - SendLocationUpdate(location, true, yaw, pitch, true); - } + if (protocolVersion is >= MC_1_17_Version and < MC_1_19_4_Version) + dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 to 1.19.3 } - else - { - handler.UpdateLocation(location, yaw, pitch); - LastYaw = yaw; - LastPitch = pitch; - } - - if (protocolVersion is >= MC_1_17_Version and < MC_1_19_4_Version) - dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 to 1.19.3 - } break; case PacketTypesIn.ChunkData: if (handler.GetTerrainEnabled()) @@ -1343,7 +1625,22 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextULongArray( packetData); // Bit Mask Length and Primary Bit Mask - dataTypes.ReadNextNbt(packetData); // Heightmaps + if (protocolVersion >= MC_1_21_5_Version) + { + // 1.21.5: Heightmaps encoded as map instead of NBT + var hmCount = dataTypes.ReadNextVarInt(packetData); + for (var hm = 0; hm < hmCount; hm++) + { + dataTypes.ReadNextVarInt(packetData); // Heightmap type id + var longCount = dataTypes.ReadNextVarInt(packetData); + for (var l = 0; l < longCount; l++) + dataTypes.ReadNextLong(packetData); + } + } + else + { + dataTypes.ReadNextNbt(packetData); // Heightmaps (NBT format) + } if (protocolVersion is MC_1_17_Version or MC_1_17_1_Version) { @@ -1355,6 +1652,7 @@ namespace MinecraftClient.Protocol.Handlers var dataSize = dataTypes.ReadNextVarInt(packetData); // Size pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); + ProcessChunkBlockEntityData(chunkX, chunkZ, packetData); Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); // Block Entity data: ignored @@ -1465,26 +1763,26 @@ namespace MinecraftClient.Protocol.Handlers { // 1.8 - 1.13 case < MC_1_13_2_Version: - { - var directionAndType = dataTypes.ReadNextByte(packetData); - byte direction, type; - - // 1.12.2+ - if (protocolVersion >= MC_1_12_2_Version) { - direction = (byte)(directionAndType & 0xF); - type = (byte)(directionAndType >> 4 & 0xF); - } - else // 1.8 - 1.12 - { - direction = (byte)(directionAndType >> 4 & 0xF); - type = (byte)(directionAndType & 0xF); - } + var directionAndType = dataTypes.ReadNextByte(packetData); + byte direction, type; - mapIcon.Type = (MapIconType)type; - mapIcon.Direction = direction; - break; - } + // 1.12.2+ + if (protocolVersion >= MC_1_12_2_Version) + { + direction = (byte)(directionAndType & 0xF); + type = (byte)(directionAndType >> 4 & 0xF); + } + else // 1.8 - 1.12 + { + direction = (byte)(directionAndType >> 4 & 0xF); + type = (byte)(directionAndType & 0xF); + } + + mapIcon.Type = (MapIconType)type; + mapIcon.Direction = direction; + break; + } // 1.13.2+ case >= MC_1_13_2_Version: mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); @@ -1803,7 +2101,7 @@ namespace MinecraftClient.Protocol.Handlers // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. // Since chunks that have not been loaded are not recorded, this may result // in loading chunks that should be unloaded and inaccurate statistics. - if (handler.GetWorld()[chunkX, chunkZ] != null) + if (handler.GetWorld()[chunkX, chunkZ] is not null) Interlocked.Decrement(ref handler.GetWorld().chunkCnt); handler.GetWorld()[chunkX, chunkZ] = null; @@ -1811,20 +2109,17 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.ChangeGameState: - if (protocolVersion >= MC_1_15_2_Version) - { - var reason = dataTypes.ReadNextByte(packetData); - var state = dataTypes.ReadNextFloat(packetData); - handler.OnGameEvent(reason, state); - } + var reason = dataTypes.ReadNextByte(packetData); + var state = dataTypes.ReadNextFloat(packetData); + handler.OnGameEvent(reason, state); break; case PacketTypesIn.PlayerInfo: if (protocolVersion >= MC_1_19_3_Version) { var actionBitset = dataTypes.ReadNextByte(packetData); - var numberOfActions = dataTypes.ReadNextVarInt(packetData); - for (var i = 0; i < numberOfActions; i++) + var entryCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < entryCount; i++) { var playerUuid = dataTypes.ReadNextUUID(packetData); @@ -1847,7 +2142,7 @@ namespace MinecraftClient.Protocol.Handlers else { var playerGet = handler.GetPlayerInfo(playerUuid); - if (playerGet == null) + if (playerGet is null) { player = new(string.Empty, playerUuid); handler.OnPlayerJoin(player); @@ -1873,7 +2168,7 @@ namespace MinecraftClient.Protocol.Handlers if (playerUuid == handler.GetUserUuid()) { - log.Debug($"Receive ChatUuid = {chatUuid}"); + log.PacketDebug($"Receive ChatUuid = {chatUuid}"); this.chatUuid = chatUuid; } } @@ -1882,7 +2177,7 @@ namespace MinecraftClient.Protocol.Handlers player.ClearPublicKey(); if (playerUuid == handler.GetUserUuid()) - log.Debug("Receive ChatUuid = Empty"); + log.PacketDebug("Receive ChatUuid = Empty"); } if (playerUuid == handler.GetUserUuid()) @@ -1906,10 +2201,19 @@ namespace MinecraftClient.Protocol.Handlers } // Actions bit 5: update display name - if ((actionBitset & 1 << 5) <= 0) continue; - player.DisplayName = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextChat(packetData) - : null; + if ((actionBitset & 1 << 5) > 0) + { + player.DisplayName = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextChat(packetData) + : null; + } + + // Consume all action-selected fields to keep entry boundaries aligned. + if (protocolVersion >= MC_1_21_2_Version && (actionBitset & 1 << 6) > 0) // Actions bit 6: update list order + player.TabListOrder = dataTypes.ReadNextVarInt(packetData); + + if (protocolVersion >= MC_1_21_4_Version && (actionBitset & 1 << 7) > 0) // Actions bit 7: update hat + dataTypes.ReadNextBool(packetData); } } else if (protocolVersion >= MC_1_8_Version) @@ -1958,7 +2262,7 @@ namespace MinecraftClient.Protocol.Handlers string? displayName = null; if (dataTypes.ReadNextBool(packetData)) // Has display name - displayName = dataTypes.ReadNextString(packetData); // Display name + displayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); // Display name // 1.19 Additions long? keyExpiration = null; @@ -1998,8 +2302,8 @@ namespace MinecraftClient.Protocol.Handlers if (dataTypes.ReadNextBool(packetData)) { var player = handler.GetPlayerInfo(uuid); - if (player != null) - player.DisplayName = dataTypes.ReadNextString(packetData); + if (player is not null) + player.DisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); else dataTypes.SkipNextString(packetData); } @@ -2036,6 +2340,11 @@ namespace MinecraftClient.Protocol.Handlers } break; + case PacketTypesIn.PlayerListHeaderAndFooter: + handler.OnTabListHeaderAndFooter( + dataTypes.ReadNextChat(packetData), + dataTypes.ReadNextChat(packetData)); + break; case PacketTypesIn.TabComplete: var oldTransactionId = autocomplete_transaction_id; if (protocolVersion >= MC_1_13_Version) @@ -2064,10 +2373,15 @@ namespace MinecraftClient.Protocol.Handlers // Length is unneeded as the whole remaining packetData is the entire payload of the packet. if (protocolVersion < MC_1_8_Version) pForge.ReadNextVarShort(packetData); + if (IsOpenBookPluginChannel(channel)) + handler.OnBookOpen(ReadBookHand(new Queue(packetData))); handler.OnPluginChannelMessage(channel, packetData.ToArray()); return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); + case PacketTypesIn.OpenBook: + handler.OnBookOpen(ReadBookHand(packetData)); + break; case PacketTypesIn.Disconnect: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, dataTypes.ReadNextChat(packetData)); return false; case PacketTypesIn.SetCompression: @@ -2096,7 +2410,7 @@ namespace MinecraftClient.Protocol.Handlers var windowId = dataTypes.ReadNextVarInt(packetData); var windowType = dataTypes.ReadNextVarInt(packetData); var title = dataTypes.ReadNextChat(packetData); - Container inventory = new(windowId, windowType, ChatParser.ParseText(title)); + Container inventory = new(windowId, windowType, ChatParser.ParseText(title), protocolVersion); handler.OnInventoryOpen(windowId, inventory); } } @@ -2105,7 +2419,9 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.CloseWindow: if (handler.GetInventoryEnabled()) { - var windowId = dataTypes.ReadNextByte(packetData); + var windowId = protocolVersion >= MC_1_21_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); lock (window_actions) { window_actions[windowId] = 0; @@ -2118,9 +2434,11 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.WindowItems: if (handler.GetInventoryEnabled()) { - var windowId = dataTypes.ReadNextByte(packetData); + var windowId = (byte)(protocolVersion >= MC_1_21_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData)); var stateId = -1; - var elements = 0; + int elements; if (protocolVersion >= MC_1_17_1_Version) { @@ -2131,14 +2449,14 @@ namespace MinecraftClient.Protocol.Handlers else { // Elements as Short - 1.17.0 and below - dataTypes.ReadNextShort(packetData); + elements = dataTypes.ReadNextShort(packetData); } Dictionary inventorySlots = new(); for (var slotId = 0; slotId < elements; slotId++) { var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - if (item != null) + if (item is not null) inventorySlots[slotId] = item; } @@ -2150,7 +2468,9 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.WindowProperty: - var containerId = dataTypes.ReadNextByte(packetData); + var containerId = (byte)(protocolVersion >= MC_1_21_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData)); var propertyId = dataTypes.ReadNextShort(packetData); var propertyValue = dataTypes.ReadNextShort(packetData); handler.OnWindowProperties(containerId, propertyId, propertyValue); @@ -2158,7 +2478,9 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.SetSlot: if (handler.GetInventoryEnabled()) { - var windowId = dataTypes.ReadNextByte(packetData); + var windowId = (byte)(protocolVersion >= MC_1_21_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData)); var stateId = -1; if (protocolVersion >= MC_1_17_1_Version) stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above @@ -2181,7 +2503,9 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.RemoveResourcePack: if (dataTypes.ReadNextBool(packetData)) // Has UUID - dataTypes.ReadNextUUID(packetData); // UUID + ChatParser.RemoveResourcePackTranslations(dataTypes.ReadNextUUID(packetData).ToString("D")); // UUID + else + ChatParser.ClearResourcePackTranslations(); break; case PacketTypesIn.ResourcePackSend: HandleResourcePackPacket(packetData); @@ -2196,14 +2520,17 @@ namespace MinecraftClient.Protocol.Handlers if (handler.GetEntityHandlingEnabled()) { var entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); - + if (protocolVersion >= MC_1_20_2_Version) { if (entity.Type == EntityType.Player) handler.OnSpawnPlayer(entity.ID, entity.UUID, entity.Location, (byte)entity.Yaw, (byte)entity.Pitch); + else + handler.OnSpawnEntity(entity); + break; } - + handler.OnSpawnEntity(entity); } @@ -2220,7 +2547,7 @@ namespace MinecraftClient.Protocol.Handlers var bitsData = dataTypes.ReadNextByte(packetData); // Top bit set if another entry follows, and otherwise unset if this is the last item in the array hasNext = bitsData >> 7 == 1; - var slot2 = bitsData >> 1; + var slot2 = bitsData & 0x7F; var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); handler.OnEntityEquipment(entityId, slot2, item); } while (hasNext); @@ -2278,19 +2605,22 @@ namespace MinecraftClient.Protocol.Handlers if (handler.GetEntityHandlingEnabled()) { var entityId = dataTypes.ReadNextVarInt(packetData); - var effectId = protocolVersion >= MC_1_18_2_Version - ? dataTypes.ReadNextVarInt(packetData) + var effectId = protocolVersion >= MC_1_20_4_Version + ? dataTypes.ReadNextVarInt(packetData) + 1 : dataTypes.ReadNextByte(packetData); - if (Enum.TryParse(effectId.ToString(), out Effects effect)) + if (Enum.IsDefined(typeof(Effects), effectId)) { - var amplifier = dataTypes.ReadNextByte(packetData); + var effect = (Effects)effectId; + var amplifier = protocolVersion >= MC_1_20_6_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); var duration = dataTypes.ReadNextVarInt(packetData); var flags = dataTypes.ReadNextByte(packetData); var hasFactorData = false; Dictionary? factorCodec = null; - if (protocolVersion >= MC_1_19_Version) + if (protocolVersion >= MC_1_19_Version && protocolVersion < MC_1_20_6_Version) { hasFactorData = dataTypes.ReadNextBool(packetData); if (hasFactorData) @@ -2302,6 +2632,22 @@ namespace MinecraftClient.Protocol.Handlers } } + break; + case PacketTypesIn.RemoveEntityEffect: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var effectId = protocolVersion >= MC_1_20_4_Version + ? dataTypes.ReadNextVarInt(packetData) + 1 + : dataTypes.ReadNextByte(packetData); + + if (Enum.IsDefined(typeof(Effects), effectId)) + { + var effect = (Effects)effectId; + handler.OnRemoveEntityEffect(entityId, effect); + } + } + break; case PacketTypesIn.DestroyEntities: if (handler.GetEntityHandlingEnabled()) @@ -2389,6 +2735,27 @@ namespace MinecraftClient.Protocol.Handlers handler.OnEntityRotation(entityId, yaw, pitch, isOnGround); } + break; + case PacketTypesIn.EntityVelocity: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + double velocityX, velocityY, velocityZ; + + if (protocolVersion >= MC_1_21_9_Version) + { + (velocityX, velocityY, velocityZ) = dataTypes.ReadNextLpVec3Values(packetData); + } + else + { + velocityX = dataTypes.ReadNextShort(packetData) / 8000.0D; + velocityY = dataTypes.ReadNextShort(packetData) / 8000.0D; + velocityZ = dataTypes.ReadNextShort(packetData) / 8000.0D; + } + + handler.OnEntityVelocity(entityId, velocityX, velocityY, velocityZ); + } + break; case PacketTypesIn.EntityProperties: if (handler.GetEntityHandlingEnabled()) @@ -2401,7 +2768,16 @@ namespace MinecraftClient.Protocol.Handlers Dictionary keys = new(); for (var i = 0; i < numberOfProperties; i++) { - var propertyKey = dataTypes.ReadNextString(packetData); + string propertyKey; + if (protocolVersion < MC_1_20_6_Version) + { + propertyKey = dataTypes.ReadNextString(packetData); + } + else + { + var attrId = dataTypes.ReadNextVarInt(packetData); + propertyKey = World.GetAttributeNameById(attrId) ?? "unknown"; + } var propertyValue2 = dataTypes.ReadNextDouble(packetData); List op0 = new(); @@ -2411,7 +2787,7 @@ namespace MinecraftClient.Protocol.Handlers var numberOfModifiers = dataTypes.ReadNextVarInt(packetData); for (var j = 0; j < numberOfModifiers; j++) { - dataTypes.ReadNextUUID(packetData); + var modifierId = protocolVersion < MC_1_21_Version ? dataTypes.ReadNextUUID(packetData).ToString() : dataTypes.ReadNextString(packetData); var amount = dataTypes.ReadNextDouble(packetData); var operation = dataTypes.ReadNextByte(packetData); switch (operation) @@ -2431,7 +2807,7 @@ namespace MinecraftClient.Protocol.Handlers if (op0.Count > 0) propertyValue2 += op0.Sum(); if (op1.Count > 0) propertyValue2 *= 1 + op1.Sum(); if (op2.Count > 0) propertyValue2 *= op2.Aggregate((a, _x) => a * _x); - keys.Add(propertyKey, propertyValue2); + keys[propertyKey] = propertyValue2; } handler.OnEntityProperties(entityId, keys); @@ -2447,7 +2823,7 @@ namespace MinecraftClient.Protocol.Handlers // Also make a palette for field? Will be a lot of work var healthField = protocolVersion switch { - > MC_1_20_4_Version => throw new NotImplementedException(Translations + > MC_26_1_Version => throw new NotImplementedException(Translations .exception_palette_healthfield), // 1.17 and above >= MC_1_17_Version => 9, @@ -2477,9 +2853,30 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.TimeUpdate: - var worldAge = dataTypes.ReadNextLong(packetData); - var timeOfDay = dataTypes.ReadNextLong(packetData); - handler.OnTimeUpdate(worldAge, timeOfDay); + if (protocolVersion >= MC_26_1_Version) + { + var worldAge = dataTypes.ReadNextLong(packetData); + long timeOfDay = 0; + var clockCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < clockCount; i++) + { + dataTypes.ReadNextVarInt(packetData); // clock holder id + var totalTicks = dataTypes.ReadNextVarLong(packetData); + dataTypes.ReadNextFloat(packetData); // partialTick + dataTypes.ReadNextFloat(packetData); // rate + if (i == 0) + timeOfDay = totalTicks; + } + handler.OnTimeUpdate(worldAge, timeOfDay); + } + else + { + var worldAge = dataTypes.ReadNextLong(packetData); + var timeOfDay = dataTypes.ReadNextLong(packetData); + if (protocolVersion >= MC_1_21_2_Version) + dataTypes.ReadNextBool(packetData); // Tick day time + handler.OnTimeUpdate(worldAge, timeOfDay); + } break; case PacketTypesIn.EntityTeleport: if (handler.GetEntityHandlingEnabled()) @@ -2487,23 +2884,41 @@ namespace MinecraftClient.Protocol.Handlers var entityId = dataTypes.ReadNextVarInt(packetData); double x, y, z; - if (protocolVersion < MC_1_9_Version) + if (protocolVersion >= MC_1_21_2_Version) + { + // 1.21.2+: PositionMoveRotation + relative flags + x = dataTypes.ReadNextDouble(packetData); + y = dataTypes.ReadNextDouble(packetData); + z = dataTypes.ReadNextDouble(packetData); + dataTypes.ReadNextDouble(packetData); // Delta movement X + dataTypes.ReadNextDouble(packetData); // Delta movement Y + dataTypes.ReadNextDouble(packetData); // Delta movement Z + dataTypes.ReadNextFloat(packetData); // Yaw + dataTypes.ReadNextFloat(packetData); // Pitch + dataTypes.ReadNextInt(packetData); // Relative flags bitmask + var isOnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(entityId, x, y, z, isOnGround); + } + else if (protocolVersion < MC_1_9_Version) { x = dataTypes.ReadNextInt(packetData) / 32.0D; y = dataTypes.ReadNextInt(packetData) / 32.0D; z = dataTypes.ReadNextInt(packetData) / 32.0D; + dataTypes.ReadNextByte(packetData); // Yaw + dataTypes.ReadNextByte(packetData); // Pitch + var isOnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(entityId, x, y, z, isOnGround); } else { x = dataTypes.ReadNextDouble(packetData); y = dataTypes.ReadNextDouble(packetData); z = dataTypes.ReadNextDouble(packetData); + dataTypes.ReadNextByte(packetData); // Yaw + dataTypes.ReadNextByte(packetData); // Pitch + var isOnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(entityId, x, y, z, isOnGround); } - - var entityYaw = dataTypes.ReadNextByte(packetData); - var entityPitch = dataTypes.ReadNextByte(packetData); - var isOnGround = dataTypes.ReadNextBool(packetData); - handler.OnEntityTeleport(entityId, x, y, z, isOnGround); } break; @@ -2523,44 +2938,169 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.Explosion: Location explosionLocation; - if (protocolVersion >= MC_1_19_3_Version) + float explosionStrength; + int explosionBlockCount; + + if (protocolVersion >= MC_1_21_2_Version) + { explosionLocation = new(dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); + + if (protocolVersion >= MC_1_21_9_Version) + { + // 1.21.9+: added radius (float), blockCount (int), and blockParticles (WeightedList) + explosionStrength = dataTypes.ReadNextFloat(packetData); // Radius + explosionBlockCount = dataTypes.ReadNextInt(packetData); // Block count + } + else + { + // 1.21.2–1.21.8: no strength/block-count fields + explosionStrength = 0; + explosionBlockCount = 0; + } + + if (dataTypes.ReadNextBool(packetData)) // Has player knockback + { + dataTypes.ReadNextDouble(packetData); // Knockback X + dataTypes.ReadNextDouble(packetData); // Knockback Y + dataTypes.ReadNextDouble(packetData); // Knockback Z + } + + dataTypes.ReadParticleData(packetData, itemPalette); // Explosion particle + + var soundHolderId = dataTypes.ReadNextVarInt(packetData); + if (soundHolderId == 0) + { + dataTypes.ReadNextString(packetData); // Sound ResourceLocation + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextFloat(packetData); // Fixed range + } + + if (protocolVersion >= MC_1_21_9_Version) + { + // 1.21.9+: WeightedList blockParticles + // Each entry: particle + float scaling + float speed + VarInt weight + var blockParticleCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < blockParticleCount; i++) + { + dataTypes.ReadParticleData(packetData, itemPalette); // Particle type + dataTypes.ReadNextFloat(packetData); // Scaling + dataTypes.ReadNextFloat(packetData); // Speed + dataTypes.ReadNextVarInt(packetData); // Weight + } + } + } else - explosionLocation = new(dataTypes.ReadNextFloat(packetData), - dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); - - var explosionStrength = dataTypes.ReadNextFloat(packetData); - var explosionBlockCount = protocolVersion >= MC_1_17_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextInt(packetData); // Record count - - // Records - for (var i = 0; i < explosionBlockCount; i++) - dataTypes.ReadData(3, packetData); - - // Maybe use in the future when the physics are implemented - dataTypes.ReadNextFloat(packetData); // Player Motion X - dataTypes.ReadNextFloat(packetData); // Player Motion Y - dataTypes.ReadNextFloat(packetData); // Player Motion Z - - if (protocolVersion >= MC_1_20_4_Version) { - dataTypes.ReadNextVarInt(packetData); // Block Interaction - dataTypes.ReadParticleData(packetData, itemPalette); // Small Explosion Particles - dataTypes.ReadParticleData(packetData, itemPalette); // Large Explosion Particles + if (protocolVersion >= MC_1_19_3_Version) + explosionLocation = new(dataTypes.ReadNextDouble(packetData), + dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); + else + explosionLocation = new(dataTypes.ReadNextFloat(packetData), + dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); - // Explosion Sound - dataTypes.ReadNextString(packetData); // Sound Name - var hasFixedRange = dataTypes.ReadNextBool(packetData); - if (hasFixedRange) - dataTypes.ReadNextFloat(packetData); // Range + explosionStrength = dataTypes.ReadNextFloat(packetData); + explosionBlockCount = protocolVersion >= MC_1_17_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextInt(packetData); + + for (var i = 0; i < explosionBlockCount; i++) + dataTypes.ReadNextByteArray(packetData, 3); + + dataTypes.ReadNextFloat(packetData); // Player Motion X + dataTypes.ReadNextFloat(packetData); // Player Motion Y + dataTypes.ReadNextFloat(packetData); // Player Motion Z + + if (protocolVersion >= MC_1_20_4_Version) + { + dataTypes.ReadNextVarInt(packetData); // Block Interaction + dataTypes.ReadParticleData(packetData, itemPalette); // Small Explosion Particles + dataTypes.ReadParticleData(packetData, itemPalette); // Large Explosion Particles + + var soundHolderId = dataTypes.ReadNextVarInt(packetData); + if (soundHolderId == 0) + { + dataTypes.ReadNextString(packetData); // Sound ResourceLocation + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextFloat(packetData); // Fixed range + } + } } handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); break; + case PacketTypesIn.NamedSoundEffect: + { + string? soundName = dataTypes.ReadNextString(packetData); + int category = dataTypes.ReadNextVarInt(packetData); + double x = dataTypes.ReadNextInt(packetData) / 8.0D; + double y = dataTypes.ReadNextInt(packetData) / 8.0D; + double z = dataTypes.ReadNextInt(packetData) / 8.0D; + float volume = dataTypes.ReadNextFloat(packetData); + float pitch = protocolVersion < MC_1_10_Version + ? dataTypes.ReadNextByte(packetData) / 63.0f + : dataTypes.ReadNextFloat(packetData); + + handler.OnSoundEffect(soundName, new Location(x, y, z), category, volume, pitch, null); + break; + } + case PacketTypesIn.SoundEffect: + { + string? soundName; + if (protocolVersion >= MC_1_19_Version) + soundName = ReadSoundEventHolderName(packetData); + else + { + dataTypes.ReadNextVarInt(packetData); // Sound id + soundName = null; + } + + if (protocolVersion < MC_1_19_Version && packetData.Count < 21) + break; + + int category = dataTypes.ReadNextVarInt(packetData); + double x = dataTypes.ReadNextInt(packetData) / 8.0D; + double y = dataTypes.ReadNextInt(packetData) / 8.0D; + double z = dataTypes.ReadNextInt(packetData) / 8.0D; + float volume = dataTypes.ReadNextFloat(packetData); + float pitch = protocolVersion < MC_1_10_Version + ? dataTypes.ReadNextByte(packetData) / 63.0f + : dataTypes.ReadNextFloat(packetData); + + if (protocolVersion >= MC_1_19_Version) + dataTypes.ReadNextLong(packetData); // Seed + + handler.OnSoundEffect(soundName, new Location(x, y, z), category, volume, pitch, null); + break; + } + case PacketTypesIn.EntitySoundEffect: + { + string? soundName; + if (protocolVersion >= MC_1_19_Version) + soundName = ReadSoundEventHolderName(packetData); + else + { + dataTypes.ReadNextVarInt(packetData); // Sound id + soundName = null; + } + + int category = dataTypes.ReadNextVarInt(packetData); + int entityId = dataTypes.ReadNextVarInt(packetData); + float volume = dataTypes.ReadNextFloat(packetData); + float pitch = dataTypes.ReadNextFloat(packetData); + + if (protocolVersion >= MC_1_19_Version) + dataTypes.ReadNextLong(packetData); // Seed + + handler.OnSoundEffect(soundName, null, category, volume, pitch, entityId); + break; + } case PacketTypesIn.HeldItemChange: - handler.OnHeldItemChange(dataTypes.ReadNextByte(packetData)); // Slot + case PacketTypesIn.SetHeldSlot: + var heldSlot = protocolVersion >= MC_1_21_4_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); + handler.OnHeldItemChange((byte)heldSlot); break; case PacketTypesIn.ScoreboardObjective: var objectiveName = dataTypes.ReadNextString(packetData); @@ -2578,7 +3118,18 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_20_4_Version) { if (dataTypes.ReadNextBool(packetData)) // Has Number Format + { numberFormat = dataTypes.ReadNextVarInt(packetData); // Number Format + switch (numberFormat) + { + case 1: // styled + dataTypes.ReadNextNbt(packetData); // Styling compound tag + break; + case 2: // fixed + dataTypes.ReadNextChat(packetData); // Content text component + break; + } + } } } @@ -2599,11 +3150,21 @@ namespace MinecraftClient.Protocol.Handlers objectiveValue2 = dataTypes.ReadNextVarInt(packetData); // Value if (dataTypes.ReadNextBool(packetData)) // Has Display Name - objectiveDisplayName3 = - ChatParser.ParseText(dataTypes.ReadNextString(packetData)); // Has Display Name + objectiveDisplayName3 = dataTypes.ReadNextChat(packetData); if (dataTypes.ReadNextBool(packetData)) // Has Number Format + { numberFormat2 = dataTypes.ReadNextVarInt(packetData); // Number Format + switch (numberFormat2) + { + case 1: // styled + dataTypes.ReadNextNbt(packetData); // Styling compound tag + break; + case 2: // fixed + dataTypes.ReadNextChat(packetData); // Content text component + break; + } + } } else { @@ -2621,6 +3182,110 @@ namespace MinecraftClient.Protocol.Handlers handler.OnUpdateScore(entityName, action3, objectiveName3, objectiveDisplayName3, objectiveValue2, numberFormat2); break; + case PacketTypesIn.Teams: + // Wire format per version: + // All versions: name (string), method (byte) + // 1.8/1.8.9 method 0/2: + // displayName (string), prefix (string), suffix (string), + // options (byte), nameTagVisibility, color (byte) + // 1.9-1.12.2 method 0/2: + // displayName (string), prefix (string), suffix (string), + // options (byte), nameTagVisibility, collisionRule, + // color (byte) + // 1.13-1.21.4 method 0/2: + // displayName (component), options (byte), + // nameTagVisibility, collisionRule, color (VarInt), + // prefix (component), suffix (component) + // 1.21.5+ method 0/2: + // displayName (component), options (byte), + // nameTagVisibility (VarInt), collisionRule (VarInt), + // color (VarInt), prefix (component), suffix (component) + // method 0/3/4: players list (VarInt count + strings) + var teamName = dataTypes.ReadNextString(packetData); + var teamMethod = dataTypes.ReadNextByte(packetData); + + var teamDisplayName = string.Empty; + byte teamFriendlyFlags = 0; + var teamNameTagVisibility = string.Empty; + var teamCollisionRule = string.Empty; + var teamColor = -1; + var teamPrefix = string.Empty; + var teamSuffix = string.Empty; + + if (teamMethod is 0 or 2) + { + if (protocolVersion < MC_1_13_Version) + { + teamDisplayName = dataTypes.ReadNextString(packetData); + teamPrefix = dataTypes.ReadNextString(packetData); + teamSuffix = dataTypes.ReadNextString(packetData); + teamFriendlyFlags = dataTypes.ReadNextByte(packetData); + teamNameTagVisibility = dataTypes.ReadNextString(packetData); + + if (protocolVersion >= MC_1_9_Version) + teamCollisionRule = dataTypes.ReadNextString(packetData); + + teamColor = unchecked((sbyte)dataTypes.ReadNextByte(packetData)); + } + else + { + teamDisplayName = dataTypes.ReadNextChat(packetData); + teamFriendlyFlags = dataTypes.ReadNextByte(packetData); + + // nameTagVisibility + if (protocolVersion >= MC_1_21_5_Version) + { + // STREAM_CODEC: 0=always, 1=never, 2=hideForOtherTeams, 3=hideForOwnTeam + teamNameTagVisibility = dataTypes.ReadNextVarInt(packetData) switch + { + 0 => "always", + 1 => "never", + 2 => "hideForOtherTeams", + 3 => "hideForOwnTeam", + _ => "always" + }; + } + else + { + teamNameTagVisibility = dataTypes.ReadNextString(packetData); + } + + // collisionRule + if (protocolVersion >= MC_1_21_5_Version) + { + // STREAM_CODEC: 0=always, 1=never, 2=pushOtherTeams, 3=pushOwnTeam + teamCollisionRule = dataTypes.ReadNextVarInt(packetData) switch + { + 0 => "always", + 1 => "never", + 2 => "pushOtherTeams", + 3 => "pushOwnTeam", + _ => "always" + }; + } + else + { + teamCollisionRule = dataTypes.ReadNextString(packetData); + } + + teamColor = dataTypes.ReadNextVarInt(packetData); + teamPrefix = dataTypes.ReadNextChat(packetData); + teamSuffix = dataTypes.ReadNextChat(packetData); + } + } + + var teamPlayers = new List(); + if (teamMethod is 0 or 3 or 4) + { + int playerCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < playerCount; i++) + teamPlayers.Add(dataTypes.ReadNextString(packetData)); + } + + handler.OnTeam(teamName, teamMethod, teamDisplayName, teamFriendlyFlags, + teamNameTagVisibility, teamCollisionRule, teamColor, + teamPrefix, teamSuffix, teamPlayers); + break; case PacketTypesIn.BlockChangedAck: handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); break; @@ -2653,23 +3318,164 @@ namespace MinecraftClient.Protocol.Handlers // TODO: Use break; + case PacketTypesIn.BlockEntityData: + if (handler.GetTerrainEnabled() && protocolVersion >= MC_1_17_Version) + { + var location_ = dataTypes.ReadNextLocation(packetData); + dataTypes.ReadNextVarInt(packetData); // Block entity type registry id + var nbt = dataTypes.ReadNextNbt(packetData); + handler.OnBlockEntityData(location_, nbt); + } - // Temporarily disabled until I find a fix - /*case PacketTypesIn.BlockEntityData: - var location_ = dataTypes.ReadNextLocation(packetData); - var type_ = dataTypes.ReadNextInt(packetData); - var nbt = dataTypes.ReadNextNbt(packetData); - var nbtJson = JsonConvert.SerializeObject(nbt["messages"]); - - //log.Info($"BLOCK ENTITY DATA -> {location_.ToString()} [{type_}] -> NBT: {nbtJson}"); - - break;*/ + break; case PacketTypesIn.SetTickingState: dataTypes.ReadNextFloat(packetData); dataTypes.ReadNextBool(packetData); break; + case PacketTypesIn.CookieRequest: + var cookieName = dataTypes.ReadNextString(packetData); + var cookieData = null as byte[]; + McClient.Instance?.GetCookie(cookieName, out cookieData); + SendCookieResponse(cookieName, cookieData); + break; + + case PacketTypesIn.StoreCookie: + var cookieName2 = dataTypes.ReadNextString(packetData); + var cookieData2 = dataTypes.ReadNextByteArray(packetData); + McClient.Instance?.SetCookie(cookieName2, cookieData2); + break; + + case PacketTypesIn.Transfer: + var host = dataTypes.ReadNextString(packetData); + var port = dataTypes.ReadNextVarInt(packetData); + + McClient.Instance?.Transfer(host, port); + break; + + case PacketTypesIn.ProjectilePower: + dataTypes.ReadNextVarInt(packetData); // Entity ID + if (protocolVersion >= MC_1_21_Version) + { + dataTypes.ReadNextDouble(packetData); // Acceleration Power + } + else + { + dataTypes.ReadNextDouble(packetData); // X Power + dataTypes.ReadNextDouble(packetData); // Y Power + dataTypes.ReadNextDouble(packetData); // Z Power + } + break; + + case PacketTypesIn.CustomReportDetails: + var detailsCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < detailsCount; i++) + { + dataTypes.ReadNextString(packetData); // Title + dataTypes.ReadNextString(packetData); // Description + } + break; + + case PacketTypesIn.ServerLinks: + handler.OnServerLinksUpdated(ReadServerLinks(packetData)); + break; + + case PacketTypesIn.ClearDialog: + handler.OnDialogCleared(); + break; + + case PacketTypesIn.ShowDialog: + HandleShowDialog(packetData, DialogPhase.Play); + break; + + // 1.21.2+ new packets + case PacketTypesIn.SetCursorItem: + if (handler.GetInventoryEnabled()) + { + dataTypes.ReadNextItemSlot(packetData, itemPalette); + } + break; + + case PacketTypesIn.SetPlayerInventory: + if (handler.GetInventoryEnabled()) + { + var slotId = dataTypes.ReadNextVarInt(packetData); + var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnSetSlot(0, (short)slotId, item, -1); + } + break; + + case PacketTypesIn.EntityPositionSync: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var x = dataTypes.ReadNextDouble(packetData); + var y = dataTypes.ReadNextDouble(packetData); + var z = dataTypes.ReadNextDouble(packetData); + dataTypes.ReadNextDouble(packetData); // Delta movement X + dataTypes.ReadNextDouble(packetData); // Delta movement Y + dataTypes.ReadNextDouble(packetData); // Delta movement Z + var yaw = dataTypes.ReadNextFloat(packetData); + var pitch = dataTypes.ReadNextFloat(packetData); + var isOnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(entityId, x, y, z, isOnGround); + } + break; + + case PacketTypesIn.PlayerRotation: + dataTypes.ReadNextFloat(packetData); // Yaw + dataTypes.ReadNextFloat(packetData); // Pitch + break; + + case PacketTypesIn.MoveMinecartAlongTrack: + { + dataTypes.ReadNextVarInt(packetData); // Entity ID + var stepCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < stepCount; i++) + { + dataTypes.ReadNextDouble(packetData); // Pos X + dataTypes.ReadNextDouble(packetData); // Pos Y + dataTypes.ReadNextDouble(packetData); // Pos Z + dataTypes.ReadNextDouble(packetData); // Movement X + dataTypes.ReadNextDouble(packetData); // Movement Y + dataTypes.ReadNextDouble(packetData); // Movement Z + dataTypes.ReadNextByte(packetData); // Yaw + dataTypes.ReadNextByte(packetData); // Pitch + dataTypes.ReadNextFloat(packetData); // Weight + } + } + break; + + case PacketTypesIn.UnlockRecipes: + if (protocolVersion >= MC_1_13_Version) + HandleUnlockRecipes(packetData); + break; + + case PacketTypesIn.RecipeBookAdd: + if (protocolVersion >= MC_1_21_2_Version) + HandleRecipeBookAdd(packetData); + break; + case PacketTypesIn.RecipeBookRemove: + if (protocolVersion >= MC_1_21_2_Version) + handler.OnRecipeBookRemove(ReadRecipeBookDisplayIds(packetData)); + break; + case PacketTypesIn.RecipeBookSettings: + break; + + case PacketTypesIn.Statistics: + if (protocolVersion < MC_1_12_Version) + HandleLegacyStatistics(packetData); + break; + + case PacketTypesIn.Advancements: + HandleAdvancements(packetData); + break; + + case PacketTypesIn.SelectAdvancementTab: + HandleSelectAdvancementTab(packetData); + break; + default: return false; //Ignored packet } @@ -2677,6 +3483,527 @@ namespace MinecraftClient.Protocol.Handlers return true; //Packet processed } + /// + /// Read a Holder<SoundEvent> from packet data and return its key when inline. + /// Returns null when the holder is a registry reference. + /// + private string? ReadSoundEventHolderName(Queue packetData) + { + int soundHolderId = dataTypes.ReadNextVarInt(packetData); + if (soundHolderId != 0) + return null; + + string soundName = dataTypes.ReadNextString(packetData); + bool hasFixedRange = dataTypes.ReadNextBool(packetData); + if (hasFixedRange) + dataTypes.ReadNextFloat(packetData); + return soundName; + } + + /// + /// Handle the Statistics packet for pre-1.12 legacy achievements. + /// + private void HandleLegacyStatistics(Queue packetData) + { + int statCount = dataTypes.ReadNextVarInt(packetData); + + for (int i = 0; i < statCount; i++) + { + string statId = dataTypes.ReadNextString(packetData); + int value = dataTypes.ReadNextVarInt(packetData); + + if (statId.StartsWith("achievement.", StringComparison.Ordinal)) + legacyAchievementProgress[statId] = value > 0; + } + + List added = new(LegacyAchievementCatalog.Ids.Count + legacyAchievementProgress.Count); + + foreach (string achievementId in LegacyAchievementCatalog.Ids) + added.Add(CreateLegacyAchievement(achievementId, legacyAchievementProgress.TryGetValue(achievementId, out bool completed) && completed)); + + foreach (var (achievementId, completed) in legacyAchievementProgress) + { + if (!LegacyAchievementCatalog.Contains(achievementId)) + added.Add(CreateLegacyAchievement(achievementId, completed)); + } + + handler.OnAchievementsUpdate(added, [], reset: !legacyAchievementsInitialized); + legacyAchievementsInitialized = true; + } + + /// + /// Handle the Advancements packet (1.12+). + /// + private void HandleAdvancements(Queue packetData) + { + bool reset = dataTypes.ReadNextBool(packetData); + + // --- Added advancements --- + int addedCount = dataTypes.ReadNextVarInt(packetData); + var added = new List(addedCount); + var addedDefinitions = new Dictionary> requirements)>(addedCount); + + for (int i = 0; i < addedCount; i++) + { + string id = dataTypes.ReadNextString(packetData); + + // Parent + bool hasParent = dataTypes.ReadNextBool(packetData); + if (hasParent) + dataTypes.ReadNextString(packetData); // parentId - read and discard + + // Display + string? title = null; + string? description = null; + var type = AchievementType.Task; + bool isHidden = false; + + bool hasDisplay = dataTypes.ReadNextBool(packetData); + if (hasDisplay) + { + title = dataTypes.ReadNextChat(packetData); + description = dataTypes.ReadNextChat(packetData); + dataTypes.ReadNextItemSlot(packetData, itemPalette); // icon - read and discard + + int frameType = dataTypes.ReadNextVarInt(packetData); + type = frameType switch + { + 1 => AchievementType.Challenge, + 2 => AchievementType.Goal, + _ => AchievementType.Task + }; + + int flags = dataTypes.ReadNextInt(packetData); + isHidden = (flags & 0x04) != 0; + if ((flags & 0x01) != 0) + dataTypes.ReadNextString(packetData); // background texture - read and discard + + dataTypes.ReadNextFloat(packetData); // x + dataTypes.ReadNextFloat(packetData); // y + } + + // Criteria and requirements differ by version + var requirements = new List>(); + + if (protocolVersion < MC_1_20_2_Version) + { + // Builder-based (pre-1.20.2): criteria names list, then requirements + int criteriaCount = dataTypes.ReadNextVarInt(packetData); + for (int c = 0; c < criteriaCount; c++) + dataTypes.ReadNextString(packetData); // criterion name only, no trigger data + } + + // Requirements (all versions) + int reqGroupCount = dataTypes.ReadNextVarInt(packetData); + for (int g = 0; g < reqGroupCount; g++) + { + int groupSize = dataTypes.ReadNextVarInt(packetData); + var group = new List(groupSize); + for (int s = 0; s < groupSize; s++) + group.Add(dataTypes.ReadNextString(packetData)); + requirements.Add(group); + } + + // sendsTelemetryEvent (added in 1.20, present in all versions since) + if (protocolVersion >= MC_1_20_Version) + dataTypes.ReadNextBool(packetData); + + addedDefinitions[id] = (title, description, type, isHidden, requirements); + } + + // --- Removed advancement IDs --- + int removedCount = dataTypes.ReadNextVarInt(packetData); + var removedIds = new List(removedCount); + for (int i = 0; i < removedCount; i++) + removedIds.Add(dataTypes.ReadNextString(packetData)); + + // --- Progress updates --- + int progressCount = dataTypes.ReadNextVarInt(packetData); + var progressMap = new Dictionary>(progressCount); + + for (int i = 0; i < progressCount; i++) + { + string id = dataTypes.ReadNextString(packetData); + int criteriaEntries = dataTypes.ReadNextVarInt(packetData); + var criteria = new Dictionary(criteriaEntries); + + for (int c = 0; c < criteriaEntries; c++) + { + string criterionName = dataTypes.ReadNextString(packetData); + bool isDone = dataTypes.ReadNextBool(packetData); + if (isDone) + dataTypes.ReadNextLong(packetData); // epochMs - read and discard + criteria[criterionName] = isDone; + } + + progressMap[id] = criteria; + } + + // showAdvancements boolean added in 1.21.11+ + if (protocolVersion >= MC_1_21_11_Version) + dataTypes.ReadNextBool(packetData); // showAdvancements - read and discard + + // Build Achievement records from definitions + progress + foreach (var (id, def) in addedDefinitions) + { + progressMap.TryGetValue(id, out var criteria); + criteria ??= new Dictionary(); + + bool isCompleted = ComputeAdvancementCompleted(def.requirements, criteria); + + var readOnlyReqs = def.requirements.ConvertAll>(static g => g.AsReadOnly()); + added.Add(new Achievement(id, def.title, def.description, def.type, def.isHidden, isCompleted, readOnlyReqs.AsReadOnly(), criteria)); + } + + // Also build Achievement records for progress-only updates (no definition change) + foreach (var (id, criteria) in progressMap) + { + if (!addedDefinitions.ContainsKey(id)) + added.Add(new Achievement(id, null, null, AchievementType.Task, false, false, [], criteria)); + } + + handler.OnAchievementsUpdate(added, removedIds, reset); + } + + private static Achievement CreateLegacyAchievement(string id, bool isCompleted) + { + Dictionary criteria = new(StringComparer.Ordinal) + { + [id] = isCompleted + }; + IReadOnlyList[] requirements = [[id]]; + return new Achievement(id, null, null, AchievementType.Legacy, false, isCompleted, requirements, criteria); + } + + /// + /// Compute whether an advancement is completed based on AND-of-ORs requirements. + /// + private static bool ComputeAdvancementCompleted(List> requirements, Dictionary criteria) + { + // Zero requirements = automatically done + if (requirements.Count == 0) + return true; + + // Each OR-group must have at least one satisfied criterion + foreach (var group in requirements) + { + bool groupSatisfied = false; + foreach (string criterion in group) + { + if (criteria.TryGetValue(criterion, out bool done) && done) + { + groupSatisfied = true; + break; + } + } + if (!groupSatisfied) + return false; + } + return true; + } + + /// + /// Handle the SelectAdvancementTab packet. + /// + private void HandleSelectAdvancementTab(Queue packetData) + { + bool hasTab = dataTypes.ReadNextBool(packetData); + string? tabId = hasTab ? dataTypes.ReadNextString(packetData) : null; + handler.OnSelectAdvancementTab(tabId); + } + + private void HandleUnlockRecipes(Queue packetData) + { + int action = dataTypes.ReadNextVarInt(packetData); + if (!SkipRecipeBookSettings(packetData)) + return; + + string[] recipeIds = ReadRecipeBookRecipeIds(packetData); + RecipeBookRecipeEntry[] recipeEntries = recipeIds.Select(static recipeId => new RecipeBookRecipeEntry(recipeId, recipeId)).ToArray(); + + switch (action) + { + case 0: + handler.OnRecipeBookAdd(recipeEntries, replace: true); + // INIT packets also include a second "to be displayed" recipe list. + // MCC only needs the unlocked recipe identifiers for listing/crafting. + _ = ReadRecipeBookRecipeIds(packetData); + break; + case 1: + case 3: + // Action 3 is the silent-add variant, so MCC tracks it like a regular add. + handler.OnRecipeBookAdd(recipeEntries, replace: false); + break; + case 2: + handler.OnRecipeBookRemove(recipeIds); + break; + } + } + + private void HandleRecipeBookAdd(Queue packetData) + { + int entryCount = dataTypes.ReadNextVarInt(packetData); + RecipeBookRecipeEntry[] recipeEntries = new RecipeBookRecipeEntry[entryCount]; + + // 1.21.2+ RecipeBookAdd contains one display entry per recipe: + // RecipeDisplayEntry (display id, recipe display, group, category, optional requirements), then flags. + for (int i = 0; i < entryCount; i++) + { + recipeEntries[i] = ReadRecipeBookDisplayEntry(packetData); + _ = dataTypes.ReadNextByte(packetData); // flags + } + + bool replace = dataTypes.ReadNextBool(packetData); + handler.OnRecipeBookAdd(recipeEntries, replace); + } + + private string[] ReadRecipeBookRecipeIds(Queue packetData) + { + int recipeCount = dataTypes.ReadNextVarInt(packetData); + string[] recipeIds = new string[recipeCount]; + + for (int i = 0; i < recipeCount; i++) + recipeIds[i] = dataTypes.ReadNextString(packetData); + + return recipeIds; + } + + private string[] ReadRecipeBookDisplayIds(Queue packetData) + { + int recipeCount = dataTypes.ReadNextVarInt(packetData); + string[] recipeIds = new string[recipeCount]; + + for (int i = 0; i < recipeCount; i++) + recipeIds[i] = dataTypes.ReadNextVarInt(packetData).ToString(CultureInfo.InvariantCulture); + + return recipeIds; + } + + private RecipeBookRecipeEntry ReadRecipeBookDisplayEntry(Queue packetData) + { + int displayId = dataTypes.ReadNextVarInt(packetData); + string resultLabel = ReadRecipeDisplayResultLabel(packetData); + + _ = dataTypes.ReadNextVarInt(packetData); // Optional group, encoded as varint+1 or 0 + _ = dataTypes.ReadNextVarInt(packetData); // Recipe book category registry id + SkipOptionalCraftingRequirements(packetData); + + string commandId = displayId.ToString(CultureInfo.InvariantCulture); + string displayText = $"{commandId}: {resultLabel}"; + return new RecipeBookRecipeEntry(commandId, displayText); + } + + private string ReadRecipeDisplayResultLabel(Queue packetData) + { + int displayType = dataTypes.ReadNextVarInt(packetData); + return displayType switch + { + 0 => ReadShapelessRecipeDisplayResultLabel(packetData), + 1 => ReadShapedRecipeDisplayResultLabel(packetData), + 2 => ReadFurnaceRecipeDisplayResultLabel(packetData), + 3 => ReadStonecutterRecipeDisplayResultLabel(packetData), + 4 => ReadSmithingRecipeDisplayResultLabel(packetData), + _ => $"recipe_display_{displayType}", + }; + } + + private string ReadShapelessRecipeDisplayResultLabel(Queue packetData) + { + int ingredientCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < ingredientCount; i++) + _ = ReadSlotDisplayLabel(packetData); + + string result = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // crafting station + return result; + } + + private string ReadShapedRecipeDisplayResultLabel(Queue packetData) + { + _ = dataTypes.ReadNextVarInt(packetData); // width + _ = dataTypes.ReadNextVarInt(packetData); // height + int ingredientCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < ingredientCount; i++) + _ = ReadSlotDisplayLabel(packetData); + + string result = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // crafting station + return result; + } + + private string ReadFurnaceRecipeDisplayResultLabel(Queue packetData) + { + _ = ReadSlotDisplayLabel(packetData); // ingredient + _ = ReadSlotDisplayLabel(packetData); // fuel + string result = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // crafting station + _ = dataTypes.ReadNextVarInt(packetData); // duration + _ = dataTypes.ReadNextFloat(packetData); // experience + return result; + } + + private string ReadStonecutterRecipeDisplayResultLabel(Queue packetData) + { + _ = ReadSlotDisplayLabel(packetData); // input + string result = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // crafting station + return result; + } + + private string ReadSmithingRecipeDisplayResultLabel(Queue packetData) + { + _ = ReadSlotDisplayLabel(packetData); // template + _ = ReadSlotDisplayLabel(packetData); // base + _ = ReadSlotDisplayLabel(packetData); // addition + string result = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // crafting station + return result; + } + + private string ReadSlotDisplayLabel(Queue packetData) + { + int slotDisplayType = dataTypes.ReadNextVarInt(packetData); + + // 26.1 changed the slot display registry order, inserting 3 new types: + // Pre-26.1: 0=empty, 1=any_fuel, 2=item, 3=item_stack, 4=tag, 5=smithing_trim, 6=with_remainder, 7=composite + // 26.1+: 0=empty, 1=any_fuel, 2=with_any_potion, 3=only_with_component, 4=item, 5=item_stack, 6=tag, 7=dyed, 8=smithing_trim, 9=with_remainder, 10=composite + if (protocolVersion >= MC_26_1_Version) + { + return slotDisplayType switch + { + 0 => "Empty", + 1 => "Any Fuel", + 2 => ReadWithAnyPotionSlotDisplayLabel(packetData), + 3 => ReadOnlyWithComponentSlotDisplayLabel(packetData), + 4 => Item.GetTypeString(itemPalette.FromId(dataTypes.ReadNextVarInt(packetData))), + 5 => ReadItemStackTemplateLabel(packetData), + 6 => "#" + dataTypes.ReadNextString(packetData), + 7 => ReadDyedSlotDisplayLabel(packetData), + 8 => ReadSmithingTrimSlotDisplayLabel(packetData), + 9 => ReadWithRemainderSlotDisplayLabel(packetData), + 10 => ReadCompositeSlotDisplayLabel(packetData), + _ => $"slot_display_{slotDisplayType}", + }; + } + + return slotDisplayType switch + { + 0 => "Empty", + 1 => "Any Fuel", + 2 => Item.GetTypeString(itemPalette.FromId(dataTypes.ReadNextVarInt(packetData))), + 3 => dataTypes.ReadNextItemSlot(packetData, itemPalette)?.GetTypeString() ?? "Empty", + 4 => "#" + dataTypes.ReadNextString(packetData), + 5 => ReadSmithingTrimSlotDisplayLabel(packetData), + 6 => ReadWithRemainderSlotDisplayLabel(packetData), + 7 => ReadCompositeSlotDisplayLabel(packetData), + _ => $"slot_display_{slotDisplayType}", + }; + } + + /// + /// Reads a with_any_potion slot display (26.1+): contains a nested SlotDisplay. + /// + private string ReadWithAnyPotionSlotDisplayLabel(Queue packetData) + { + return ReadSlotDisplayLabel(packetData); + } + + /// + /// Reads an only_with_component slot display (26.1+): contains a nested SlotDisplay and a DataComponentType VarInt ID. + /// + private string ReadOnlyWithComponentSlotDisplayLabel(Queue packetData) + { + string sourceLabel = ReadSlotDisplayLabel(packetData); + _ = dataTypes.ReadNextVarInt(packetData); // DataComponentType registry id + return sourceLabel; + } + + /// + /// Reads a dyed slot display (26.1+): contains two nested SlotDisplays (dye + target). + /// + private string ReadDyedSlotDisplayLabel(Queue packetData) + { + _ = ReadSlotDisplayLabel(packetData); // dye + string targetLabel = ReadSlotDisplayLabel(packetData); // target + return targetLabel; + } + + private string ReadSmithingTrimSlotDisplayLabel(Queue packetData) + { + string baseLabel = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // material + _ = dataTypes.ReadNextVarInt(packetData); // trim pattern registry id + return baseLabel; + } + + private string ReadWithRemainderSlotDisplayLabel(Queue packetData) + { + string inputLabel = ReadSlotDisplayLabel(packetData); + _ = ReadSlotDisplayLabel(packetData); // remainder + return inputLabel; + } + + private string ReadCompositeSlotDisplayLabel(Queue packetData) + { + int optionCount = dataTypes.ReadNextVarInt(packetData); + string label = "Composite"; + + for (int i = 0; i < optionCount; i++) + { + string optionLabel = ReadSlotDisplayLabel(packetData); + if (label == "Composite" && optionLabel is not "Empty" and not "Composite") + label = optionLabel; + } + + return label; + } + + /// + /// Read an ItemStackTemplate (26.1+) which encodes fields in a different order + /// than ItemStack: item_id (VarInt), count (VarInt), DataComponentPatch. + /// + private string ReadItemStackTemplateLabel(Queue packetData) + { + return dataTypes.ReadNextItemStackTemplate(packetData, itemPalette).GetTypeString(); + } + + private void SkipOptionalCraftingRequirements(Queue packetData) + { + if (!dataTypes.ReadNextBool(packetData)) + return; + + int ingredientCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < ingredientCount; i++) + SkipItemHolderSet(packetData); + } + + private void SkipItemHolderSet(Queue packetData) + { + int entryCount = dataTypes.ReadNextVarInt(packetData) - 1; + if (entryCount == -1) + { + _ = dataTypes.ReadNextString(packetData); + return; + } + + for (int i = 0; i < entryCount; i++) + _ = dataTypes.ReadNextVarInt(packetData); + } + + private bool SkipRecipeBookSettings(Queue packetData) + { + // MC 1.13-1.16.1 uses 4 booleans for the crafting/smelting recipe book states. + // MC 1.16.2+ expands this to 8 booleans through RecipeBookSettings. + int boolCount = protocolVersion >= MC_1_16_2_Version ? 8 : 4; + if (packetData.Count < boolCount) + return false; + + for (int i = 0; i < boolCount; i++) + _ = dataTypes.ReadNextBool(packetData); + + return true; + } + /// /// Start the updating thread. Should be called after login success. /// @@ -2703,7 +4030,7 @@ namespace MinecraftClient.Protocol.Handlers /// Net read thread ID public int GetNetMainThreadId() { - return netMain != null ? netMain.Item1.ManagedThreadId : -1; + return netMain is not null ? netMain.Item1.ManagedThreadId : -1; } /// @@ -2713,12 +4040,12 @@ namespace MinecraftClient.Protocol.Handlers { try { - if (netMain != null) + if (netMain is not null) { netMain.Item2.Cancel(); } - if (netReader != null) + if (netReader is not null) { netReader.Item2.Cancel(); socketWrapper.Disconnect(); @@ -2736,7 +4063,40 @@ namespace MinecraftClient.Protocol.Handlers /// packet Data private void SendPacket(PacketTypesOut packet, IEnumerable packetData) { - SendPacket(packetPalette.GetOutgoingIdByType(packet), packetData); + SendPacket(packetPalette.GetOutgoingIdByType(packet), packetData, packet.ToString()); + } + + private void ProcessChunkBlockEntityData(int chunkX, int chunkZ, Queue packetData) + { + if (protocolVersion < MC_1_17_Version || packetData.Count == 0) + return; + + int blockEntityCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < blockEntityCount; i++) + { + if (protocolVersion < MC_1_18_1_Version) + { + Dictionary? blockEntityNbt = dataTypes.ReadNextNbt(packetData); + if (blockEntityNbt.TryGetValue("x", out var nbtX) + && blockEntityNbt.TryGetValue("y", out var nbtY) + && blockEntityNbt.TryGetValue("z", out var nbtZ)) + { + handler.OnBlockEntityData( + new Location(Convert.ToInt32(nbtX), Convert.ToInt32(nbtY), Convert.ToInt32(nbtZ)), + blockEntityNbt); + } + + continue; + } + + int packedXZ = dataTypes.ReadNextByte(packetData); + int y = dataTypes.ReadNextShort(packetData); + dataTypes.ReadNextVarInt(packetData); // Block entity type registry id + Dictionary? nbt = dataTypes.ReadNextNbt(packetData); + int blockX = chunkX * Chunk.SizeX + ((packedXZ >> 4) & 0x0F); + int blockZ = chunkZ * Chunk.SizeZ + (packedXZ & 0x0F); + handler.OnBlockEntityData(new Location(blockX, y, blockZ), nbt); + } } /// @@ -2746,7 +4106,62 @@ namespace MinecraftClient.Protocol.Handlers /// packet Data private void SendPacket(ConfigurationPacketTypesOut packet, IEnumerable packetData) { - SendPacket(packetPalette.GetOutgoingIdByTypeConfiguration(packet), packetData); + SendPacket(packetPalette.GetOutgoingIdByTypeConfiguration(packet), packetData, packet.ToString()); + } + + private void HandleShowDialog(Queue packetData, DialogPhase phase) + { + if (phase == DialogPhase.Play) + { + var holderId = dataTypes.ReadNextVarInt(packetData); + if (holderId != 0) + { + handler.OnDialogRegistryReferenceShown(holderId - 1, phase); + return; + } + } + + var dialog = dialogNbtParser.Parse(dataTypes.ReadNextNbt(packetData)); + handler.OnDialogShown(dialog, phase); + } + + private IReadOnlyList ReadServerLinks(Queue packetData) + { + var linksCount = dataTypes.ReadNextVarInt(packetData); + List links = new(linksCount); + + for (var i = 0; i < linksCount; i++) + { + string label; + var isBuiltIn = dataTypes.ReadNextBool(packetData); + if (isBuiltIn) + label = GetKnownServerLinkLabel(dataTypes.ReadNextVarInt(packetData)); + else + label = dataTypes.ReadNextChat(packetData); + + var url = dataTypes.ReadNextString(packetData); + links.Add(new DialogServerLink(label, url)); + } + + return links; + } + + private static string GetKnownServerLinkLabel(int id) + { + return id switch + { + 0 => Translations.dialog_server_link_report_bug, + 1 => Translations.dialog_server_link_community_guidelines, + 2 => Translations.dialog_server_link_support, + 3 => Translations.dialog_server_link_status, + 4 => Translations.dialog_server_link_feedback, + 5 => Translations.dialog_server_link_community, + 6 => Translations.dialog_server_link_website, + 7 => Translations.dialog_server_link_forums, + 8 => Translations.dialog_server_link_news, + 9 => Translations.dialog_server_link_announcements, + _ => id.ToString(CultureInfo.InvariantCulture) + }; } /// @@ -2754,40 +4169,178 @@ namespace MinecraftClient.Protocol.Handlers /// /// packet ID /// packet Data - private void SendPacket(int packetId, IEnumerable packetData) + private void SendPacket(int packetId, IEnumerable packetData, string? packetType = null) { + byte[] payload = packetData as byte[] ?? packetData.ToArray(); + LogOutgoingPacket(packetId, payload.Length, packetType); + if (handler.GetNetworkPacketCaptureEnabled()) { - var clone = packetData.ToList(); - handler.OnNetworkPacket(packetId, clone, currentState == CurrentState.Login, false); + handler.OnNetworkPacket(packetId, payload.ToList(), currentState == CurrentState.Login, false); } //log.Info($"[C -> S] Sending packet {packetId:X} > {dataTypes.ByteArrayToString(packetData.ToArray())}"); //The inner packet - var thePacket = dataTypes.ConcatBytes(DataTypes.GetVarInt(packetId), packetData.ToArray()); + byte[] packetIdBytes = DataTypes.GetVarInt(packetId); + byte[] thePacket = new byte[packetIdBytes.Length + payload.Length]; + Buffer.BlockCopy(packetIdBytes, 0, thePacket, 0, packetIdBytes.Length); + Buffer.BlockCopy(payload, 0, thePacket, packetIdBytes.Length, payload.Length); if (compression_treshold >= 0) //Compression enabled? { - thePacket = thePacket.Length >= compression_treshold - ? dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), ZlibUtils.Compress(thePacket)) - : dataTypes.ConcatBytes(DataTypes.GetVarInt(0), thePacket); + byte[] compressedHeader = thePacket.Length >= compression_treshold + ? DataTypes.GetVarInt(thePacket.Length) + : DataTypes.GetVarInt(0); + byte[] compressedPayload = thePacket.Length >= compression_treshold + ? ZlibUtils.Compress(thePacket) + : thePacket; + + byte[] compressedPacket = new byte[compressedHeader.Length + compressedPayload.Length]; + Buffer.BlockCopy(compressedHeader, 0, compressedPacket, 0, compressedHeader.Length); + Buffer.BlockCopy(compressedPayload, 0, compressedPacket, compressedHeader.Length, compressedPayload.Length); + thePacket = compressedPacket; } //log.Debug("[C -> S] Sending packet " + packetId + " > " + dataTypes.ByteArrayToString(dataTypes.ConcatBytes(dataTypes.GetVarInt(thePacket.Length), thePacket))); - socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), thePacket)); + byte[] packetLengthBytes = DataTypes.GetVarInt(thePacket.Length); + byte[] fullPacket = new byte[packetLengthBytes.Length + thePacket.Length]; + Buffer.BlockCopy(packetLengthBytes, 0, fullPacket, 0, packetLengthBytes.Length); + Buffer.BlockCopy(thePacket, 0, fullPacket, packetLengthBytes.Length, thePacket.Length); + socketWrapper.SendDataRAW(fullPacket); + } + + private void SetCurrentState(CurrentState newState) + { + if (currentState == newState) + return; + + var previousState = currentState; + currentState = newState; + + if (!log.DebugEnabled) + return; + + log.PacketDebug(string.Format(Translations.debug_packet_state_change, previousState, newState)); + } + + private static bool IsPacketExcluded(string packetType) + { + var exclusions = Settings.Config.Logging.PacketDebugExclusions; + return exclusions.Count > 0 && exclusions.Contains(packetType, StringComparer.OrdinalIgnoreCase); + } + + private void LogIncomingPacket(int packetId, int payloadLength, int frameLength, bool compressed, int uncompressedLength) + { + if (!log.DebugEnabled) + return; + + var packetType = ResolveIncomingPacketType(packetId); + if (IsPacketExcluded(packetType)) + return; + + var compressionInfo = compression_treshold < 0 + ? Translations.debug_packet_compression_disabled + : compressed + ? string.Format(Translations.debug_packet_compression_compressed, uncompressedLength) + : Translations.debug_packet_compression_uncompressed; + + log.PacketDebug(string.Format(Translations.debug_packet_incoming, + currentState, + packetId, + packetType, + payloadLength, + frameLength, + compressionInfo)); + } + + private void LogOutgoingPacket(int packetId, int payloadLength, string? packetType) + { + if (!log.DebugEnabled) + return; + + var resolvedType = packetType ?? ResolveOutgoingPacketType(packetId); + if (IsPacketExcluded(resolvedType)) + return; + + log.PacketDebug(string.Format(Translations.debug_packet_outgoing, + currentState, + packetId, + resolvedType, + payloadLength, + compression_treshold)); + } + + private void LogNetworkLoopExit(string loopName, string reason) + { + if (!log.DebugEnabled) + return; + + log.PacketDebug(string.Format(Translations.debug_packet_loop_exit, + loopName, + reason, + currentState, + socketWrapper.IsConnected())); + } + + private string ResolveIncomingPacketType(int packetId) + { + return currentState switch + { + CurrentState.Login => packetId switch + { + 0x00 => "Disconnect", + 0x01 => "EncryptionRequest", + 0x02 => "LoginSuccess", + 0x03 => "SetCompression", + 0x04 => "LoginPluginRequest", + 0x05 => "CookieRequest", + _ => string.Format(Translations.debug_packet_unknown_type, packetId) + }, + CurrentState.Configuration when packetPalette.GetMappingInConfiguration().TryGetValue(packetId, out var configurationPacket) => + configurationPacket.ToString(), + CurrentState.Play when packetPalette.GetMappingIn().TryGetValue(packetId, out var playPacket) => + playPacket.ToString(), + _ => string.Format(Translations.debug_packet_unknown_type, packetId) + }; + } + + private string ResolveOutgoingPacketType(int packetId) + { + return currentState switch + { + CurrentState.Login => packetId switch + { + 0x00 => "LoginStart", + 0x01 => "EncryptionResponse", + 0x02 => "LoginPluginResponse", + 0x03 => "LoginAcknowledged", + 0x04 => "CookieResponse", + _ => string.Format(Translations.debug_packet_unknown_type, packetId) + }, + CurrentState.Configuration when packetPalette.GetMappingOutConfiguration().TryGetValue(packetId, out var configurationPacket) => + configurationPacket.ToString(), + CurrentState.Play when packetPalette.GetMappingOut().TryGetValue(packetId, out var playPacket) => + playPacket.ToString(), + _ => string.Format(Translations.debug_packet_unknown_type, packetId) + }; } /// /// Do the Minecraft login. /// /// True if login successful - public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) + public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false) { + int nextState = isTransfer && protocolVersion >= MC_1_20_6_Version ? 3 : 2; + + if (nextState == 3) + log.PacketDebug("Using transfer handshake intent for transferred login."); + // 1. Send the handshake packet SendPacket(0x00, dataTypes.ConcatBytes( - // Protocol Version - DataTypes.GetVarInt(protocolVersion), + // Protocol Version (use raw version for snapshot/RC servers) + DataTypes.GetVarInt(rawProtocolVersion), // Server Address dataTypes.GetString(pForge.GetServerAddress(handler.GetServerHost())), @@ -2796,7 +4349,8 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.GetUShort((ushort)handler.GetServerPort()), // Next State - DataTypes.GetVarInt(2)) // 2 is for the Login state + DataTypes.GetVarInt(nextState)), // 2 is Login, 3 is Transfer + "Handshake" ); // 2. Send the Login Start packet @@ -2806,7 +4360,7 @@ namespace MinecraftClient.Protocol.Handlers // 1.19 - 1.19.2 if (protocolVersion is >= MC_1_19_Version and < MC_1_19_3_Version) { - if (playerKeyPair == null) + if (playerKeyPair is null) fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has Sig Data else { @@ -2830,17 +4384,17 @@ namespace MinecraftClient.Protocol.Handlers switch (protocolVersion) { case >= MC_1_19_2_Version and < MC_1_20_2_Version: - { - if (uuid == Guid.Empty) - fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has UUID - else { - fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has UUID - fullLoginPacket.AddRange(DataTypes.GetUUID(uuid)); // UUID - } + if (uuid == Guid.Empty) + fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has UUID + else + { + fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has UUID + fullLoginPacket.AddRange(DataTypes.GetUUID(uuid)); // UUID + } - break; - } + break; + } case >= MC_1_20_2_Version: uuid = handler.GetUserUuid(); @@ -2851,7 +4405,7 @@ namespace MinecraftClient.Protocol.Handlers break; } - SendPacket(0x00, fullLoginPacket); + SendPacket(0x00, fullLoginPacket, "LoginStart"); // 3. Encryption Request - 9. Login Acknowledged while (true) @@ -2868,36 +4422,46 @@ namespace MinecraftClient.Protocol.Handlers // Encryption request case 0x01: - { - isOnlineMode = true; - var serverId = dataTypes.ReadNextString(packetData); - var serverPublicKey = dataTypes.ReadNextByteArray(packetData); - var token = dataTypes.ReadNextByteArray(packetData); - return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), - Config.Main.General.AccountType, token, serverId, - serverPublicKey, playerKeyPair, session); - } + { + isOnlineMode = true; + var serverId = dataTypes.ReadNextString(packetData); + var serverPublicKey = dataTypes.ReadNextByteArray(packetData); + var token = dataTypes.ReadNextByteArray(packetData); + + var shouldAuthetnicate = false; + + if (protocolVersion >= MC_1_20_6_Version) + shouldAuthetnicate = dataTypes.ReadNextBool(packetData); + + return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), + Config.Main.General.AccountType, token, serverId, + serverPublicKey, playerKeyPair, session, shouldAuthetnicate); + } // Login successful case 0x02: - { - log.Info($"§8{Translations.mcc_server_offline}"); - currentState = protocolVersion < MC_1_20_2_Version - ? CurrentState.Play - : CurrentState.Configuration; - - if (protocolVersion >= MC_1_20_2_Version) - SendPacket(0x03, new List()); - - if (!pForge.CompleteForgeHandshake()) { - log.Error($"§8{Translations.error_forge}"); - return false; - } + log.Info($"§8{Translations.mcc_server_offline}"); + SetCurrentState(protocolVersion < MC_1_20_2_Version + ? CurrentState.Play + : CurrentState.Configuration); - StartUpdating(); - return true; //No need to check session or start encryption - } + if (protocolVersion >= MC_1_20_2_Version) + { + // Hypixel and other 1.20.2+ servers stay in configuration until ClientInformation is sent. + SendPacket(0x03, new List(), "LoginAcknowledged"); + SendConfiguredClientSettings(); + } + + if (!pForge.CompleteForgeHandshake()) + { + log.Error($"§8{Translations.error_forge}"); + return false; + } + + StartUpdating(); + return true; //No need to check session or start encryption + } default: HandlePacket(packetId, packetData); break; @@ -2910,14 +4474,14 @@ namespace MinecraftClient.Protocol.Handlers /// /// True if encryption was successful private bool StartEncryption(string uuid, string sessionID, LoginType type, byte[] token, string serverIDhash, - byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session) + byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session, bool shouldAuthetnicate) { var RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; var secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey(); - log.Debug($"§8{Translations.debug_crypto}"); + log.PacketDebug($"§8{Translations.debug_crypto}"); - if (serverIDhash != "-") + if (serverIDhash != "-" && !string.IsNullOrWhiteSpace(sessionID)) { log.Info(Translations.mcc_session); @@ -2931,6 +4495,10 @@ namespace MinecraftClient.Protocol.Handlers needCheckSession = false; } + // 1.20.6++ + if (shouldAuthetnicate) + needCheckSession = true; + if (needCheckSession) { var serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey); @@ -2955,7 +4523,7 @@ namespace MinecraftClient.Protocol.Handlers // 1.19 - 1.19.2 if (protocolVersion is >= MC_1_19_Version and < MC_1_19_3_Version) { - if (playerKeyPair == null) + if (playerKeyPair is null) { encryptionResponse.AddRange(dataTypes.GetBool(true)); // Has Verify Token encryptionResponse.AddRange(dataTypes.GetArray(RSAService.Encrypt(token, false))); // Verify Token @@ -2999,46 +4567,55 @@ namespace MinecraftClient.Protocol.Handlers handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; + //Login successful case 0x02: - { - var uuidReceived = protocolVersion >= MC_1_16_Version - ? dataTypes.ReadNextUUID(packetData) - : Guid.Parse(dataTypes.ReadNextString(packetData)); - var userName = dataTypes.ReadNextString(packetData); - Tuple[]? playerProperty = null; - if (protocolVersion >= MC_1_19_Version) { - var count = dataTypes.ReadNextVarInt(packetData); // Number Of Properties - playerProperty = new Tuple[count]; - for (var i = 0; i < count; ++i) + var uuidReceived = protocolVersion >= MC_1_16_Version + ? dataTypes.ReadNextUUID(packetData) + : Guid.Parse(dataTypes.ReadNextString(packetData)); + var userName = dataTypes.ReadNextString(packetData); + Tuple[]? playerProperty = null; + if (protocolVersion >= MC_1_19_Version) { - var name = dataTypes.ReadNextString(packetData); - var value = dataTypes.ReadNextString(packetData); - var isSigned = dataTypes.ReadNextBool(packetData); - var signature = isSigned ? dataTypes.ReadNextString(packetData) : string.Empty; - playerProperty[i] = new Tuple(name, value, signature); + var count = dataTypes.ReadNextVarInt(packetData); // Number Of Properties + playerProperty = new Tuple[count]; + for (var i = 0; i < count; ++i) + { + var name = dataTypes.ReadNextString(packetData); + var value = dataTypes.ReadNextString(packetData); + var isSigned = dataTypes.ReadNextBool(packetData); + var signature = isSigned ? dataTypes.ReadNextString(packetData) : string.Empty; + playerProperty[i] = new Tuple(name, value, signature); + } } + + // Strict Error Handling (removed in 1.21.2) + if (protocolVersion >= MC_1_20_6_Version && protocolVersion < MC_1_21_2_Version) + dataTypes.ReadNextBool(packetData); + + SetCurrentState(protocolVersion < MC_1_20_2_Version + ? CurrentState.Play + : CurrentState.Configuration); + + if (protocolVersion >= MC_1_20_2_Version) + { + // Hypixel and other 1.20.2+ servers stay in configuration until ClientInformation is sent. + SendPacket(0x03, new List(), "LoginAcknowledged"); + SendConfiguredClientSettings(); + } + + handler.OnLoginSuccess(uuidReceived, userName, playerProperty); + + if (!pForge.CompleteForgeHandshake()) + { + log.Error($"§8{Translations.error_forge_encrypt}"); + return false; + } + + StartUpdating(); + return true; } - - currentState = protocolVersion < MC_1_20_2_Version - ? CurrentState.Play - : CurrentState.Configuration; - - if (protocolVersion >= MC_1_20_2_Version) - SendPacket(0x03, new List()); - - handler.OnLoginSuccess(uuidReceived, userName, playerProperty); - - if (!pForge.CompleteForgeHandshake()) - { - log.Error($"§8{Translations.error_forge_encrypt}"); - return false; - } - - StartUpdating(); - return true; - } default: HandlePacket(packetId, packetData); break; @@ -3065,9 +4642,9 @@ namespace MinecraftClient.Protocol.Handlers return -1; var transactionId = DataTypes.GetVarInt(autocomplete_transaction_id); - var assumeCommand = new byte[] { 0x00 }; - var hasPosition = new byte[] { 0x00 }; - var tabCompletePacket = Array.Empty(); + byte[] assumeCommand = [0x00]; + byte[] hasPosition = [0x00]; + byte[] tabCompletePacket = []; switch (protocolVersion) { @@ -3077,15 +4654,15 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.GetString(BehindCursor.Replace(' ', (char)0x00))); break; case >= MC_1_8_Version: - { - tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, dataTypes.GetString(BehindCursor)); + { + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, dataTypes.GetString(BehindCursor)); - if (protocolVersion >= MC_1_9_Version) - tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, assumeCommand); + if (protocolVersion >= MC_1_9_Version) + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, assumeCommand); - tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, hasPosition); - break; - } + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, hasPosition); + break; + } default: tabCompletePacket = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); break; @@ -3102,90 +4679,186 @@ namespace MinecraftClient.Protocol.Handlers /// True if ping was successful public static bool DoPing(string host, int port, ref int protocolVersion, ref ForgeInfo? forgeInfo) { - var version = ""; - var tcp = ProxyHandler.NewTcpClient(host, port); - tcp.ReceiveTimeout = 30000; // 30 seconds - tcp.ReceiveBufferSize = 1024 * 1024; - SocketWrapper socketWrapper = new(tcp); - DataTypes dataTypes = new(MC_1_8_Version); + SocketWrapper? socketWrapper = null; - var serverPort = BitConverter.GetBytes((ushort)port); - Array.Reverse(serverPort); - - // Ping Packet - var pingPacket = dataTypes.ConcatBytes( - // Packet Id - DataTypes.GetVarInt(0), - - // Protocol Version - DataTypes.GetVarInt(-1), - - // Server IP (Host) - dataTypes.GetString(host), - - // Server port - serverPort, - - // Next State - DataTypes.GetVarInt(1)); - - socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket)); - - // Status Request Packet - var statusRequest = DataTypes.GetVarInt(0); - socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest)); - - // Read Response length - var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); - if (packetLength <= 0) return false; - - // Read the Packet Id - var packetData = new Queue(socketWrapper.ReadDataRAW(packetLength)); - if (dataTypes.ReadNextVarInt(packetData) != 0x00) return false; - - var result = dataTypes.ReadNextString(packetData); // Get the Json data - - if (Config.Logging.DebugMessages) + try { - // May contain formatting codes, cannot use WriteLineFormatted - Console.ForegroundColor = ConsoleColor.DarkGray; - ConsoleIO.WriteLine(result); - Console.ForegroundColor = ConsoleColor.Gray; + var version = ""; + var tcp = ProxyHandler.NewTcpClient(host, port); + tcp.ReceiveTimeout = 30000; // 30 seconds + tcp.ReceiveBufferSize = 1024 * 1024; + socketWrapper = new SocketWrapper(tcp); + DataTypes dataTypes = new(MC_1_8_Version); + + var serverPort = BitConverter.GetBytes((ushort)port); + Array.Reverse(serverPort); + + // Ping Packet + var pingPacket = dataTypes.ConcatBytes( + // Packet Id + DataTypes.GetVarInt(0), + + // Protocol Version + DataTypes.GetVarInt(-1), + + // Server IP (Host) + dataTypes.GetString(host), + + // Server port + serverPort, + + // Next State + DataTypes.GetVarInt(1)); + + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket)); + + // Status Request Packet + var statusRequest = DataTypes.GetVarInt(0); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest)); + + // Read Response length + var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); + if (packetLength <= 0) + return false; + + // Read the Packet Id + var packetData = new Queue(socketWrapper.ReadDataRAW(packetLength)); + if (dataTypes.ReadNextVarInt(packetData) != 0x00) + return false; + + // Get the Json data + var result = dataTypes.ReadNextString(packetData); + + if (Config.Logging.DebugMessages) + { + // May contain formatting codes, cannot use WriteLineFormatted + Console.ForegroundColor = ConsoleColor.DarkGray; + ConsoleIO.WriteLine(result); + Console.ForegroundColor = ConsoleColor.Gray; + } + + if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}")) + return false; + + var jsonData = Json.ParseJson(result); + if (jsonData is not System.Text.Json.Nodes.JsonObject jsonObj || !jsonObj.ContainsKey("version")) + return false; + + var versionData = jsonObj["version"]!.AsObject(); + + // Retrieve display name of the Minecraft version + if (versionData["name"] is { } nameNode) + version = nameNode.GetStringValue(); + + // Retrieve protocol version number for handling this server + if (versionData["protocol"] is { } protocolNode) + protocolVersion = int.Parse(protocolNode.GetStringValue(), + NumberStyles.Any, CultureInfo.CurrentCulture); + + // Check for forge on the server. + Protocol18Forge.ServerInfoCheckForge(jsonObj, ref forgeInfo); + + int onlinePlayers = 0, maxPlayers = 0; + List samplePlayers = []; + + if (jsonObj["players"] is System.Text.Json.Nodes.JsonObject playersObj) + { + if (playersObj["online"] is { } onlineNode) + onlinePlayers = int.Parse(onlineNode.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); + if (playersObj["max"] is { } maxNode) + maxPlayers = int.Parse(maxNode.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); + if (playersObj["sample"] is System.Text.Json.Nodes.JsonArray sampleArray) + { + foreach (var entry in sampleArray) + { + if (entry is not System.Text.Json.Nodes.JsonObject playerObj) continue; + samplePlayers.Add(new ServerStatusInfo.SamplePlayer + { + Name = playerObj["name"]?.GetStringValue() ?? "", + Id = playerObj["id"]?.GetStringValue() ?? "" + }); + } + } + } + + string motdRaw = ""; + if (jsonObj["description"] is { } descNode) + motdRaw = descNode.ToJsonString(); + + string? faviconBase64 = null; + if (jsonObj["favicon"] is { } faviconNode) + { + var faviconStr = faviconNode.GetStringValue(); + const string prefix = "data:image/png;base64,"; + faviconBase64 = faviconStr.StartsWith(prefix, StringComparison.Ordinal) + ? faviconStr[prefix.Length..] + : faviconStr; + } + + long pingMs = -1; + try + { + long pingPayload = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload)); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest)); + + packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); + if (packetLength > 0) + { + packetData = new Queue(socketWrapper.ReadDataRAW(packetLength)); + if (dataTypes.ReadNextVarInt(packetData) == 0x01) + { + long pongPayload = dataTypes.ReadNextLong(packetData); + pingMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - pingPayload; + } + } + } + catch + { + // Some servers may close the probe connection immediately after the status response. + } + + var statusInfo = new ServerStatusInfo + { + Host = host, + Port = port, + VersionName = version, + ProtocolVersion = protocolVersion, + OnlinePlayers = onlinePlayers, + MaxPlayers = maxPlayers, + SamplePlayers = samplePlayers, + MotdRaw = motdRaw, + FaviconBase64 = faviconBase64, + PingMs = pingMs + }; + + ProtocolHandler.TryUpgradeProtocolVersion(version, ref protocolVersion); + statusInfo.ResolvedProtocol = protocolVersion; + + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version, + protocolVersion + (forgeInfo is not null ? Translations.mcc_with_forge : ""))); + + ServerStatusDisplay.Show(statusInfo); + + return true; + } + finally + { + socketWrapper?.Disconnect(); } - - if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}")) return false; - - var jsonData = Json.ParseJson(result); - if (jsonData.Type != Json.JSONData.DataType.Object || !jsonData.Properties.ContainsKey("version")) - return false; - - var versionData = jsonData.Properties["version"]; - - //Retrieve display name of the Minecraft version - if (versionData.Properties.TryGetValue("name", out var property)) - version = property.StringValue; - - //Retrieve protocol version number for handling this server - if (versionData.Properties.TryGetValue("protocol", out var dataProperty)) - protocolVersion = int.Parse(dataProperty.StringValue, - NumberStyles.Any, CultureInfo.CurrentCulture); - - // Check for forge on the server. - Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo); - - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version, - protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : ""))); - - return true; } /// /// Get max length for chat messages /// /// Max length, in characters - public int GetMaxChatMessageLength() => protocolVersion > MC_1_10_Version - ? 256 - : 100; + public int GetMaxChatMessageLength() + { + int configOverride = Settings.MainConfigHelper.Config.Advanced.MaxChatMessageLength; + if (configOverride > 0) + return configOverride; + return protocolVersion > MC_1_10_Version ? 256 : 100; + } /// /// Get the current protocol version. @@ -3266,7 +4939,7 @@ namespace MinecraftClient.Protocol.Handlers public void Acknowledge(ChatMessage message) { var entry = message.ToLastSeenMessageEntry(); - if (entry == null) return; + if (entry is null) return; if (protocolVersion >= MC_1_19_3_Version) { @@ -3287,12 +4960,12 @@ namespace MinecraftClient.Protocol.Handlers } /// - /// Send a chat command to the server - 1.19 and above + /// Send a chat command to the server, with or without signing based on the online mode and version. /// /// Command - /// PlayerKeyPair + /// PlayerKeyPair (optional) /// True if properly sent - public bool SendChatCommand(string command, PlayerKeyPair? playerKeyPair) + public bool SendChatCommand(string command, PlayerKeyPair? playerKeyPair = null) { if (string.IsNullOrEmpty(command)) return true; @@ -3300,87 +4973,99 @@ namespace MinecraftClient.Protocol.Handlers command = Regex.Replace(command, @"\s+", " "); command = Regex.Replace(command, @"\s$", string.Empty); - log.Debug($"chat command = {command}"); + log.PacketDebug($"chat command = {command}"); + + if (protocolVersion >= MC_1_20_6_Version && !isOnlineMode) + { + List fields = new(); + fields.AddRange(dataTypes.GetString(command)); + SendPacket(PacketTypesOut.ChatCommand, fields); + return true; + } try { - List>? needSigned = null; // List< Argument Name, Argument Value > - if (playerKeyPair != null && isOnlineMode && protocolVersion >= MC_1_19_Version - && Config.Signature is { LoginWithSecureProfile: true, SignMessageInCommand: true }) - needSigned = DeclareCommands.CollectSignArguments(command); + List>? needSigned = null; + bool canSignCommand = protocolVersion >= MC_1_19_Version && + isOnlineMode && + playerKeyPair is not null && + Config.Signature.LoginWithSecureProfile && + Config.Signature.SignMessageInCommand; + + if (canSignCommand) + { + if (DeclareCommands.IsCommandTreeAvailable) + { + needSigned = DeclareCommands.CollectSignArguments(command); + } + else + { + needSigned = []; + log.PacketDebug("DeclareCommands tree unavailable, sending command without signed arguments."); + } + } lock (MessageSigningLock) { - var acknowledgment1192 = - protocolVersion == MC_1_19_2_Version ? ConsumeAcknowledgment() : null; + var acknowledgment1192 = protocolVersion == MC_1_19_2_Version ? ConsumeAcknowledgment() : null; - var (acknowledgment1193, bitset1193, messageCount1193) = - protocolVersion >= MC_1_19_3_Version - ? lastSeenMessagesCollector.Collect_1_19_3() - : new(Array.Empty(), Array.Empty(), 0); + var (acknowledgment1193, bitset1193, messageCount1193) = protocolVersion >= MC_1_19_3_Version + ? lastSeenMessagesCollector.Collect_1_19_3() + : new(Array.Empty(), Array.Empty(), 0); List fields = new(); - - // Command: String fields.AddRange(dataTypes.GetString(command)); - - // Timestamp: Instant(Long) var timeNow = DateTimeOffset.UtcNow; fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); - if (needSigned == null || needSigned!.Count == 0) + if (needSigned is null || needSigned.Count == 0) { - fields.AddRange(DataTypes.GetLong(0)); // Salt: Long - fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt + fields.AddRange(DataTypes.GetLong(0)); + fields.AddRange(DataTypes.GetVarInt(0)); } else { var uuid = handler.GetUserUuid(); var salt = GenerateSalt(); - fields.AddRange(salt); // Salt: Long - fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt + fields.AddRange(salt); + fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); foreach (var (argName, message) in needSigned) { - fields.AddRange(dataTypes.GetString(argName)); // Argument name: String - + fields.AddRange(dataTypes.GetString(argName)); var sign = protocolVersion switch { - MC_1_19_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, - ref salt), - MC_1_19_2_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, - ref salt, acknowledgment1192!.lastSeen), - _ => playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, - timeNow, ref salt, acknowledgment1193) + MC_1_19_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt), + MC_1_19_2_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment1192!.lastSeen), + _ => playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment1193) }; if (protocolVersion <= MC_1_19_2_Version) - fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt + fields.AddRange(DataTypes.GetVarInt(sign.Length)); - fields.AddRange(sign); // Signature: Byte Array + fields.AddRange(sign); } } if (protocolVersion <= MC_1_19_2_Version) - fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean + fields.AddRange(dataTypes.GetBool(false)); switch (protocolVersion) { case MC_1_19_2_Version: - // Message Acknowledgment (1.19.2) - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment1192!, - isOnlineMode && Config.Signature.LoginWithSecureProfile)); + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment1192!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); break; case >= MC_1_19_3_Version: - // message count fields.AddRange(DataTypes.GetVarInt(messageCount1193)); - - // Acknowledged: BitSet fields.AddRange(bitset1193); + + // Checksum: Byte (1.21.5+, 0 = skip verification) + if (protocolVersion >= MC_1_21_5_Version) + fields.Add(0); break; } - SendPacket(PacketTypesOut.ChatCommand, fields); + SendPacket(protocolVersion < MC_1_20_6_Version ? PacketTypesOut.ChatCommand : PacketTypesOut.SignedChatCommand, fields); } return true; @@ -3399,6 +5084,49 @@ namespace MinecraftClient.Protocol.Handlers } } + public bool SendCustomClickAction(string id, Dictionary? payload) + { + if (protocolVersion < MC_1_21_6_Version) + return false; + + try + { + List fields = new(); + fields.AddRange(dataTypes.GetString(id.Contains(':', StringComparison.Ordinal) ? id : "minecraft:" + id)); + + var tagBytes = dataTypes.GetNbtTag(payload); + if (tagBytes.Length > 65536) + return false; + + fields.AddRange(DataTypes.GetVarInt(tagBytes.Length)); + fields.AddRange(tagBytes); + + switch (currentState) + { + case CurrentState.Configuration: + SendPacket(ConfigurationPacketTypesOut.CustomClickAction, fields); + return true; + case CurrentState.Play: + SendPacket(PacketTypesOut.CustomClickAction, fields); + return true; + default: + return false; + } + } + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) + { + return false; + } + } + /// /// Send a chat message to the server /// @@ -3437,7 +5165,7 @@ namespace MinecraftClient.Protocol.Handlers var timeNow = DateTimeOffset.UtcNow; fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); - if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || + if (!isOnlineMode || playerKeyPair is null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) { fields.AddRange(DataTypes.GetLong(0)); // Salt: Long @@ -3480,6 +5208,10 @@ namespace MinecraftClient.Protocol.Handlers // Acknowledged: BitSet fields.AddRange(bitset1193); + + // Checksum: Byte (1.21.5+, 0 = skip verification) + if (protocolVersion >= MC_1_21_5_Version) + fields.Add(0); break; case MC_1_19_2_Version: // Message Acknowledgment @@ -3540,7 +5272,7 @@ namespace MinecraftClient.Protocol.Handlers { try { - SendPacket(PacketTypesOut.ClientStatus, new byte[] { 0 }); + SendPacket(PacketTypesOut.ClientStatus, [0]); return true; } catch (SocketException) @@ -3595,7 +5327,7 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(protocolVersion >= MC_1_9_Version ? DataTypes.GetVarInt(chatMode) - : new byte[] { chatMode }); + : [chatMode]); fields.Add(chatColors ? (byte)1 : (byte)0); if (protocolVersion < MC_1_8_Version) @@ -3617,7 +5349,16 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_18_1_Version) fields.Add(1); // 1.18 and above - Allow server listings - SendPacket(PacketTypesOut.ClientSettings, fields); + + if (protocolVersion >= MC_1_21_2_Version) + fields.AddRange(DataTypes.GetVarInt(0)); // 1.21.2+ Particle status: 0=All, 1=Decreased, 2=Minimal + + if (currentState == CurrentState.Configuration) + SendPacket(ConfigurationPacketTypesOut.ClientInformation, fields); + else + SendPacket(PacketTypesOut.ClientSettings, fields); + + return true; } catch (SocketException) { @@ -3634,66 +5375,191 @@ namespace MinecraftClient.Protocol.Handlers return false; } + private bool SendConfiguredClientSettings() + { + if (!Config.MCSettings.Enabled) + return true; + + // Keep the configuration-phase send separate so modern servers receive ClientInformation, not play-era ClientSettings. + return SendClientSettings( + Config.MCSettings.Locale, + Config.MCSettings.RenderDistance, + (byte)Config.MCSettings.Difficulty, + (byte)Config.MCSettings.ChatMode, + Config.MCSettings.ChatColors, + Config.MCSettings.Skin.GetByte(), + (byte)Config.MCSettings.MainHand); + } + /// /// Send a location update to the server /// /// The new location of the player /// True if the player is on the ground + /// True if the player is colliding horizontally /// Optional new yaw for updating player look /// Optional new pitch for updating player look /// True if the location update was successfully sent - public bool SendLocationUpdate(Location location, bool onGround, float? yaw, float? pitch) + public bool SendLocationUpdate(Location location, bool onGround, bool horizontalCollision, float? yaw, float? pitch) { - return SendLocationUpdate(location, onGround, yaw, pitch, true); + return SendLocationUpdate(location, onGround, horizontalCollision, yaw, pitch, true); } - public bool SendLocationUpdate(Location location, bool onGround, float? yaw = null, float? pitch = null, + public bool SendLocationUpdate(Location location, bool onGround, bool horizontalCollision, float? yaw = null, float? pitch = null, bool forceUpdate = false) { if (handler.GetTerrainEnabled()) { - var yawPitch = Array.Empty(); - var packetType = PacketTypesOut.PlayerPosition; + bool legacyMovementCadence = protocolVersion < MC_1_9_Version; + bool supportsHorizontalCollision = protocolVersion >= MC_1_21_5_Version; + int positionReminderInterval = ClientTicksPerSecond; - if (Config.Main.Advanced.TemporaryFixBadpacket) - { - if (yaw.HasValue && pitch.HasValue && - (forceUpdate || yaw.Value != LastYaw || pitch.Value != LastPitch)) - { - yawPitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), - dataTypes.GetFloat(pitch.Value)); - packetType = PacketTypesOut.PlayerPositionAndRotation; + double dx = location.X - lastSentX; + double dy = location.Y - lastSentY; + double dz = location.Z - lastSentZ; + double distSqr = dx * dx + dy * dy + dz * dz; - LastYaw = yaw.Value; - LastPitch = pitch.Value; - } - } - else - { - if (yaw.HasValue && pitch.HasValue) - { - yawPitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), - dataTypes.GetFloat(pitch.Value)); - packetType = PacketTypesOut.PlayerPositionAndRotation; + bool rotationChanged = false; + if (yaw.HasValue && pitch.HasValue) + rotationChanged = forceUpdate || yaw.Value != lastSentYaw || pitch.Value != lastSentPitch; - LastYaw = yaw.Value; - LastPitch = pitch.Value; - } - } + positionReminder++; try { - SendPacket(packetType, dataTypes.ConcatBytes( - dataTypes.GetDouble(location.X), - dataTypes.GetDouble(location.Y), - protocolVersion < MC_1_8_Version - ? dataTypes.GetDouble(location.Y + 1.62) - : Array.Empty(), - dataTypes.GetDouble(location.Z), - yawPitch, - new byte[] { onGround ? (byte)1 : (byte)0 }) - ); + PacketTypesOut packetType; + byte[] payload; + byte flags = (byte)(onGround ? 1 : 0); + bool positionChanged; + + if (legacyMovementCadence) + { + // 1.7.2-1.8.9 mirrors EntityPlayerSP#onUpdateWalkingPlayer: + // send an idle PlayerMovement packet every client tick and force + // a position refresh every 20 ticks even if the player is standing still. + positionChanged = distSqr > 9.0E-4 || positionReminder >= positionReminderInterval; + + if (positionChanged && rotationChanged && yaw.HasValue && pitch.HasValue) + { + packetType = PacketTypesOut.PlayerPositionAndRotation; + payload = dataTypes.ConcatBytes( + dataTypes.GetDouble(location.X), + dataTypes.GetDouble(location.Y), + protocolVersion < MC_1_8_Version + ? dataTypes.GetDouble(location.Y + 1.62) + : [], + dataTypes.GetDouble(location.Z), + dataTypes.GetFloat(yaw.Value), + dataTypes.GetFloat(pitch.Value), + new[] { flags }); + lastSentYaw = yaw.Value; + lastSentPitch = pitch.Value; + LastYaw = yaw.Value; + LastPitch = pitch.Value; + } + else if (positionChanged) + { + packetType = PacketTypesOut.PlayerPosition; + payload = dataTypes.ConcatBytes( + dataTypes.GetDouble(location.X), + dataTypes.GetDouble(location.Y), + protocolVersion < MC_1_8_Version + ? dataTypes.GetDouble(location.Y + 1.62) + : [], + dataTypes.GetDouble(location.Z), + new[] { flags }); + } + else if (rotationChanged && yaw.HasValue && pitch.HasValue) + { + packetType = PacketTypesOut.PlayerRotation; + payload = dataTypes.ConcatBytes( + dataTypes.GetFloat(yaw.Value), + dataTypes.GetFloat(pitch.Value), + new[] { flags }); + lastSentYaw = yaw.Value; + lastSentPitch = pitch.Value; + LastYaw = yaw.Value; + LastPitch = pitch.Value; + } + else + { + packetType = PacketTypesOut.PlayerMovement; + payload = new[] { flags }; + } + } + else + { + positionChanged = distSqr > 4.0E-8 || positionReminder >= positionReminderInterval; + bool movementStateChanged = onGround != lastSentOnGround + || (supportsHorizontalCollision && horizontalCollision != lastSentHorizontalCollision); + + if (!positionChanged && !rotationChanged && !movementStateChanged) + return true; // Nothing to send + + if (supportsHorizontalCollision && horizontalCollision) + flags |= 0x2; + + if (positionChanged && rotationChanged && yaw.HasValue && pitch.HasValue) + { + packetType = PacketTypesOut.PlayerPositionAndRotation; + payload = dataTypes.ConcatBytes( + dataTypes.GetDouble(location.X), + dataTypes.GetDouble(location.Y), + protocolVersion < MC_1_8_Version + ? dataTypes.GetDouble(location.Y + 1.62) + : [], + dataTypes.GetDouble(location.Z), + dataTypes.GetFloat(yaw.Value), + dataTypes.GetFloat(pitch.Value), + new[] { flags }); + lastSentYaw = yaw.Value; + lastSentPitch = pitch.Value; + LastYaw = yaw.Value; + LastPitch = pitch.Value; + } + else if (positionChanged) + { + packetType = PacketTypesOut.PlayerPosition; + payload = dataTypes.ConcatBytes( + dataTypes.GetDouble(location.X), + dataTypes.GetDouble(location.Y), + protocolVersion < MC_1_8_Version + ? dataTypes.GetDouble(location.Y + 1.62) + : [], + dataTypes.GetDouble(location.Z), + new[] { flags }); + } + else if (rotationChanged && yaw.HasValue && pitch.HasValue) + { + packetType = PacketTypesOut.PlayerRotation; + payload = dataTypes.ConcatBytes( + dataTypes.GetFloat(yaw.Value), + dataTypes.GetFloat(pitch.Value), + new[] { flags }); + lastSentYaw = yaw.Value; + lastSentPitch = pitch.Value; + LastYaw = yaw.Value; + LastPitch = pitch.Value; + } + else + { + packetType = PacketTypesOut.PlayerMovement; + payload = new[] { flags }; + } + } + + if (positionChanged) + { + lastSentX = location.X; + lastSentY = location.Y; + lastSentZ = location.Z; + positionReminder = 0; + } + lastSentOnGround = onGround; + lastSentHorizontalCollision = horizontalCollision; + + SendPacket(packetType, payload); return true; } catch (SocketException) @@ -3752,6 +5618,18 @@ namespace MinecraftClient.Protocol.Handlers } } + private bool IsOpenBookPluginChannel(string channel) + { + return protocolVersion < MC_1_13_Version + ? string.Equals(channel, "MC|BOpen", StringComparison.Ordinal) + : string.Equals(channel, "minecraft:book_open", StringComparison.Ordinal); + } + + private int ReadBookHand(Queue packetData) + { + return packetData.Count > 0 ? dataTypes.ReadNextVarInt(packetData) : (int)BookHand.Main; + } + /// /// Send a Login Plugin Response packet (0x02) /// @@ -3764,7 +5642,8 @@ namespace MinecraftClient.Protocol.Handlers try { SendPacket(0x02, - dataTypes.ConcatBytes(DataTypes.GetVarInt(messageId), dataTypes.GetBool(understood), data)); + dataTypes.ConcatBytes(DataTypes.GetVarInt(messageId), dataTypes.GetBool(understood), data), + "LoginPluginResponse"); return true; } catch (SocketException) @@ -3900,6 +5779,13 @@ namespace MinecraftClient.Protocol.Handlers packet.AddRange(DataTypes.GetVarInt(hand)); if (protocolVersion >= MC_1_19_Version) packet.AddRange(DataTypes.GetVarInt(sequenceId)); + + if (protocolVersion >= MC_1_21_Version) + { + packet.AddRange(dataTypes.GetFloat(LastYaw)); + packet.AddRange(dataTypes.GetFloat(LastPitch)); + } + SendPacket(PacketTypesOut.UseItem, packet); return true; } @@ -3949,6 +5835,7 @@ namespace MinecraftClient.Protocol.Handlers try { var packet = new List(); + var (cursorX, cursorY, cursorZ) = GetFaceHitCursor(face); switch (protocolVersion) { @@ -3961,16 +5848,15 @@ namespace MinecraftClient.Protocol.Handlers if (playerInventory?.Items is null) return false; - var slotWindowIds = new int[]{ 36, 37, 38, 39, 40, 41, 42, 43, 44 }; + int[] slotWindowIds = [36, 37, 38, 39, 40, 41, 42, 43, 44]; var currentSlot = ((McClient)handler).GetCurrentSlot(); - + playerInventory.Items.TryGetValue(slotWindowIds[currentSlot], out var item); packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); - - packet.Add(0); // cursorX - packet.Add(0); // cursorY - packet.Add(0); // cursorZ + AddLegacyBlockPlacementCursor(packet, cursorX, cursorY, cursorZ); + + SendPacket(PacketTypesOut.PlayerBlockPlacement, packet); return true; case < MC_1_14_Version: packet.AddRange(dataTypes.GetLocation(location)); @@ -3983,17 +5869,25 @@ namespace MinecraftClient.Protocol.Handlers packet.AddRange(DataTypes.GetVarInt(dataTypes.GetBlockFace(face))); break; } - - packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorX - packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorY - packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorZ - - if(protocolVersion >= MC_1_14_Version) + + if (protocolVersion < MC_1_11_Version) + AddLegacyBlockPlacementCursor(packet, cursorX, cursorY, cursorZ); + else + { + packet.AddRange(dataTypes.GetFloat(cursorX)); // cursorX + packet.AddRange(dataTypes.GetFloat(cursorY)); // cursorY + packet.AddRange(dataTypes.GetFloat(cursorZ)); // cursorZ + } + + if (protocolVersion >= MC_1_14_Version) packet.Add(0); // insideBlock = false - + + if (protocolVersion >= MC_1_21_2_Version) + packet.Add(0); // worldBorderHit = false + if (protocolVersion >= MC_1_19_Version) packet.AddRange(DataTypes.GetVarInt(sequenceId)); - + SendPacket(PacketTypesOut.PlayerBlockPlacement, packet); return true; } @@ -4011,6 +5905,29 @@ namespace MinecraftClient.Protocol.Handlers } } + private static (float x, float y, float z) GetFaceHitCursor(Direction face) => face switch + { + Direction.Up => (0.5f, 1.0f, 0.5f), + Direction.Down => (0.5f, 0.0f, 0.5f), + Direction.North => (0.5f, 0.5f, 0.0f), + Direction.South => (0.5f, 0.5f, 1.0f), + Direction.West => (0.0f, 0.5f, 0.5f), + Direction.East => (1.0f, 0.5f, 0.5f), + _ => (0.5f, 0.5f, 0.5f), + }; + + private static void AddLegacyBlockPlacementCursor(List packet, float cursorX, float cursorY, float cursorZ) + { + packet.Add(ToLegacyBlockPlacementCursor(cursorX)); + packet.Add(ToLegacyBlockPlacementCursor(cursorY)); + packet.Add(ToLegacyBlockPlacementCursor(cursorZ)); + } + + private static byte ToLegacyBlockPlacementCursor(float cursor) + { + return (byte)Math.Clamp((int)(cursor * 16.0f), 0, 15); + } + public bool SendHeldItemChange(short slot) { try @@ -4136,23 +6053,19 @@ namespace MinecraftClient.Protocol.Handlers break; } - List packet = new() - { - (byte)windowId // Window ID - }; + List packet = new(); + if (protocolVersion >= MC_1_21_2_Version) + packet.AddRange(DataTypes.GetVarInt(windowId)); // Window ID (VarInt in 1.21.2+) + else + packet.Add((byte)windowId); // Window ID (byte before 1.21.2) switch (protocolVersion) { - // 1.18+ - case >= MC_1_18_1_Version: + // 1.17.1+ + case >= MC_1_17_1_Version: packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID break; - // 1.17.1 - case MC_1_17_1_Version: - packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID - packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID - break; // Older default: packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID @@ -4175,11 +6088,19 @@ namespace MinecraftClient.Protocol.Handlers foreach (var slot in changedSlots) { packet.AddRange(dataTypes.GetShort(slot.Item1)); // slot ID - packet.AddRange(dataTypes.GetItemSlot(slot.Item2, itemPalette)); // slot Data + // 1.21.5+ uses HashedStack instead of ItemStack for container_click + if (protocolVersion >= MC_1_21_5_Version) + packet.AddRange(dataTypes.GetHashedItemSlot(slot.Item2, itemPalette)); + else + packet.AddRange(dataTypes.GetItemSlot(slot.Item2, itemPalette)); } } - packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); // Carried item (Clicked item) + // 1.21.5+ uses HashedStack instead of ItemStack for carried item + if (protocolVersion >= MC_1_21_5_Version) + packet.AddRange(dataTypes.GetHashedItemSlot(item, itemPalette)); + else + packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); SendPacket(PacketTypesOut.ClickWindow, packet); return true; @@ -4270,11 +6191,18 @@ namespace MinecraftClient.Protocol.Handlers { try { - var packet = new List + List packet = new(); + if (protocolVersion >= MC_1_20_6_Version) { - (byte)windowId, - (byte)buttonId - }; + packet.AddRange(DataTypes.GetVarInt(windowId)); + packet.AddRange(DataTypes.GetVarInt(buttonId)); + } + else + { + packet.Add((byte)windowId); + packet.Add((byte)buttonId); + } + SendPacket(PacketTypesOut.ClickWindowButton, packet); return true; } @@ -4292,6 +6220,111 @@ namespace MinecraftClient.Protocol.Handlers } } + public bool SendPlaceRecipe(int windowId, string recipeId, bool makeAll) + { + try + { + List packet = new(); + if (protocolVersion < MC_1_13_Version) + return false; + + packet.AddRange(DataTypes.GetVarInt(windowId)); + if (protocolVersion >= MC_1_21_2_Version) + packet.AddRange(DataTypes.GetVarInt(int.Parse(recipeId, CultureInfo.InvariantCulture))); + else + packet.AddRange(dataTypes.GetString(recipeId)); + packet.AddRange(dataTypes.GetBool(makeAll)); + SendPacket(PacketTypesOut.CraftRecipeRequest, packet); + return true; + } + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) + { + return false; + } + } + + public bool SendEditBook(Item currentBook, IReadOnlyList pages, string? title, string author, int selectedHotbarSlot) + { + try + { + if (protocolVersion < MC_1_8_Version) + return false; + + bool signing = title is not null; + IReadOnlyList normalizedPages = BookContentHelper.NormalizePages(pages); + + if (protocolVersion < MC_1_13_Version) + { + Item payload = signing + ? BookContentHelper.CreateWrittenPayload( + currentBook, + normalizedPages, + title ?? string.Empty, + author, + encodePagesAsJson: protocolVersion < MC_1_9_Version) + : BookContentHelper.CreateWritablePayload(currentBook, normalizedPages); + + byte[] payloadData = dataTypes.GetItemSlot(payload, itemPalette); + if (payloadData.Length > 32767) + return false; + + return SendPluginChannelPacket(signing ? "MC|BSign" : "MC|BEdit", payloadData); + } + + if (protocolVersion < MC_1_17_Version) + { + Item payload = signing + ? BookContentHelper.CreateWrittenPayload(currentBook, normalizedPages, title ?? string.Empty, author, encodePagesAsJson: false) + : BookContentHelper.CreateWritablePayload(currentBook, normalizedPages); + + List packet = new(); + packet.AddRange(dataTypes.GetItemSlot(payload, itemPalette)); + packet.AddRange(dataTypes.GetBool(signing)); + + if (protocolVersion >= MC_1_16_5_Version) + packet.AddRange(DataTypes.GetVarInt(selectedHotbarSlot)); + else if (protocolVersion >= MC_1_13_2_Version) + packet.AddRange(DataTypes.GetVarInt((int)BookHand.Main)); + + SendPacket(PacketTypesOut.EditBook, packet); + return true; + } + + List modernPacket = new(); + modernPacket.AddRange(DataTypes.GetVarInt(selectedHotbarSlot)); + modernPacket.AddRange(DataTypes.GetVarInt(normalizedPages.Count)); + foreach (string page in normalizedPages) + modernPacket.AddRange(dataTypes.GetString(page)); + + modernPacket.AddRange(dataTypes.GetBool(signing)); + if (signing) + modernPacket.AddRange(dataTypes.GetString(title ?? string.Empty)); + + SendPacket(PacketTypesOut.EditBook, modernPacket); + return true; + } + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) + { + return false; + } + } + public bool SendAnimation(int animation, int playerId) { try @@ -4342,7 +6375,10 @@ namespace MinecraftClient.Protocol.Handlers window_actions[windowId] = 0; } - SendPacket(PacketTypesOut.CloseWindow, new[] { (byte)windowId }); + SendPacket(PacketTypesOut.CloseWindow, + protocolVersion >= MC_1_21_2_Version + ? DataTypes.GetVarInt(windowId) + : new[] { (byte)windowId }); return true; } catch (SocketException) @@ -4505,7 +6541,7 @@ namespace MinecraftClient.Protocol.Handlers public bool SendPlayerSession(PlayerKeyPair? playerKeyPair) { - if (playerKeyPair == null || !isOnlineMode) + if (playerKeyPair is null || !isOnlineMode) return false; if (protocolVersion >= MC_1_19_3_Version) @@ -4521,7 +6557,7 @@ namespace MinecraftClient.Protocol.Handlers packet.AddRange(DataTypes.GetVarInt(playerKeyPair.PublicKey.SignatureV2!.Length)); packet.AddRange(playerKeyPair.PublicKey.SignatureV2); - log.Debug( + log.PacketDebug( $"SendPlayerSession MessageUUID = {chatUuid.ToString()}, len(PublicKey) = {playerKeyPair.PublicKey.Key.Length}, len(SignatureV2) = {playerKeyPair.PublicKey.SignatureV2!.Length}"); SendPacket(PacketTypesOut.PlayerSession, packet); @@ -4567,6 +6603,90 @@ namespace MinecraftClient.Protocol.Handlers } } + public bool SendCookieResponse(string name, byte[]? data) + { + try + { + var packet = new List(); + var hasPayload = data is not null; + packet.AddRange(dataTypes.GetString(name)); // Identifier + packet.AddRange(dataTypes.GetBool(hasPayload)); // Has payload + + if (hasPayload) + packet.AddRange(dataTypes.GetArray(data!)); // Payload Data Array Size + Data Array + + switch (currentState) + { + case CurrentState.Login: + SendPacket(0x04, packet, "CookieResponse"); + break; + + case CurrentState.Configuration: + SendPacket(ConfigurationPacketTypesOut.CookieResponse, packet); + break; + + case CurrentState.Play: + SendPacket(PacketTypesOut.CookieResponse, packet); + break; + } + + McClient.Instance?.DeleteCookie(name); + return true; + } + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) + { + return false; + } + } + + public bool SendKnownDataPacks(List<(string, string, string)> knownDataPacks) + { + try + { + var packet = new List(); + packet.AddRange(DataTypes.GetVarInt(knownDataPacks.Count)); // Known Packs Count + foreach (var dataPack in knownDataPacks) + { + packet.AddRange(dataTypes.GetString(dataPack.Item1)); + packet.AddRange(dataTypes.GetString(dataPack.Item2)); + packet.AddRange(dataTypes.GetString(dataPack.Item3)); + } + + switch (currentState) + { + case CurrentState.Configuration: + SendPacket(ConfigurationPacketTypesOut.KnownDataPacks, packet); + break; + + case CurrentState.Play: + SendPacket(PacketTypesOut.KnownDataPacks, packet); + break; + } + + return true; + } + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) + { + return false; + } + } + private byte[] GenerateSalt() { var salt = new byte[8]; @@ -4587,6 +6707,7 @@ namespace MinecraftClient.Protocol.Handlers { Login = 0, Configuration, - Play + Play, + Transfer } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index cbda452b..e940fc11 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -12,31 +12,16 @@ namespace MinecraftClient.Protocol.Handlers /// /// Handler for the Minecraft Forge protocol /// - class Protocol18Forge + class Protocol18Forge(ForgeInfo? forgeInfo, int protocolVersion, DataTypes dataTypes, Protocol18Handler protocol18, IMinecraftComHandler mcHandler) { - private readonly int protocolversion; - private readonly DataTypes dataTypes; - private readonly Protocol18Handler protocol18; - private readonly IMinecraftComHandler mcHandler; + private readonly int protocolversion = protocolVersion; + private readonly DataTypes dataTypes = dataTypes; + private readonly Protocol18Handler protocol18 = protocol18; + private readonly IMinecraftComHandler mcHandler = mcHandler; - private readonly ForgeInfo? forgeInfo; + private readonly ForgeInfo? forgeInfo = forgeInfo; private FMLHandshakeClientState fmlHandshakeState = FMLHandshakeClientState.START; - private bool ForgeEnabled() { return forgeInfo != null; } - - /// - /// Initialize a new Forge protocol handler - /// - /// Forge Server Information - /// Minecraft protocol version - /// Minecraft data types handler - public Protocol18Forge(ForgeInfo? forgeInfo, int protocolVersion, DataTypes dataTypes, Protocol18Handler protocol18, IMinecraftComHandler mcHandler) - { - this.forgeInfo = forgeInfo; - protocolversion = protocolVersion; - this.dataTypes = dataTypes; - this.protocol18 = protocol18; - this.mcHandler = mcHandler; - } + private bool ForgeEnabled() { return forgeInfo is not null; } /// /// Get Forge-Tagged server address @@ -316,6 +301,8 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < modCount; i++) mods.Add(dataTypes.ReadNextString(packetData)); + ChatParser.LoadForgeModTranslations(mods); + Dictionary channels = new(); int channelCount = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < channelCount; i++) @@ -390,7 +377,7 @@ namespace MinecraftClient.Protocol.Handlers string registryName = dataTypes.ReadNextString(packetData); ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_registry, registryName)); } - + fmlResponsePacket.AddRange(DataTypes.GetVarInt(99)); fmlResponseReady = true; break; @@ -423,7 +410,7 @@ namespace MinecraftClient.Protocol.Handlers // [ Version ][ String ] // // We're ignoring this packet in MCC - + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + "Received FML3 Server Mod Data List"); @@ -484,7 +471,7 @@ namespace MinecraftClient.Protocol.Handlers /// JSON data returned by the server /// ForgeInfo to populate /// True if the server is running Forge - public static bool ServerInfoCheckForge(Json.JSONData jsonData, ref ForgeInfo? forgeInfo) + public static bool ServerInfoCheckForge(System.Text.Json.Nodes.JsonObject jsonData, ref ForgeInfo? forgeInfo) { return ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML) // MC 1.12 and lower || ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML2) // MC 1.13 to 1.17 @@ -518,7 +505,7 @@ namespace MinecraftClient.Protocol.Handlers { return new ForgeInfo(FMLVersion.FML3); } - return new ForgeInfo(FMLVersion.FML2); + return new ForgeInfo(FMLVersion.FML2); } else throw new InvalidOperationException(Translations.error_forgeforce); } @@ -530,7 +517,7 @@ namespace MinecraftClient.Protocol.Handlers /// ForgeInfo to populate /// Forge protocol version /// True if the server is running Forge - private static bool ServerInfoCheckForgeSub(Json.JSONData jsonData, ref ForgeInfo? forgeInfo, FMLVersion fmlVersion) + private static bool ServerInfoCheckForgeSub(System.Text.Json.Nodes.JsonObject jsonData, ref ForgeInfo? forgeInfo, FMLVersion fmlVersion) { string forgeDataTag; string versionField; @@ -557,10 +544,9 @@ namespace MinecraftClient.Protocol.Handlers throw new NotImplementedException("FMLVersion '" + fmlVersion + "' not implemented!"); } - if (jsonData.Properties.ContainsKey(forgeDataTag) && jsonData.Properties[forgeDataTag].Type == Json.JSONData.DataType.Object) + if (jsonData[forgeDataTag] is System.Text.Json.Nodes.JsonObject modData) { - Json.JSONData modData = jsonData.Properties[forgeDataTag]; - if (modData.Properties.ContainsKey(versionField) && modData.Properties[versionField].StringValue == versionString) + if (modData[versionField] is not null && modData[versionField]!.GetStringValue() == versionString) { forgeInfo = new ForgeInfo(modData, fmlVersion); if (forgeInfo.Mods.Any()) @@ -582,6 +568,6 @@ namespace MinecraftClient.Protocol.Handlers } } return false; - } + } } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs index fc66edd0..9bd29e87 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,23 +12,11 @@ namespace MinecraftClient.Protocol.Handlers /// /// Terrain Decoding handler for Protocol18 /// - class Protocol18Terrain + class Protocol18Terrain(int protocolVersion, DataTypes dataTypes, IMinecraftComHandler handler) { - private readonly int protocolversion; - private readonly DataTypes dataTypes; - private readonly IMinecraftComHandler handler; - - /// - /// Initialize a new Terrain Decoder - /// - /// Minecraft Protocol Version - /// Minecraft Protocol Data Types - public Protocol18Terrain(int protocolVersion, DataTypes dataTypes, IMinecraftComHandler handler) - { - protocolversion = protocolVersion; - this.dataTypes = dataTypes; - this.handler = handler; - } + private readonly int protocolversion = protocolVersion; + private readonly DataTypes dataTypes = dataTypes; + private readonly IMinecraftComHandler handler = handler; /// /// Reading the "Block states" field: consists of 4096 entries, representing all the blocks in the chunk section. @@ -47,7 +35,8 @@ namespace MinecraftClient.Protocol.Handlers ushort blockId = (ushort)dataTypes.ReadNextVarInt(cache); Block block = new(blockId); - dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero (removed in 1.21.5) // Empty chunks will not be stored if (block.Type == Material.Air) @@ -80,7 +69,8 @@ namespace MinecraftClient.Protocol.Handlers palette[i] = (uint)dataTypes.ReadNextVarInt(cache); //// Block IDs are packed in the array of 64-bits integers - dataTypes.SkipNextVarInt(cache); // Entry length + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Entry length (removed in 1.21.5) Span entryDataByte = stackalloc byte[8]; Span entryDataLong = MemoryMarshal.Cast(entryDataByte); // Faster than MemoryMarshal.Read @@ -183,6 +173,9 @@ namespace MinecraftClient.Protocol.Handlers // Non-air block count inside chunk section, for lighting purposes int blockCnt = dataTypes.ReadNextShort(cache); + if (protocolversion >= Protocol18Handler.MC_26_1_Version) + dataTypes.ReadNextShort(cache); // Fluid count (26.1+) + // Read Block states (Type: Paletted Container) Chunk? chunk = ReadBlockStatesField(cache); @@ -196,8 +189,8 @@ namespace MinecraftClient.Protocol.Handlers if (bitsPerEntryBiome == 0) { dataTypes.SkipNextVarInt(cache); // Value - dataTypes.SkipNextVarInt(cache); // Data Array Length - // Data Array must be empty + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Data Array Length (removed in 1.21.5) } else { @@ -207,8 +200,20 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < paletteLength; i++) dataTypes.SkipNextVarInt(cache); // Palette } - int dataArrayLength = dataTypes.ReadNextVarInt(cache); // Data Array Length - dataTypes.DropData(dataArrayLength * 8, cache); // Data Array + if (protocolversion >= Protocol18Handler.MC_1_21_5_Version) + { + // 1.21.5: No VarInt length prefix; calculate from bits per entry + // Biome container has 64 entries (4x4x4) + // Uses SimpleBitStorage: valuesPerLong = 64/bitsPerEntry, longs = ceil(64/valuesPerLong) + int valuesPerLong = 64 / bitsPerEntryBiome; + int dataArrayLength = (64 + valuesPerLong - 1) / valuesPerLong; + dataTypes.DropData(dataArrayLength * 8, cache); + } + else + { + int dataArrayLength = dataTypes.ReadNextVarInt(cache); // Data Array Length + dataTypes.DropData(dataArrayLength * 8, cache); // Data Array + } } } } diff --git a/MinecraftClient/Protocol/Handlers/SocketWrapper.cs b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs index d9793024..a4f451b1 100644 --- a/MinecraftClient/Protocol/Handlers/SocketWrapper.cs +++ b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Sockets; using MinecraftClient.Crypto; @@ -7,7 +7,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Wrapper for handling unencrypted & encrypted socket /// - class SocketWrapper + public class SocketWrapper { readonly TcpClient c; AesCfb8Stream? s; @@ -29,7 +29,7 @@ namespace MinecraftClient.Protocol.Handlers /// Silently dropped connection can only be detected by attempting to read/write data public bool IsConnected() { - return c.Client != null && c.Connected; + return c.Client is not null && c.Connected; } /// @@ -90,6 +90,9 @@ namespace MinecraftClient.Protocol.Handlers /// data to send public void SendDataRAW(byte[] buffer) { + if (!IsConnected()) + throw new SocketException((int)SocketError.NotConnected); + if (encrypted) s!.Write(buffer, 0, buffer.Length); else diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/AttributeModifiersComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/AttributeModifiersComponent.cs new file mode 100644 index 00000000..4741eed0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/AttributeModifiersComponent.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class AttributeModifiersComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfAttributes { get; set; } + public List Attributes { get; set; } = new(); + public bool ShowInTooltip { get; set; } + + public override void Parse(Queue data) + { + NumberOfAttributes = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfAttributes; i++) + Attributes.Add(SubComponentRegistry.ParseSubComponent(SubComponents.Attribute, data)); + + ShowInTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfAttributes)); + + if (Attributes.Count != NumberOfAttributes) + throw new ArgumentNullException($"Can not serialize a AttributeModifiersComponent when the Attributes count != NumberOfAttributes!"); + + foreach (var attribute in Attributes) + data.AddRange(attribute.Serialize()); + + data.AddRange(DataTypes.GetBool(ShowInTooltip)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BannerPatternsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BannerPatternsComponent.cs new file mode 100644 index 00000000..fb2a21a4 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BannerPatternsComponent.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class BannerPatternsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfLayers { get; set; } + public List Layers { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfLayers = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfLayers; i++) + { + var patternType = DataTypes.ReadNextVarInt(data); + Layers.Add(new BannerLayer + { + PatternType = patternType, + AssetId = patternType == 0 ? DataTypes.ReadNextString(data) : null, + TranslationKey = patternType == 0 ? DataTypes.ReadNextString(data) : null, + DyeColor = DataTypes.ReadNextVarInt(data) + }); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfLayers)); + + if (NumberOfLayers > 0) + { + if (NumberOfLayers != Layers.Count) + throw new Exception("Can't serialize BannerPatternsComponent because NumberOfLayers and Layers.Count differ!"); + + foreach (var bannerLayer in Layers) + { + data.AddRange(DataTypes.GetVarInt(bannerLayer.PatternType)); + + if (bannerLayer.PatternType == 0) + { + if (string.IsNullOrEmpty(bannerLayer.AssetId) || string.IsNullOrEmpty(bannerLayer.TranslationKey)) + throw new Exception("Can't serialize BannerPatternsComponent because AssetId or TranslationKey is null/empty!"); + + data.AddRange(DataTypes.GetString(bannerLayer.AssetId)); + data.AddRange(DataTypes.GetString(bannerLayer.TranslationKey)); + } + + data.AddRange(DataTypes.GetVarInt(bannerLayer.DyeColor)); + } + } + + return new Queue(data); + } +} + +public record BannerLayer +{ + public int PatternType { get; set; } + public string? AssetId { get; set; } = null!; + public string? TranslationKey { get; set; } = null!; + public int DyeColor { get; set; } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BaseColorComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BaseColorComponent.cs new file mode 100644 index 00000000..615328d2 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BaseColorComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class BaseColorComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int DyeColor { get; set; } + + public override void Parse(Queue data) + { + DyeColor = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(DyeColor)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BeesComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BeesComponent.cs new file mode 100644 index 00000000..575bfda3 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BeesComponent.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class BeesComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfBees { get; set; } + public List Bees { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfBees = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < NumberOfBees; i++) + { + Bees.Add(new Bee(DataTypes.ReadNextNbt(data), DataTypes.ReadNextVarInt(data), DataTypes.ReadNextVarInt(data))); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfBees)); + + if (NumberOfBees > 0) + { + if (NumberOfBees != Bees.Count) + throw new Exception("Can't serialize the BeeComponent because NumberOfBees and Bees.Count differ!"); + + foreach (var bee in Bees) + { + data.AddRange(DataTypes.GetNbt(bee.EntityDataNbt)); + data.AddRange(DataTypes.GetVarInt(bee.TicksInHive)); + data.AddRange(DataTypes.GetVarInt(bee.MinTicksInHive)); + } + } + + return new Queue(data); + } +} + +public record Bee(Dictionary? EntityDataNbt, int TicksInHive, int MinTicksInHive); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BlockStateComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BlockStateComponent.cs new file mode 100644 index 00000000..8bbeeb2b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BlockStateComponent.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class BlockStateComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List<(string, string)> Properties { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + Properties.Add((DataTypes.ReadNextString(data), DataTypes.ReadNextString(data))); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Properties.Count)); + foreach (var (key, value) in Properties) + { + data.AddRange(DataTypes.GetString(key)); + data.AddRange(DataTypes.GetString(value)); + } + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BundleContentsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BundleContentsComponent.cs new file mode 100644 index 00000000..76e11195 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/BundleContentsComponent.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class BundleContentsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < count; i++) + { + var item = DataTypes.ReadNextItemSlot(data, ItemPalette); + if (item is not null) + Items.Add(item); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + + foreach (var item in Items) + data.AddRange(DataTypes.GetItemSlot(item, ItemPalette)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanBreakComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanBreakComponent.cs new file mode 100644 index 00000000..aeff50f2 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanBreakComponent.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CanBreakComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfPredicates { get; set; } + public List BlockPredicates { get; set; } = new(); + public bool ShowInTooltip { get; set; } + + public override void Parse(Queue data) + { + NumberOfPredicates = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfPredicates; i++) + BlockPredicates.Add((BlockPredicateSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockPredicate, data)); + + ShowInTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfPredicates)); + + if (NumberOfPredicates > 0 && BlockPredicates.Count == 0) + throw new ArgumentNullException($"Can not serialize a CanBreakComponent when the BlockPredicates is empty but NumberOfPredicates is > 0!"); + + foreach (var blockPredicate in BlockPredicates) + data.AddRange(blockPredicate.Serialize()); + + data.AddRange(DataTypes.GetBool(ShowInTooltip)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanPlaceOnComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanPlaceOnComponent.cs new file mode 100644 index 00000000..0134563e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CanPlaceOnComponent.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CanPlaceOnComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfPredicates { get; set; } + public List BlockPredicates { get; set; } = new(); + public bool ShowInTooltip { get; set; } + + public override void Parse(Queue data) + { + NumberOfPredicates = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfPredicates; i++) + BlockPredicates.Add((BlockPredicateSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockPredicate, data)); + + ShowInTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfPredicates)); + + if (NumberOfPredicates > 0 && BlockPredicates.Count == 0) + throw new ArgumentNullException($"Can not serialize a CanPlaceOnComponent when the BlockPredicates is empty but NumberOfPredicates is > 0!"); + + foreach (var blockPredicate in BlockPredicates) + data.AddRange(blockPredicate.Serialize()); + + data.AddRange(DataTypes.GetBool(ShowInTooltip)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ChargedProjectilesComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ChargedProjectilesComponent.cs new file mode 100644 index 00000000..5a5259ad --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ChargedProjectilesComponent.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ChargedProjectilesComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < count; i++) + { + var item = DataTypes.ReadNextItemSlot(data, ItemPalette); + if (item is not null) + Items.Add(item); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + + foreach (var item in Items) + data.AddRange(DataTypes.GetItemSlot(item, ItemPalette)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerComponent.cs new file mode 100644 index 00000000..5264f43d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerComponent.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ContainerComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextItemSlot(data, ItemPalette)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + foreach (var item in Items) + data.AddRange(DataTypes.GetItemSlot(item, ItemPalette)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerLootComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerLootComponent.cs new file mode 100644 index 00000000..8b68ecd7 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ContainerLootComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ContainerLootComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CreativeSlotLockComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CreativeSlotLockComponent.cs new file mode 100644 index 00000000..6d188397 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CreativeSlotLockComponent.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CreativeSlotLockComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomDataComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomDataComponent.cs new file mode 100644 index 00000000..9509d539 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomDataComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CustomDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } = new(); + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent.cs new file mode 100644 index 00000000..20df0870 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CustomModelDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Floats { get; set; } = []; + public List Flags { get; set; } = []; + public List Strings { get; set; } = []; + public List Colors { get; set; } = []; + + public override void Parse(Queue data) + { + Floats = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextFloat(componentData)); + Flags = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextBool(componentData)); + Strings = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextString(componentData)); + Colors = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextInt(componentData)); + } + + public override Queue Serialize() + { + var data = new List(); + WriteList(data, Floats, static (dataTypes, value) => dataTypes.GetFloat(value)); + WriteList(data, Flags, static (dataTypes, value) => dataTypes.GetBool(value)); + WriteList(data, Strings, static (dataTypes, value) => dataTypes.GetString(value)); + WriteList(data, Colors, static (_, value) => DataTypes.GetInt(value)); + return new Queue(data); + } + + private List ReadList(Queue data, ReadDelegate read) + { + var count = DataTypes.ReadNextVarInt(data); + var values = new List(count); + + for (var i = 0; i < count; i++) + values.Add(read(DataTypes, data)); + + return values; + } + + private void WriteList(List data, List values, WriteDelegate write) + { + data.AddRange(DataTypes.GetVarInt(values.Count)); + foreach (var value in values) + data.AddRange(write(DataTypes, value)); + } + + private delegate T ReadDelegate(DataTypes dataTypes, Queue data); + private delegate byte[] WriteDelegate(DataTypes dataTypes, T value); +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent1206.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent1206.cs new file mode 100644 index 00000000..5483b384 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomModelDataComponent1206.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CustomModelDataComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Value { get; set; } + + public override void Parse(Queue data) + { + Value = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + return new Queue(DataTypes.GetVarInt(Value)); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomNameComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomNameComponent.cs new file mode 100644 index 00000000..1104b598 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/CustomNameComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class CustomNameComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string CustomName { get; set; } = string.Empty; + public Dictionary? CustomNameNbt { get; set; } + + public override void Parse(Queue data) + { + CustomNameNbt = DataTypes.ReadNextNbt(data); + CustomName = ChatParser.ParseText(CustomNameNbt); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(CustomNameNbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DamageComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DamageComponent.cs new file mode 100644 index 00000000..c105ee0c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DamageComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class DamageComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Damage { get; set; } + + public override void Parse(Queue data) + { + Damage = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Damage)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DebugStickStateComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DebugStickStateComponent.cs new file mode 100644 index 00000000..a39a352d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DebugStickStateComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class DebugStickStateComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DyeColorComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DyeColorComponent.cs new file mode 100644 index 00000000..d887ecee --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/DyeColorComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class DyeColorComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Color { get; set; } + public bool ShowInTooltip { get; set; } + + public override void Parse(Queue data) + { + Color = DataTypes.ReadNextInt(data); + ShowInTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetInt(Color)); + data.AddRange(DataTypes.GetBool(ShowInTooltip)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentGlintOverrideComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentGlintOverrideComponent.cs new file mode 100644 index 00000000..6528ffb5 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentGlintOverrideComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class EnchantmentGlintOverrideComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HasGlint { get; set; } + + public override void Parse(Queue data) + { + HasGlint = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(HasGlint)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentsComponent.cs new file mode 100644 index 00000000..8322201f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EnchantmentsComponent.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class EnchantmentsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfEnchantments { get; set; } + public List Enchantments { get; set; } = new(); + public bool ShowTooltip { get; set; } + + public override void Parse(Queue data) + { + NumberOfEnchantments = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfEnchantments; i++) + { + var registryId = DataTypes.ReadNextVarInt(data); + var level = DataTypes.ReadNextVarInt(data); + Enchantments.Add(new Enchantment(EnchantmentMapping.GetEnchantmentByRegistryId1206(DataTypes.ProtocolVersion, registryId), level)); + } + + ShowTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Enchantments.Count)); + foreach (var enchantment in Enchantments) + { + data.AddRange(DataTypes.GetVarInt(EnchantmentMapping.GetRegistryId1206ByEnchantment(DataTypes.ProtocolVersion, enchantment.Type))); + data.AddRange(DataTypes.GetVarInt(enchantment.Level)); + } + data.AddRange(DataTypes.GetBool(ShowTooltip)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EntityDataComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EntityDataComponent.cs new file mode 100644 index 00000000..ba31c9f9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/EntityDataComponent.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class EntityDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} + +public class BucketEntityDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EntityDataComponent(dataTypes, itemPalette, subComponentRegistry) +{ } + +public class BlockEntityDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EntityDataComponent(dataTypes, itemPalette, subComponentRegistry) +{ } \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireResistantComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireResistantComponent.cs new file mode 100644 index 00000000..351eb684 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireResistantComponent.cs @@ -0,0 +1,7 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class FireResistantComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworkExplosionComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworkExplosionComponent.cs new file mode 100644 index 00000000..a515f3bb --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworkExplosionComponent.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Mapping; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class FireworkExplosionComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public FireworkExplosionSubComponent? FireworkExplosionSubComponent { get; set; } + + public override void Parse(Queue data) + { + FireworkExplosionSubComponent = (FireworkExplosionSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.FireworkExplosion, data); + } + + public override Queue Serialize() + { + return FireworkExplosionSubComponent!.Serialize(); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworksComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworksComponent.cs new file mode 100644 index 00000000..db73c815 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FireworksComponent.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Mapping; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class FireworksComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int FlightDuration { get; set; } + public int NumberOfExplosions { get; set; } + + public List Explosions { get; set; } = []; + + public override void Parse(Queue data) + { + FlightDuration = DataTypes.ReadNextVarInt(data); + NumberOfExplosions = DataTypes.ReadNextVarInt(data); + + if (NumberOfExplosions > 0) + { + for (var i = 0; i < NumberOfExplosions; i++) + Explosions.Add( + (FireworkExplosionSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.FireworkExplosion, + data)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(FlightDuration)); + data.AddRange(DataTypes.GetVarInt(NumberOfExplosions)); + if (NumberOfExplosions > 0) + { + if (NumberOfExplosions != Explosions.Count) + throw new Exception("Can't serialize FireworksComponent because NumberOfExplosions and the lenght of Explosions differ!"); + + foreach (var explosion in Explosions) + data.AddRange(explosion.Serialize().ToList()); + } + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FoodComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FoodComponent.cs new file mode 100644 index 00000000..f8679473 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FoodComponent.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class FoodComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Nutrition { get; set; } + public float Saturation { get; set; } + public bool CanAlwaysEat { get; set; } + public float SecondsToEat { get; set; } + public List Effects { get; set; } = new(); + + public override void Parse(Queue data) + { + Nutrition = DataTypes.ReadNextVarInt(data); + Saturation = DataTypes.ReadNextFloat(data); + CanAlwaysEat = DataTypes.ReadNextBool(data); + SecondsToEat = DataTypes.ReadNextFloat(data); + var numberOfEffects = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < numberOfEffects; i++) + Effects.Add((EffectSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Effect, data)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Nutrition)); + data.AddRange(DataTypes.GetFloat(Saturation)); + data.AddRange(DataTypes.GetBool(CanAlwaysEat)); + data.AddRange(DataTypes.GetFloat(SecondsToEat)); + data.AddRange(DataTypes.GetVarInt(Effects.Count)); + + foreach (var effect in Effects) + data.AddRange(effect.Serialize()); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideAdditionalTooltipComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideAdditionalTooltipComponent.cs new file mode 100644 index 00000000..9f659b9f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideAdditionalTooltipComponent.cs @@ -0,0 +1,7 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class HideAdditionalTooltipComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideTooltipComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideTooltipComponent.cs new file mode 100644 index 00000000..77cdc7fa --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/HideTooltipComponent.cs @@ -0,0 +1,7 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class HideTooltipComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/InstrumentComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/InstrumentComponent.cs new file mode 100644 index 00000000..87692b70 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/InstrumentComponent.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class InstrumentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + // holder ID: 0 = inline instrument data, N>0 = registry reference (id = N-1) + public int InstrumentHolderId { get; set; } + + // Inline instrument fields (only when InstrumentHolderId == 0): + // holder ID for SoundEvent: 0 = inline sound, N>0 = registry reference (id = N-1) + public int SoundEventHolderId { get; set; } + // Inline SoundEvent fields (only when SoundEventHolderId == 0): + public string? SoundLocation { get; set; } + public bool HasFixedRange { get; set; } + public float FixedRange { get; set; } + + public int UseDuration { get; set; } + public float Range { get; set; } + + public override void Parse(Queue data) + { + InstrumentHolderId = DataTypes.ReadNextVarInt(data); + + if (InstrumentHolderId == 0) + { + SoundEventHolderId = DataTypes.ReadNextVarInt(data); + + if (SoundEventHolderId == 0) + { + SoundLocation = DataTypes.ReadNextString(data); + HasFixedRange = DataTypes.ReadNextBool(data); + if (HasFixedRange) + FixedRange = DataTypes.ReadNextFloat(data); + } + + UseDuration = DataTypes.ReadNextVarInt(data); + Range = DataTypes.ReadNextFloat(data); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(InstrumentHolderId)); + + if (InstrumentHolderId == 0) + { + data.AddRange(DataTypes.GetVarInt(SoundEventHolderId)); + + if (SoundEventHolderId == 0) + { + data.AddRange(DataTypes.GetString(SoundLocation ?? "")); + data.AddRange(DataTypes.GetBool(HasFixedRange)); + if (HasFixedRange) + data.AddRange(DataTypes.GetFloat(FixedRange)); + } + + data.AddRange(DataTypes.GetVarInt(UseDuration)); + data.AddRange(DataTypes.GetFloat(Range)); + } + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/IntangibleProjectileComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/IntangibleProjectileComponent.cs new file mode 100644 index 00000000..b6d24a5e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/IntangibleProjectileComponent.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class IntangibleProjectileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry) +{ +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ItemNameComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ItemNameComponent.cs new file mode 100644 index 00000000..4b42c779 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ItemNameComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ItemNameComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string ItemName { get; set; } = string.Empty; + public Dictionary? ItemNameNbt { get; set; } + + public override void Parse(Queue data) + { + ItemNameNbt = DataTypes.ReadNextNbt(data); + ItemName = ChatParser.ParseText(ItemNameNbt); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(ItemNameNbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LockComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LockComponent.cs new file mode 100644 index 00000000..85d050e2 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LockComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class LockComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LodestoneTrackerComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LodestoneTrackerComponent.cs new file mode 100644 index 00000000..f3353755 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LodestoneTrackerComponent.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Mapping; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class LodestoneTrackerComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HasGlobalPosition { get; set; } + public string Dimension { get; set; } = null!; + public Location Position { get; set; } + public bool Tracked { get; set; } + + public override void Parse(Queue data) + { + HasGlobalPosition = DataTypes.ReadNextBool(data); + + if (HasGlobalPosition) + { + Dimension = DataTypes.ReadNextString(data); + Position = DataTypes.ReadNextLocation(data); + } + + Tracked = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(HasGlobalPosition)); + + if (HasGlobalPosition) + { + data.AddRange(DataTypes.GetString(Dimension)); + data.AddRange(DataTypes.GetLocation(Position)); + } + + data.AddRange(DataTypes.GetBool(Tracked)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LoreComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LoreComponent.cs new file mode 100644 index 00000000..d8cb5161 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/LoreComponent.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class LoreNameComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfLines { get; set; } + public List Lines { get; set; } = []; + public List> LinesNbt { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfLines = DataTypes.ReadNextVarInt(data); + + if (NumberOfLines <= 0) return; + + for (var i = 0; i < NumberOfLines; i++) + { + var lineNbt = DataTypes.ReadNextNbt(data); + LinesNbt.Add(lineNbt); + Lines.Add(ChatParser.ParseText(lineNbt)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(LinesNbt.Count)); + + foreach (var lineNbt in LinesNbt) + data.AddRange(DataTypes.GetNbt(lineNbt)); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapColorComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapColorComponent.cs new file mode 100644 index 00000000..457e74b5 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapColorComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MapColorComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Id { get; set; } + + public override void Parse(Queue data) + { + Id = DataTypes.ReadNextInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetInt(Id)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapDecorationsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapDecorationsComponent.cs new file mode 100644 index 00000000..70b8b28d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapDecorationsComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MapDecorationsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } = new(); + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapIdComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapIdComponent.cs new file mode 100644 index 00000000..712b721f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapIdComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MapIdComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Id { get; set; } + + public override void Parse(Queue data) + { + Id = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Id)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapPostProcessingComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapPostProcessingComponent.cs new file mode 100644 index 00000000..c7d58700 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MapPostProcessingComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MapPostProcessingComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Type { get; set; } + + public override void Parse(Queue data) + { + Type = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Type)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxDamageComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxDamageComponent.cs new file mode 100644 index 00000000..45e8545b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxDamageComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MaxDamageComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int MaxDamage { get; set; } + + public override void Parse(Queue data) + { + MaxDamage = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(MaxDamage)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxStackSizeComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxStackSizeComponent.cs new file mode 100644 index 00000000..32da4c9f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/MaxStackSizeComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class MaxStackSizeComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int MaxStackSize { get; set; } + + public override void Parse(Queue data) + { + MaxStackSize = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(MaxStackSize)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/NoteBlockSoundComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/NoteBlockSoundComponent.cs new file mode 100644 index 00000000..5d7c0792 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/NoteBlockSoundComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class NoteBlockSoundComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string Identifier { get; set; } = null!; + + public override void Parse(Queue data) + { + Identifier = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetString(Identifier)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/OminousBottleAmplifierComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/OminousBottleAmplifierComponent.cs new file mode 100644 index 00000000..ed92d953 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/OminousBottleAmplifierComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class OminousBottleAmplifierComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Amplifier { get; set; } + + public override void Parse(Queue data) + { + Amplifier = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Amplifier)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotDecorationsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotDecorationsComponent.cs new file mode 100644 index 00000000..97b91827 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotDecorationsComponent.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class PotDecorationsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextVarInt(data)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + foreach (var item in Items) + data.AddRange(DataTypes.GetVarInt(item)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotionContentsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotionContentsComponent.cs new file mode 100644 index 00000000..b56e6897 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/PotionContentsComponent.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class PotionContentsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HasPotionId { get; set; } + public int PotionId { get; set; } + public bool HasCustomColor { get; set; } + public int CustomColor { get; set; } + public List Effects { get; set; } = new(); + + public override void Parse(Queue data) + { + HasPotionId = DataTypes.ReadNextBool(data); + if (HasPotionId) + PotionId = DataTypes.ReadNextVarInt(data); + + HasCustomColor = DataTypes.ReadNextBool(data); + if (HasCustomColor) + CustomColor = DataTypes.ReadNextInt(data); + + var numberOfEffects = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < numberOfEffects; i++) + Effects.Add((PotionEffectSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.PotionEffect, data)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(HasPotionId)); + if (HasPotionId) + data.AddRange(DataTypes.GetVarInt(PotionId)); + + data.AddRange(DataTypes.GetBool(HasCustomColor)); + if (HasCustomColor) + data.AddRange(DataTypes.GetInt(CustomColor)); + + data.AddRange(DataTypes.GetVarInt(Effects.Count)); + foreach (var effect in Effects) + data.AddRange(effect.Serialize()); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs new file mode 100644 index 00000000..23cc92bf --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ProfileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HasName { get; set; } + public string? Name { get; set; } = null!; + public bool HasUniqueId { get; set; } + public Guid Uuid { get; set; } + public int NumberOfProperties { get; set; } + public List ProfileProperties { get; set; } = []; + public bool IsFullProfile { get; set; } + public string? BodyAssetId { get; set; } + public string? CapeAssetId { get; set; } + public string? ElytraAssetId { get; set; } + public ProfileSkinModel? Model { get; set; } + + public override void Parse(Queue data) + { + ResetState(); + + if (DataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version) + { + ParseResolvableProfile(data); + return; + } + + ParseLegacyProfile(data); + } + + public override Queue Serialize() + { + return DataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version + ? SerializeResolvableProfile() + : SerializeLegacyProfile(); + } + + private void ResetState() + { + HasName = false; + Name = null; + HasUniqueId = false; + Uuid = Guid.Empty; + NumberOfProperties = 0; + ProfileProperties = []; + IsFullProfile = false; + BodyAssetId = null; + CapeAssetId = null; + ElytraAssetId = null; + Model = null; + } + + private void ParseLegacyProfile(Queue data) + { + HasName = DataTypes.ReadNextBool(data); + + if (HasName) + Name = DataTypes.ReadNextString(data); + + HasUniqueId = DataTypes.ReadNextBool(data); + + if (HasUniqueId) + Uuid = DataTypes.ReadNextUUID(data); + + NumberOfProperties = DataTypes.ReadNextVarInt(data); + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); + } + + private void ParseResolvableProfile(Queue data) + { + IsFullProfile = DataTypes.ReadNextBool(data); + + if (IsFullProfile) + { + HasUniqueId = true; + Uuid = DataTypes.ReadNextUUID(data); + HasName = true; + Name = DataTypes.ReadNextString(data); + NumberOfProperties = DataTypes.ReadNextVarInt(data); + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); + } + else + { + HasName = DataTypes.ReadNextBool(data); + if (HasName) + Name = DataTypes.ReadNextString(data); + + HasUniqueId = DataTypes.ReadNextBool(data); + if (HasUniqueId) + Uuid = DataTypes.ReadNextUUID(data); + + NumberOfProperties = DataTypes.ReadNextVarInt(data); + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); + } + + BodyAssetId = ReadOptionalResourceLocation(data); + CapeAssetId = ReadOptionalResourceLocation(data); + ElytraAssetId = ReadOptionalResourceLocation(data); + + if (DataTypes.ReadNextBool(data)) + Model = DataTypes.ReadNextBool(data) ? ProfileSkinModel.Slim : ProfileSkinModel.Wide; + } + + private Queue SerializeLegacyProfile() + { + var data = new List(); + NumberOfProperties = ProfileProperties.Count; + + data.AddRange(DataTypes.GetBool(HasName)); + if (HasName) + data.AddRange(DataTypes.GetString(Name ?? "")); + + data.AddRange(DataTypes.GetBool(HasUniqueId)); + if (HasUniqueId) + data.AddRange(DataTypes.GetUUID(Uuid)); + + data.AddRange(DataTypes.GetVarInt(NumberOfProperties)); + SerializeProfileProperties(data); + + return new Queue(data); + } + + private Queue SerializeResolvableProfile() + { + var data = new List(); + NumberOfProperties = ProfileProperties.Count; + + data.AddRange(DataTypes.GetBool(IsFullProfile)); + if (IsFullProfile) + { + if (!HasUniqueId) + throw new NullReferenceException("Can't serialize the ProfileComponent because a full profile requires a UUID!"); + + data.AddRange(DataTypes.GetUUID(Uuid)); + data.AddRange(DataTypes.GetString(Name ?? "")); + } + else + { + data.AddRange(DataTypes.GetBool(HasName)); + if (HasName) + data.AddRange(DataTypes.GetString(Name ?? "")); + + data.AddRange(DataTypes.GetBool(HasUniqueId)); + if (HasUniqueId) + data.AddRange(DataTypes.GetUUID(Uuid)); + } + + data.AddRange(DataTypes.GetVarInt(NumberOfProperties)); + SerializeProfileProperties(data); + + SerializeOptionalResourceLocation(data, BodyAssetId); + SerializeOptionalResourceLocation(data, CapeAssetId); + SerializeOptionalResourceLocation(data, ElytraAssetId); + + data.AddRange(DataTypes.GetBool(Model.HasValue)); + if (Model.HasValue) + data.AddRange(DataTypes.GetBool(Model.Value == ProfileSkinModel.Slim)); + + return new Queue(data); + } + + private List ReadProfileProperties(Queue data, int count) + { + var properties = new List(count); + for (var i = 0; i < count; i++) + { + var propertyName = DataTypes.ReadNextString(data); + var propertyValue = DataTypes.ReadNextString(data); + var hasSignature = DataTypes.ReadNextBool(data); + var signature = hasSignature ? DataTypes.ReadNextString(data) : null; + + properties.Add(new ProfileProperty(propertyName, propertyValue, hasSignature, signature)); + } + + return properties; + } + + private void SerializeProfileProperties(List data) + { + foreach (var profileProperty in ProfileProperties) + { + data.AddRange(DataTypes.GetString(profileProperty.Name)); + data.AddRange(DataTypes.GetString(profileProperty.Value)); + data.AddRange(DataTypes.GetBool(profileProperty.HasSignature)); + if (!profileProperty.HasSignature) + continue; + + if (string.IsNullOrEmpty(profileProperty.Signature)) + throw new NullReferenceException("Can't serialize the ProfileComponent because HasSignature is true, but the Signature is null/empty!"); + + data.AddRange(DataTypes.GetString(profileProperty.Signature)); + } + } + + private string? ReadOptionalResourceLocation(Queue data) + { + return DataTypes.ReadNextBool(data) ? DataTypes.ReadNextString(data) : null; + } + + private void SerializeOptionalResourceLocation(List data, string? resourceLocation) + { + data.AddRange(DataTypes.GetBool(!string.IsNullOrEmpty(resourceLocation))); + if (!string.IsNullOrEmpty(resourceLocation)) + data.AddRange(DataTypes.GetString(resourceLocation)); + } +} + +public record ProfileProperty(string Name, string Value, bool HasSignature, string? Signature); + +public enum ProfileSkinModel +{ + Wide, + Slim +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RarityComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RarityComponent.cs new file mode 100644 index 00000000..cbe3b9e9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RarityComponent.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class RarityComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public ItemRarity Rarity { get; set; } + + public override void Parse(Queue data) + { + Rarity = (ItemRarity)DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt((int)Rarity)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RecipesComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RecipesComponent.cs new file mode 100644 index 00000000..9df0e22b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RecipesComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class RecipesComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RepairCostComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RepairCostComponent.cs new file mode 100644 index 00000000..951f14f5 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/RepairCostComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class RepairCostComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Cost { get; set; } + + public override void Parse(Queue data) + { + Cost = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Cost)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/StoredEnchantmentsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/StoredEnchantmentsComponent.cs new file mode 100644 index 00000000..a741d54d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/StoredEnchantmentsComponent.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class StoredEnchantmentsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EnchantmentsComponent(dataTypes, itemPalette, subComponentRegistry); \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/SuspiciousStewEffectsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/SuspiciousStewEffectsComponent.cs new file mode 100644 index 00000000..b6518820 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/SuspiciousStewEffectsComponent.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class SuspiciousStewEffectsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfEffects { get; set; } + public List Effects { get; set; } = new(); + + public override void Parse(Queue data) + { + NumberOfEffects = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfEffects; i++) + Effects.Add(new SuspiciousStewEffect(DataTypes.ReadNextVarInt(data), DataTypes.ReadNextVarInt(data))); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfEffects)); + + if (NumberOfEffects != Effects.Count) + throw new InvalidOperationException("Can not serialize SuspiciousStewEffectsComponent1206 because umberOfEffects != Effects.Count!"); + + foreach (var effect in Effects) + { + data.AddRange(DataTypes.GetVarInt(effect.TypeId)); + data.AddRange(DataTypes.GetVarInt(effect.Duration)); + } + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ToolComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ToolComponent.cs new file mode 100644 index 00000000..2d761739 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ToolComponent.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class ToolComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfRules { get; set; } + public List Rules { get; set; } = new(); + public float DefaultMiningSpeed { get; set; } + public int DamagePerBlock { get; set; } + + public override void Parse(Queue data) + { + NumberOfRules = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfRules; i++) + Rules.Add((RuleSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Rule, data)); + + DefaultMiningSpeed = DataTypes.ReadNextFloat(data); + DamagePerBlock = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfRules)); + + if (Rules.Count != NumberOfRules) + throw new ArgumentNullException($"Can not serialize a ToolComponent1206 when the Rules count != NumberOfRules!"); + + foreach (var rule in Rules) + data.AddRange(rule.Serialize()); + + data.AddRange(DataTypes.GetFloat(DefaultMiningSpeed)); + data.AddRange(DataTypes.GetVarInt(DamagePerBlock)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/TrimComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/TrimComponent.cs new file mode 100644 index 00000000..90b79127 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/TrimComponent.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class TrimComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int TrimMaterialType { get; set; } + public string AssetName { get; set; } = null!; + public int Ingredient { get; set; } + public float ItemModelIndex { get; set; } + public int NumberOfOverrides { get; set; } + public List? Overrides { get; set; } + public Dictionary? DescriptionNbt { get; set; } + public string Description { get; set; } = null!; + public int TrimPatternType { get; set; } + public string TrimPatternTypeAssetName { get; set; } = null!; + public int TemplateItem { get; set; } + public Dictionary? TrimPatternTypeDescriptionNbt { get; set; } + public string TrimPatternTypeDescription { get; set; } = null!; + public bool Decal { get; set; } + public bool ShowInTooltip { get; set; } + + public override void Parse(Queue data) + { + TrimMaterialType = DataTypes.ReadNextVarInt(data); + + if (TrimMaterialType == 0) + { + AssetName = DataTypes.ReadNextString(data); + Ingredient = DataTypes.ReadNextVarInt(data); + ItemModelIndex = DataTypes.ReadNextFloat(data); + NumberOfOverrides = DataTypes.ReadNextVarInt(data); + + if (NumberOfOverrides > 0) + { + Overrides = []; + + for (var i = 0; i < NumberOfOverrides; i++) + Overrides.Add(new TrimAssetOverride(DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextString(data))); + } + + DescriptionNbt = DataTypes.ReadNextNbt(data); + Description = ChatParser.ParseText(DescriptionNbt); + } + + TrimPatternType = DataTypes.ReadNextVarInt(data); + + if (TrimPatternType == 0) + { + TrimPatternTypeAssetName = DataTypes.ReadNextString(data); + TemplateItem = DataTypes.ReadNextVarInt(data); + TrimPatternTypeDescriptionNbt = DataTypes.ReadNextNbt(data); + TrimPatternTypeDescription = ChatParser.ParseText(TrimPatternTypeDescriptionNbt); + Decal = DataTypes.ReadNextBool(data); + } + + ShowInTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + + data.AddRange(DataTypes.GetVarInt(TrimMaterialType)); + + if (TrimMaterialType == 0) + { + if (string.IsNullOrEmpty(AssetName)) + throw new NullReferenceException("Can't serialize the TrimComponent because the Asset Name is null!"); + + data.AddRange(DataTypes.GetString(AssetName)); + data.AddRange(DataTypes.GetVarInt(Ingredient)); + data.AddRange(DataTypes.GetFloat(ItemModelIndex)); + data.AddRange(DataTypes.GetVarInt(NumberOfOverrides)); + if (NumberOfOverrides > 0) + { + if (NumberOfOverrides != Overrides?.Count) + throw new NullReferenceException("Can't serialize the TrimComponent because value of NumberOfOverrides and the size of Overrides don't match!"); + + foreach (var (armorMaterialType, assetName) in Overrides) + { + data.AddRange(DataTypes.GetVarInt(armorMaterialType)); + data.AddRange(DataTypes.GetString(assetName)); + } + } + data.AddRange(DataTypes.GetNbt(DescriptionNbt)); + } + + data.AddRange(DataTypes.GetVarInt(TrimPatternType)); + if (TrimPatternType == 0) + { + if (string.IsNullOrEmpty(TrimPatternTypeAssetName)) + throw new NullReferenceException("Can't serialize the TrimComponent because the TrimPatternTypeAssetName is null!"); + + data.AddRange(DataTypes.GetString(TrimPatternTypeAssetName)); + data.AddRange(DataTypes.GetVarInt(TemplateItem)); + data.AddRange(DataTypes.GetNbt(TrimPatternTypeDescriptionNbt)); + data.AddRange(DataTypes.GetBool(Decal)); + } + + data.AddRange(DataTypes.GetBool(ShowInTooltip)); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/UnbreakableComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/UnbreakableComponent.cs new file mode 100644 index 00000000..b4f354f6 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/UnbreakableComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class UnbreakableComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool Unbreakable { get; set; } + + public override void Parse(Queue data) + { + Unbreakable = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(Unbreakable)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WritableBookContentComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WritableBookContentComponent.cs new file mode 100644 index 00000000..59dc0409 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WritableBookContentComponent.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class WritableBookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Pages { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < count; i++) + { + var rawContent = DataTypes.ReadNextString(data); + var hasFilteredContent = DataTypes.ReadNextBool(data); + var filteredContent = null as string; + + if (hasFilteredContent) + filteredContent = DataTypes.ReadNextString(data); + + Pages.Add(new BookPage(rawContent, hasFilteredContent, filteredContent)); + } + } + + public override Queue Serialize() + { + var data = new List(); + + data.AddRange(DataTypes.GetVarInt(Pages.Count)); + + foreach (var page in Pages) + { + data.AddRange(DataTypes.GetString(page.RawContent)); + data.AddRange(DataTypes.GetBool(page.HasFilteredContent)); + + if (page.HasFilteredContent) + { + if (page.FilteredContent is null) + throw new InvalidOperationException("Can not serialize WritableBookContentComponent because page.HasFilteredContent = true, but FilteredContent is null!"); + + data.AddRange(DataTypes.GetString(page.FilteredContent)); + } + } + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WrittenBookContentComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WrittenBookContentComponent.cs new file mode 100644 index 00000000..413c94d8 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WrittenBookContentComponent.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +public class WrittenBookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string RawTitle { get; set; } = null!; + public bool HasFilteredTitle { get; set; } + public string? FilteredTitle { get; set; } + public string Author { get; set; } = null!; + public int Generation { get; set; } + public int NumberOfPages { get; set; } + public List Pages { get; set; } = []; + public bool Resolved { get; set; } + + public override void Parse(Queue data) + { + RawTitle = DataTypes.ReadNextString(data); + HasFilteredTitle = DataTypes.ReadNextBool(data); + + if (HasFilteredTitle) + FilteredTitle = DataTypes.ReadNextString(data); + + Author = DataTypes.ReadNextString(data); + Generation = DataTypes.ReadNextVarInt(data); + NumberOfPages = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfPages; i++) + { + var (rawContent, rawContentNbt) = ReadPageComponent(data); + var hasFilteredContent = DataTypes.ReadNextBool(data); + Dictionary? filteredContentNbt = null; + string? filteredContent = null; + + if (hasFilteredContent) + (filteredContent, filteredContentNbt) = ReadPageComponent(data); + + Pages.Add(new BookPage(rawContent, hasFilteredContent, filteredContent, rawContentNbt, filteredContentNbt)); + } + + Resolved = DataTypes.ReadNextBool(data); + } + + private (string Content, Dictionary Nbt) ReadPageComponent(Queue data) + { + // Hypixel sent page payloads in the string-shaped form on this structured-book path, + // so keep the parser tolerant while still preserving the raw data for serialization. + Queue fallbackData = new(data); + + try + { + var nbt = DataTypes.ReadNextNbt(data); + return (ChatParser.ParseText(nbt), nbt); + } + catch (System.IO.InvalidDataException) + { + data.Clear(); + foreach (var b in fallbackData) + data.Enqueue(b); + + var json = DataTypes.ReadNextString(data); + return (ChatParser.ParseText(json), new Dictionary { [""] = json }); + } + } + + public override Queue Serialize() + { + var data = new List(); + + data.AddRange(DataTypes.GetString(RawTitle)); + data.AddRange(DataTypes.GetBool(HasFilteredTitle)); + + if (HasFilteredTitle) + { + if (FilteredTitle is null) + throw new InvalidOperationException("Can not serialize WrittenBookContentComponent because HasFilteredTitle is true but FilteredTitle is null!"); + + data.AddRange(DataTypes.GetString(FilteredTitle)); + } + + data.AddRange(DataTypes.GetString(Author)); + data.AddRange(DataTypes.GetVarInt(Generation)); + data.AddRange(DataTypes.GetVarInt(Pages.Count)); + + foreach (var page in Pages) + { + data.AddRange(DataTypes.GetNbt(page.RawContentNbt)); + data.AddRange(DataTypes.GetBool(page.HasFilteredContent)); + + if (page.HasFilteredContent) + data.AddRange(DataTypes.GetNbt(page.FilteredContentNbt)); + } + data.AddRange(DataTypes.GetBool(Resolved)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21/JukeBoxPlayableComponent121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21/JukeBoxPlayableComponent121.cs new file mode 100644 index 00000000..0b4266fd --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21/JukeBoxPlayableComponent121.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; + +public class JukeBoxPlayableComponent121(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool IsHolder { get; set; } + public int HolderId { get; set; } + public string? ResourceKey { get; set; } + public SoundEventSubComponent? SoundEvent { get; set; } + public Dictionary? DescriptionNbt { get; set; } + public string Description { get; set; } = string.Empty; + public float Duration { get; set; } + public int ComparatorOutput { get; set; } + public bool ShowTooltip { get; set; } + + public override void Parse(Queue data) + { + IsHolder = DataTypes.ReadNextBool(data); + + if (IsHolder) + { + HolderId = DataTypes.ReadNextVarInt(data); + if (HolderId == 0) + { + SoundEvent = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + DescriptionNbt = DataTypes.ReadNextNbt(data); + Description = ChatParser.ParseText(DescriptionNbt); + Duration = DataTypes.ReadNextFloat(data); + ComparatorOutput = DataTypes.ReadNextVarInt(data); + } + } + else + { + ResourceKey = DataTypes.ReadNextString(data); + } + + ShowTooltip = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(IsHolder)); + + if (IsHolder) + { + data.AddRange(DataTypes.GetVarInt(HolderId)); + if (HolderId == 0) + { + if (SoundEvent is null) + throw new ArgumentNullException(nameof(SoundEvent), "Inline jukebox song requires a sound event."); + + if (DescriptionNbt is null) + throw new ArgumentNullException(nameof(DescriptionNbt), "Inline jukebox song requires a description."); + + data.AddRange(SoundEvent.Serialize()); + data.AddRange(DataTypes.GetNbt(DescriptionNbt)); + data.AddRange(DataTypes.GetFloat(Duration)); + data.AddRange(DataTypes.GetVarInt(ComparatorOutput)); + } + } + else + { + if (string.IsNullOrEmpty(ResourceKey)) + throw new ArgumentNullException(nameof(ResourceKey), "Resource key is required for key-backed jukebox songs."); + + data.AddRange(DataTypes.GetString(ResourceKey)); + } + + data.AddRange(DataTypes.GetBool(ShowTooltip)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/AttackRangeComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/AttackRangeComponent.cs new file mode 100644 index 00000000..f06bff42 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/AttackRangeComponent.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +public class AttackRangeComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public float MinRange { get; set; } + public float MaxRange { get; set; } + public float MinCreativeRange { get; set; } + public float MaxCreativeRange { get; set; } + public float HitboxMargin { get; set; } + public float MobFactor { get; set; } + + public override void Parse(Queue data) + { + MinRange = DataTypes.ReadNextFloat(data); + MaxRange = DataTypes.ReadNextFloat(data); + MinCreativeRange = DataTypes.ReadNextFloat(data); + MaxCreativeRange = DataTypes.ReadNextFloat(data); + HitboxMargin = DataTypes.ReadNextFloat(data); + MobFactor = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetFloat(MinRange)); + bytes.AddRange(DataTypes.GetFloat(MaxRange)); + bytes.AddRange(DataTypes.GetFloat(MinCreativeRange)); + bytes.AddRange(DataTypes.GetFloat(MaxCreativeRange)); + bytes.AddRange(DataTypes.GetFloat(HitboxMargin)); + bytes.AddRange(DataTypes.GetFloat(MobFactor)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/KineticWeaponComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/KineticWeaponComponent.cs new file mode 100644 index 00000000..4d676f51 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/KineticWeaponComponent.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +public class KineticWeaponComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int ContactCooldownTicks { get; set; } + public int DelayTicks { get; set; } + public KineticWeaponConditionData? DismountConditions { get; set; } + public KineticWeaponConditionData? KnockbackConditions { get; set; } + public KineticWeaponConditionData? DamageConditions { get; set; } + public float ForwardMovement { get; set; } + public float DamageMultiplier { get; set; } + public SoundEventHolderData? Sound { get; set; } + public SoundEventHolderData? HitSound { get; set; } + + public override void Parse(Queue data) + { + ContactCooldownTicks = DataTypes.ReadNextVarInt(data); + DelayTicks = DataTypes.ReadNextVarInt(data); + DismountConditions = ReadOptionalCondition(data); + KnockbackConditions = ReadOptionalCondition(data); + DamageConditions = ReadOptionalCondition(data); + ForwardMovement = DataTypes.ReadNextFloat(data); + DamageMultiplier = DataTypes.ReadNextFloat(data); + Sound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + HitSound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + } + + private KineticWeaponConditionData? ReadOptionalCondition(Queue data) + { + if (!DataTypes.ReadNextBool(data)) + return null; + + return new KineticWeaponConditionData( + DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextFloat(data), + DataTypes.ReadNextFloat(data)); + } + + private void WriteOptionalCondition(List data, KineticWeaponConditionData? condition) + { + data.AddRange(DataTypes.GetBool(condition is not null)); + if (condition is null) + return; + + data.AddRange(DataTypes.GetVarInt(condition.MaxDurationTicks)); + data.AddRange(DataTypes.GetFloat(condition.MinSpeed)); + data.AddRange(DataTypes.GetFloat(condition.MinRelativeSpeed)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(ContactCooldownTicks)); + data.AddRange(DataTypes.GetVarInt(DelayTicks)); + WriteOptionalCondition(data, DismountConditions); + WriteOptionalCondition(data, KnockbackConditions); + WriteOptionalCondition(data, DamageConditions); + data.AddRange(DataTypes.GetFloat(ForwardMovement)); + data.AddRange(DataTypes.GetFloat(DamageMultiplier)); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, Sound); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, HitSound); + return new Queue(data); + } +} + +public sealed record KineticWeaponConditionData(int MaxDurationTicks, float MinSpeed, float MinRelativeSpeed); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/PiercingWeaponComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/PiercingWeaponComponent.cs new file mode 100644 index 00000000..a6af8bef --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/PiercingWeaponComponent.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +public class PiercingWeaponComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool DealsKnockback { get; set; } + public bool Dismounts { get; set; } + public SoundEventHolderData? Sound { get; set; } + public SoundEventHolderData? HitSound { get; set; } + + public override void Parse(Queue data) + { + DealsKnockback = DataTypes.ReadNextBool(data); + Dismounts = DataTypes.ReadNextBool(data); + Sound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + HitSound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(DealsKnockback)); + data.AddRange(DataTypes.GetBool(Dismounts)); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, Sound); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, HitSound); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/RegistryEitherHolderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/RegistryEitherHolderComponent.cs new file mode 100644 index 00000000..6d786f8c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/RegistryEitherHolderComponent.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +/// +/// EitherHolder backed by holderRegistry (VarInt = raw registry ID, 0 is valid). +/// Used for DamageType and ZombieNautilusVariant where the holder codec is holderRegistry(), +/// unlike the holder() codec used in SoundEvent (where 0 means inline). +/// +public class RegistryEitherHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool IsHolder { get; set; } + public int HolderId { get; set; } + public string? ResourceKey { get; set; } + + public override void Parse(Queue data) + { + IsHolder = DataTypes.ReadNextBool(data); + if (IsHolder) + HolderId = DataTypes.ReadNextVarInt(data); + else + ResourceKey = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetBool(IsHolder)); + if (IsHolder) + bytes.AddRange(DataTypes.GetVarInt(HolderId)); + else + bytes.AddRange(DataTypes.GetString(ResourceKey ?? "")); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/SwingAnimationComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/SwingAnimationComponent.cs new file mode 100644 index 00000000..3ce47920 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/SwingAnimationComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +public class SwingAnimationComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int AnimationType { get; set; } + public int Duration { get; set; } + + public override void Parse(Queue data) + { + AnimationType = DataTypes.ReadNextVarInt(data); + Duration = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(AnimationType)); + bytes.AddRange(DataTypes.GetVarInt(Duration)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/UseEffectsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/UseEffectsComponent.cs new file mode 100644 index 00000000..a4b72a9f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_11/UseEffectsComponent.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; + +public class UseEffectsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool CanSprint { get; set; } + public bool InteractVibrations { get; set; } + public float SpeedMultiplier { get; set; } + + public override void Parse(Queue data) + { + CanSprint = DataTypes.ReadNextBool(data); + InteractVibrations = DataTypes.ReadNextBool(data); + SpeedMultiplier = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetBool(CanSprint)); + bytes.AddRange(DataTypes.GetBool(InteractVibrations)); + bytes.AddRange(DataTypes.GetFloat(SpeedMultiplier)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ConsumableComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ConsumableComponent.cs new file mode 100644 index 00000000..ac3c34ea --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ConsumableComponent.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class ConsumableComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public float ConsumeSeconds { get; set; } + public int Animation { get; set; } + public SoundEventSubComponent? Sound { get; set; } + public bool HasConsumeParticles { get; set; } + public List Effects { get; set; } = new(); + + public override void Parse(Queue data) + { + ConsumeSeconds = DataTypes.ReadNextFloat(data); + Animation = DataTypes.ReadNextVarInt(data); + Sound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + HasConsumeParticles = DataTypes.ReadNextBool(data); + + var effectCount = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < effectCount; i++) + { + var effectTypeId = DataTypes.ReadNextVarInt(data); + var effectData = ReadConsumeEffectPayload(effectTypeId, data); + Effects.Add(new ConsumeEffectData(effectTypeId, effectData)); + } + } + + private byte[] ReadConsumeEffectPayload(int effectTypeId, Queue data) + { + var payload = new List(); + switch (effectTypeId) + { + case 0: // apply_effects: List + probability(float) + var effectCount = DataTypes.ReadNextVarInt(data); + payload.AddRange(DataTypes.GetVarInt(effectCount)); + for (var i = 0; i < effectCount; i++) + payload.AddRange(ReadMobEffectInstance(data)); + payload.AddRange(DataTypes.GetFloat(DataTypes.ReadNextFloat(data))); + break; + case 1: // remove_effects: HolderSet + payload.AddRange(ReadHolderSet(data)); + break; + case 2: // clear_all_effects: empty + break; + case 3: // teleport_randomly: float diameter + payload.AddRange(DataTypes.GetFloat(DataTypes.ReadNextFloat(data))); + break; + case 4: // play_sound: Holder + var sound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + payload.AddRange(sound.Serialize()); + break; + } + return payload.ToArray(); + } + + private byte[] ReadMobEffectInstance(Queue data) + { + var result = new List(); + var effectId = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(effectId)); + result.AddRange(ReadMobEffectDetails(data)); + return result.ToArray(); + } + + private byte[] ReadMobEffectDetails(Queue data) + { + var result = new List(); + var amplifier = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(amplifier)); + var duration = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(duration)); + var ambient = DataTypes.ReadNextBool(data); + result.AddRange(DataTypes.GetBool(ambient)); + var showParticles = DataTypes.ReadNextBool(data); + result.AddRange(DataTypes.GetBool(showParticles)); + var showIcon = DataTypes.ReadNextBool(data); + result.AddRange(DataTypes.GetBool(showIcon)); + var hasHiddenEffect = DataTypes.ReadNextBool(data); + result.AddRange(DataTypes.GetBool(hasHiddenEffect)); + if (hasHiddenEffect) + result.AddRange(ReadMobEffectDetails(data)); + return result.ToArray(); + } + + private byte[] ReadHolderSet(Queue data) + { + var result = new List(); + var type = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(type)); + if (type == 0) + { + var tagName = DataTypes.ReadNextString(data); + result.AddRange(DataTypes.GetString(tagName)); + } + else + { + for (var i = 0; i < type - 1; i++) + { + var id = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(id)); + } + } + return result.ToArray(); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetFloat(ConsumeSeconds)); + data.AddRange(DataTypes.GetVarInt(Animation)); + if (Sound is not null) data.AddRange(Sound.Serialize()); + data.AddRange(DataTypes.GetBool(HasConsumeParticles)); + data.AddRange(DataTypes.GetVarInt(Effects.Count)); + foreach (var effect in Effects) + { + data.AddRange(DataTypes.GetVarInt(effect.EffectTypeId)); + data.AddRange(effect.Payload); + } + return new Queue(data); + } + + public record ConsumeEffectData(int EffectTypeId, byte[] Payload); +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DamageResistantComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DamageResistantComponent.cs new file mode 100644 index 00000000..cc5867bb --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DamageResistantComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class DamageResistantComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string Types { get; set; } = null!; + + public override void Parse(Queue data) + { + Types = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetString(Types)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DeathProtectionComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DeathProtectionComponent.cs new file mode 100644 index 00000000..5b5cc011 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/DeathProtectionComponent.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class DeathProtectionComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List DeathEffects { get; set; } = new(); + + public override void Parse(Queue data) + { + var effectCount = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < effectCount; i++) + { + var effectTypeId = DataTypes.ReadNextVarInt(data); + var effectData = ReadConsumeEffectPayload(effectTypeId, data); + DeathEffects.Add(new ConsumeEffectData(effectTypeId, effectData)); + } + } + + private byte[] ReadConsumeEffectPayload(int effectTypeId, Queue data) + { + var payload = new List(); + switch (effectTypeId) + { + case 0: // apply_effects + var effectCount = DataTypes.ReadNextVarInt(data); + payload.AddRange(DataTypes.GetVarInt(effectCount)); + for (var i = 0; i < effectCount; i++) + payload.AddRange(ReadMobEffectInstance(data)); + payload.AddRange(DataTypes.GetFloat(DataTypes.ReadNextFloat(data))); + break; + case 1: // remove_effects + payload.AddRange(ReadHolderSet(data)); + break; + case 2: // clear_all_effects + break; + case 3: // teleport_randomly + payload.AddRange(DataTypes.GetFloat(DataTypes.ReadNextFloat(data))); + break; + case 4: // play_sound + var sound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + payload.AddRange(sound.Serialize()); + break; + } + return payload.ToArray(); + } + + private byte[] ReadMobEffectInstance(Queue data) + { + var result = new List(); + result.AddRange(DataTypes.GetVarInt(DataTypes.ReadNextVarInt(data))); + result.AddRange(ReadMobEffectDetails(data)); + return result.ToArray(); + } + + private byte[] ReadMobEffectDetails(Queue data) + { + var result = new List(); + result.AddRange(DataTypes.GetVarInt(DataTypes.ReadNextVarInt(data))); + result.AddRange(DataTypes.GetVarInt(DataTypes.ReadNextVarInt(data))); + result.AddRange(DataTypes.GetBool(DataTypes.ReadNextBool(data))); + result.AddRange(DataTypes.GetBool(DataTypes.ReadNextBool(data))); + result.AddRange(DataTypes.GetBool(DataTypes.ReadNextBool(data))); + var hasHidden = DataTypes.ReadNextBool(data); + result.AddRange(DataTypes.GetBool(hasHidden)); + if (hasHidden) + result.AddRange(ReadMobEffectDetails(data)); + return result.ToArray(); + } + + private byte[] ReadHolderSet(Queue data) + { + var result = new List(); + var type = DataTypes.ReadNextVarInt(data); + result.AddRange(DataTypes.GetVarInt(type)); + if (type == 0) + { + result.AddRange(DataTypes.GetString(DataTypes.ReadNextString(data))); + } + else + { + for (var i = 0; i < type - 1; i++) + result.AddRange(DataTypes.GetVarInt(DataTypes.ReadNextVarInt(data))); + } + return result.ToArray(); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(DeathEffects.Count)); + foreach (var effect in DeathEffects) + { + data.AddRange(DataTypes.GetVarInt(effect.EffectTypeId)); + data.AddRange(effect.Payload); + } + return new Queue(data); + } + + public record ConsumeEffectData(int EffectTypeId, byte[] Payload); +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EnchantableComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EnchantableComponent.cs new file mode 100644 index 00000000..f522dbd9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EnchantableComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class EnchantableComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Value { get; set; } + + public override void Parse(Queue data) + { + Value = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Value)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EquippableComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EquippableComponent.cs new file mode 100644 index 00000000..eb3a93b0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/EquippableComponent.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class EquippableComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Slot { get; set; } + public SoundEventSubComponent? EquipSound { get; set; } + public bool HasModel { get; set; } + public string? Model { get; set; } + public bool HasCameraOverlay { get; set; } + public string? CameraOverlay { get; set; } + public bool HasAllowedEntities { get; set; } + public int AllowedEntitiesType { get; set; } + public string? AllowedEntitiesTag { get; set; } + public List? AllowedEntitiesIds { get; set; } + public bool Dispensable { get; set; } + public bool Swappable { get; set; } + public bool DamageOnHurt { get; set; } + + public override void Parse(Queue data) + { + Slot = DataTypes.ReadNextVarInt(data); + EquipSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + + HasModel = DataTypes.ReadNextBool(data); + if (HasModel) + Model = DataTypes.ReadNextString(data); + + HasCameraOverlay = DataTypes.ReadNextBool(data); + if (HasCameraOverlay) + CameraOverlay = DataTypes.ReadNextString(data); + + HasAllowedEntities = DataTypes.ReadNextBool(data); + if (HasAllowedEntities) + { + AllowedEntitiesType = DataTypes.ReadNextVarInt(data); + if (AllowedEntitiesType == 0) + { + AllowedEntitiesTag = DataTypes.ReadNextString(data); + } + else + { + AllowedEntitiesIds = new List(); + for (var i = 0; i < AllowedEntitiesType - 1; i++) + AllowedEntitiesIds.Add(DataTypes.ReadNextVarInt(data)); + } + } + + Dispensable = DataTypes.ReadNextBool(data); + Swappable = DataTypes.ReadNextBool(data); + DamageOnHurt = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Slot)); + if (EquipSound is not null) data.AddRange(EquipSound.Serialize()); + + data.AddRange(DataTypes.GetBool(HasModel)); + if (HasModel && Model is not null) + data.AddRange(DataTypes.GetString(Model)); + + data.AddRange(DataTypes.GetBool(HasCameraOverlay)); + if (HasCameraOverlay && CameraOverlay is not null) + data.AddRange(DataTypes.GetString(CameraOverlay)); + + data.AddRange(DataTypes.GetBool(HasAllowedEntities)); + if (HasAllowedEntities) + { + data.AddRange(DataTypes.GetVarInt(AllowedEntitiesType)); + if (AllowedEntitiesType == 0 && AllowedEntitiesTag is not null) + { + data.AddRange(DataTypes.GetString(AllowedEntitiesTag)); + } + else if (AllowedEntitiesIds is not null) + { + foreach (var id in AllowedEntitiesIds) + data.AddRange(DataTypes.GetVarInt(id)); + } + } + + data.AddRange(DataTypes.GetBool(Dispensable)); + data.AddRange(DataTypes.GetBool(Swappable)); + data.AddRange(DataTypes.GetBool(DamageOnHurt)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/FoodComponent1212.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/FoodComponent1212.cs new file mode 100644 index 00000000..65c58d53 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/FoodComponent1212.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class FoodComponent1212(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Nutrition { get; set; } + public float Saturation { get; set; } + public bool CanAlwaysEat { get; set; } + + public override void Parse(Queue data) + { + Nutrition = DataTypes.ReadNextVarInt(data); + Saturation = DataTypes.ReadNextFloat(data); + CanAlwaysEat = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Nutrition)); + data.AddRange(DataTypes.GetFloat(Saturation)); + data.AddRange(DataTypes.GetBool(CanAlwaysEat)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/GliderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/GliderComponent.cs new file mode 100644 index 00000000..1aa739d0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/GliderComponent.cs @@ -0,0 +1,8 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class GliderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EmptyComponent(dataTypes, itemPalette, subComponentRegistry); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ItemModelComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ItemModelComponent.cs new file mode 100644 index 00000000..97915e66 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/ItemModelComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class ItemModelComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string Identifier { get; set; } = null!; + + public override void Parse(Queue data) + { + Identifier = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetString(Identifier)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/PotionContentsComponent1212.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/PotionContentsComponent1212.cs new file mode 100644 index 00000000..a729adb0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/PotionContentsComponent1212.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class PotionContentsComponent1212(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HasPotionId { get; set; } + public int PotionId { get; set; } + public bool HasCustomColor { get; set; } + public int CustomColor { get; set; } + public List Effects { get; set; } = []; + public bool HasCustomName { get; set; } + public string? CustomName { get; set; } + + public override void Parse(Queue data) + { + HasPotionId = DataTypes.ReadNextBool(data); + if (HasPotionId) + PotionId = DataTypes.ReadNextVarInt(data); + + HasCustomColor = DataTypes.ReadNextBool(data); + if (HasCustomColor) + CustomColor = DataTypes.ReadNextInt(data); + + var numberOfEffects = DataTypes.ReadNextVarInt(data); + Effects = new List(numberOfEffects); + for (var i = 0; i < numberOfEffects; i++) + Effects.Add((PotionEffectSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.PotionEffect, data)); + + HasCustomName = DataTypes.ReadNextBool(data); + if (HasCustomName) + CustomName = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(HasPotionId)); + if (HasPotionId) + data.AddRange(DataTypes.GetVarInt(PotionId)); + + data.AddRange(DataTypes.GetBool(HasCustomColor)); + if (HasCustomColor) + data.AddRange(DataTypes.GetInt(CustomColor)); + + data.AddRange(DataTypes.GetVarInt(Effects.Count)); + foreach (var effect in Effects) + data.AddRange(effect.Serialize()); + + data.AddRange(DataTypes.GetBool(HasCustomName)); + if (HasCustomName && CustomName is not null) + data.AddRange(DataTypes.GetString(CustomName)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/RepairableComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/RepairableComponent.cs new file mode 100644 index 00000000..481c58a3 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/RepairableComponent.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class RepairableComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Type { get; set; } + public string? TagName { get; set; } + public List? ItemIds { get; set; } + + public override void Parse(Queue data) + { + Type = DataTypes.ReadNextVarInt(data); + if (Type == 0) + { + TagName = DataTypes.ReadNextString(data); + } + else + { + ItemIds = new List(); + for (var i = 0; i < Type - 1; i++) + ItemIds.Add(DataTypes.ReadNextVarInt(data)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Type)); + if (Type == 0 && TagName is not null) + { + data.AddRange(DataTypes.GetString(TagName)); + } + else if (ItemIds is not null) + { + foreach (var id in ItemIds) + data.AddRange(DataTypes.GetVarInt(id)); + } + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/TooltipStyleComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/TooltipStyleComponent.cs new file mode 100644 index 00000000..ac25df8f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/TooltipStyleComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class TooltipStyleComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string Identifier { get; set; } = null!; + + public override void Parse(Queue data) + { + Identifier = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetString(Identifier)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseCooldownComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseCooldownComponent.cs new file mode 100644 index 00000000..217c56ce --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseCooldownComponent.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class UseCooldownComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public float Seconds { get; set; } + public bool HasCooldownGroup { get; set; } + public string? CooldownGroup { get; set; } + + public override void Parse(Queue data) + { + Seconds = DataTypes.ReadNextFloat(data); + HasCooldownGroup = DataTypes.ReadNextBool(data); + if (HasCooldownGroup) + CooldownGroup = DataTypes.ReadNextString(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetFloat(Seconds)); + data.AddRange(DataTypes.GetBool(HasCooldownGroup)); + if (HasCooldownGroup && CooldownGroup is not null) + data.AddRange(DataTypes.GetString(CooldownGroup)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseRemainderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseRemainderComponent.cs new file mode 100644 index 00000000..da371a95 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_2/UseRemainderComponent.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; + +public class UseRemainderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Item? ConvertInto { get; set; } + + public override void Parse(Queue data) + { + ConvertInto = DataTypes.ReadNextItemSlot(data, ItemPalette); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetItemSlot(ConvertInto, ItemPalette)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs new file mode 100644 index 00000000..28652f86 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ uses AdventureModePredicate.STREAM_CODEC: +/// list, where each BlockPredicate ends with DataComponentMatchers. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public sealed class CanBreakComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : AdventureModePredicateComponent1215Base(dataTypes, itemPalette, subComponentRegistry); + +/// +/// 1.21.5+ uses AdventureModePredicate.STREAM_CODEC: +/// list, where each BlockPredicate ends with DataComponentMatchers. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public sealed class CanPlaceOnComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : AdventureModePredicateComponent1215Base(dataTypes, itemPalette, subComponentRegistry); + +public abstract class AdventureModePredicateComponent1215Base(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List BlockPredicates { get; set; } = []; + + public override void Parse(Queue data) + { + var predicateCount = DataTypes.ReadNextVarInt(data); + BlockPredicates = new List(predicateCount); + var componentHandler = new StructuredComponentsHandler(DataTypes.ProtocolVersion, DataTypes, ItemPalette); + + for (var i = 0; i < predicateCount; i++) + BlockPredicates.Add(ParseBlockPredicate(data, componentHandler)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(BlockPredicates.Count)); + + foreach (var predicate in BlockPredicates) + SerializeBlockPredicate(data, predicate); + + return new Queue(data); + } + + private AdventureModeBlockPredicate1215 ParseBlockPredicate(Queue data, StructuredComponentsHandler componentHandler) + { + BlockSetSubcomponent? blockSet = null; + if (DataTypes.ReadNextBool(data)) + blockSet = (BlockSetSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockSet, data); + + List properties = []; + if (DataTypes.ReadNextBool(data)) + { + var propertyCount = DataTypes.ReadNextVarInt(data); + properties = new List(propertyCount); + for (var i = 0; i < propertyCount; i++) + properties.Add((PropertySubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Property, data)); + } + + Dictionary? nbt = null; + if (DataTypes.ReadNextBool(data)) + nbt = DataTypes.ReadNextNbt(data); + + var exactComponentCount = DataTypes.ReadNextVarInt(data); + var exactComponents = new List(exactComponentCount); + for (var i = 0; i < exactComponentCount; i++) + { + var componentTypeId = DataTypes.ReadNextVarInt(data); + exactComponents.Add(componentHandler.Parse(componentTypeId, data)); + } + + var partialPredicateCount = DataTypes.ReadNextVarInt(data); + var partialPredicates = new List(partialPredicateCount); + for (var i = 0; i < partialPredicateCount; i++) + { + partialPredicates.Add(new DataComponentPredicatePayload1215( + DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextNbt(data))); + } + + return new AdventureModeBlockPredicate1215(blockSet, properties, nbt, exactComponents, partialPredicates); + } + + private void SerializeBlockPredicate(List data, AdventureModeBlockPredicate1215 predicate) + { + data.AddRange(DataTypes.GetBool(predicate.BlockSet is not null)); + if (predicate.BlockSet is not null) + data.AddRange(predicate.BlockSet.Serialize()); + + data.AddRange(DataTypes.GetBool(predicate.Properties.Count > 0)); + if (predicate.Properties.Count > 0) + { + data.AddRange(DataTypes.GetVarInt(predicate.Properties.Count)); + foreach (var property in predicate.Properties) + data.AddRange(property.Serialize()); + } + + data.AddRange(DataTypes.GetBool(predicate.Nbt is not null)); + if (predicate.Nbt is not null) + data.AddRange(DataTypes.GetNbt(predicate.Nbt)); + + data.AddRange(DataTypes.GetVarInt(predicate.ExactComponents.Count)); + foreach (var component in predicate.ExactComponents) + { + if (component.TypeId < 0) + throw new ArgumentException("Exact predicate component is missing its data component type id.", nameof(component)); + + data.AddRange(DataTypes.GetVarInt(component.TypeId)); + data.AddRange(component.Serialize()); + } + + data.AddRange(DataTypes.GetVarInt(predicate.PartialPredicates.Count)); + foreach (var predicatePayload in predicate.PartialPredicates) + { + data.AddRange(DataTypes.GetVarInt(predicatePayload.TypeId)); + data.AddRange(DataTypes.GetNbt(predicatePayload.Payload)); + } + } +} + +public sealed record AdventureModeBlockPredicate1215( + BlockSetSubcomponent? BlockSet, + List Properties, + Dictionary? Nbt, + List ExactComponents, + List PartialPredicates); + +public sealed record DataComponentPredicatePayload1215(int TypeId, Dictionary Payload); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AttributeModifiersComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AttributeModifiersComponent1215.cs new file mode 100644 index 00000000..f7371eac --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AttributeModifiersComponent1215.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class AttributeModifiersComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfAttributes { get; set; } + public List Attributes { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfAttributes = DataTypes.ReadNextVarInt(data); + Attributes = new List(NumberOfAttributes); + + for (var i = 0; i < NumberOfAttributes; i++) + Attributes.Add(SubComponentRegistry.ParseSubComponent(SubComponents.Attribute, data)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfAttributes)); + + if (Attributes.Count != NumberOfAttributes) + throw new ArgumentNullException(nameof(Attributes), "Attributes count must match NumberOfAttributes."); + + foreach (var attribute in Attributes) + data.AddRange(attribute.Serialize()); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/BlocksAttacksComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/BlocksAttacksComponent.cs new file mode 100644 index 00000000..5c497bf4 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/BlocksAttacksComponent.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class BlocksAttacksComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public float BlockDelaySeconds { get; set; } + public float DisableCooldownScale { get; set; } + public List DamageReductions { get; set; } = []; + public float ItemDamageThreshold { get; set; } + public float ItemDamageBase { get; set; } + public float ItemDamageFactor { get; set; } + public string? BypassedBy { get; set; } + public SoundEventHolderData? BlockSound { get; set; } + public SoundEventHolderData? DisableSound { get; set; } + + public override void Parse(Queue data) + { + BlockDelaySeconds = DataTypes.ReadNextFloat(data); + DisableCooldownScale = DataTypes.ReadNextFloat(data); + + var reductionCount = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < reductionCount; i++) + { + var horizontalBlockingAngle = DataTypes.ReadNextFloat(data); + + var hasTypeFilter = DataTypes.ReadNextBool(data); + var typeFilter = hasTypeFilter + ? StructuredComponentCodecHelpers.ReadHolderSet(DataTypes, data) + : null; + + var baseDmg = DataTypes.ReadNextFloat(data); + var factor = DataTypes.ReadNextFloat(data); + DamageReductions.Add(new DamageReductionData(horizontalBlockingAngle, typeFilter, baseDmg, factor)); + } + + ItemDamageThreshold = DataTypes.ReadNextFloat(data); + ItemDamageBase = DataTypes.ReadNextFloat(data); + ItemDamageFactor = DataTypes.ReadNextFloat(data); + + var hasBypassedBy = DataTypes.ReadNextBool(data); + if (hasBypassedBy) + BypassedBy = DataTypes.ReadNextString(data); // TagKey as ResourceLocation + + BlockSound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + DisableSound = StructuredComponentCodecHelpers.ReadOptionalSoundEventHolder(DataTypes, data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetFloat(BlockDelaySeconds)); + data.AddRange(DataTypes.GetFloat(DisableCooldownScale)); + data.AddRange(DataTypes.GetVarInt(DamageReductions.Count)); + foreach (var reduction in DamageReductions) + { + data.AddRange(DataTypes.GetFloat(reduction.HorizontalBlockingAngle)); + data.AddRange(DataTypes.GetBool(reduction.Type is not null)); + if (reduction.Type is not null) + StructuredComponentCodecHelpers.WriteHolderSet(DataTypes, data, reduction.Type); + data.AddRange(DataTypes.GetFloat(reduction.Base)); + data.AddRange(DataTypes.GetFloat(reduction.Factor)); + } + + data.AddRange(DataTypes.GetFloat(ItemDamageThreshold)); + data.AddRange(DataTypes.GetFloat(ItemDamageBase)); + data.AddRange(DataTypes.GetFloat(ItemDamageFactor)); + data.AddRange(DataTypes.GetBool(BypassedBy is not null)); + if (BypassedBy is not null) + data.AddRange(DataTypes.GetString(BypassedBy)); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, BlockSound); + StructuredComponentCodecHelpers.WriteOptionalSoundEventHolder(DataTypes, data, DisableSound); + return new Queue(data); + } +} + +public sealed record DamageReductionData(float HorizontalBlockingAngle, HolderSetData? Type, float Base, float Factor); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs new file mode 100644 index 00000000..7fa2453c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ dyed_color only carries the RGB integer. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public class DyeColorComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Color { get; set; } + + public override void Parse(Queue data) + { + Color = DataTypes.ReadNextInt(data); + } + + public override Queue Serialize() + { + return new Queue(DataTypes.GetInt(Color)); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EitherHolderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EitherHolderComponent.cs new file mode 100644 index 00000000..352e5f3e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EitherHolderComponent.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class EitherHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool IsHolder { get; set; } + public int HolderId { get; set; } + public string? ResourceKey { get; set; } + + public override void Parse(Queue data) + { + IsHolder = DataTypes.ReadNextBool(data); + if (IsHolder) + { + HolderId = DataTypes.ReadNextVarInt(data); + // For simple entity variants, holderId > 0 means registry ref (id = holderId - 1) + // holderId == 0 means inline data; for most variants the inline is just the variant fields + // We skip inline data since MCC doesn't use variant details + if (HolderId == 0) + { + // Read inline variant data - varies by type, but most are simple + // For chicken/variant specifically this might have additional fields + // We'll consume what we can based on the pattern + // TODO: If needed, specialize per variant type + } + } + else + { + ResourceKey = DataTypes.ReadNextString(data); + } + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetBool(IsHolder)); + if (IsHolder) + { + bytes.AddRange(DataTypes.GetVarInt(HolderId)); + } + else + { + bytes.AddRange(DataTypes.GetString(ResourceKey ?? "")); + } + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs new file mode 100644 index 00000000..67633788 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ enchantments: showInTooltip removed from wire format (moved to tooltip_display component). +/// Wire: VarInt count, then (VarInt holder_id + VarInt level) per entry. No trailing boolean. +/// +public class EnchantmentsComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EnchantmentsComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + NumberOfEnchantments = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfEnchantments; i++) + { + var registryId = DataTypes.ReadNextVarInt(data); + var level = DataTypes.ReadNextVarInt(data); + Enchantments.Add(new Enchantment(EnchantmentMapping.GetEnchantmentByRegistryId1206(DataTypes.ProtocolVersion, registryId), level)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Enchantments.Count)); + foreach (var enchantment in Enchantments) + { + data.AddRange(DataTypes.GetVarInt(EnchantmentMapping.GetRegistryId1206ByEnchantment(DataTypes.ProtocolVersion, enchantment.Type))); + data.AddRange(DataTypes.GetVarInt(enchantment.Level)); + } + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EquippableComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EquippableComponent1215.cs new file mode 100644 index 00000000..4376574c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EquippableComponent1215.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class EquippableComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Slot { get; set; } + public SoundEventSubComponent? EquipSound { get; set; } + public bool HasAssetId { get; set; } + public string? AssetId { get; set; } + public bool HasCameraOverlay { get; set; } + public string? CameraOverlay { get; set; } + public bool HasAllowedEntities { get; set; } + public int AllowedEntitiesType { get; set; } + public string? AllowedEntitiesTag { get; set; } + public List? AllowedEntitiesIds { get; set; } + public bool Dispensable { get; set; } + public bool Swappable { get; set; } + public bool DamageOnHurt { get; set; } + public bool EquipOnInteract { get; set; } + + public override void Parse(Queue data) + { + Slot = DataTypes.ReadNextVarInt(data); + EquipSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + + HasAssetId = DataTypes.ReadNextBool(data); + if (HasAssetId) + AssetId = DataTypes.ReadNextString(data); + + HasCameraOverlay = DataTypes.ReadNextBool(data); + if (HasCameraOverlay) + CameraOverlay = DataTypes.ReadNextString(data); + + HasAllowedEntities = DataTypes.ReadNextBool(data); + if (HasAllowedEntities) + { + AllowedEntitiesType = DataTypes.ReadNextVarInt(data); + if (AllowedEntitiesType == 0) + { + AllowedEntitiesTag = DataTypes.ReadNextString(data); + AllowedEntitiesIds = null; + } + else + { + AllowedEntitiesTag = null; + AllowedEntitiesIds = new List(Math.Max(AllowedEntitiesType - 1, 0)); + for (var i = 0; i < AllowedEntitiesType - 1; i++) + AllowedEntitiesIds.Add(DataTypes.ReadNextVarInt(data)); + } + } + + Dispensable = DataTypes.ReadNextBool(data); + Swappable = DataTypes.ReadNextBool(data); + DamageOnHurt = DataTypes.ReadNextBool(data); + EquipOnInteract = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Slot)); + + if (EquipSound is null) + throw new ArgumentNullException(nameof(EquipSound), "EquipSound is required."); + + data.AddRange(EquipSound.Serialize()); + + data.AddRange(DataTypes.GetBool(HasAssetId)); + if (HasAssetId && AssetId is not null) + data.AddRange(DataTypes.GetString(AssetId)); + + data.AddRange(DataTypes.GetBool(HasCameraOverlay)); + if (HasCameraOverlay && CameraOverlay is not null) + data.AddRange(DataTypes.GetString(CameraOverlay)); + + data.AddRange(DataTypes.GetBool(HasAllowedEntities)); + if (HasAllowedEntities) + { + data.AddRange(DataTypes.GetVarInt(AllowedEntitiesType)); + if (AllowedEntitiesType == 0 && AllowedEntitiesTag is not null) + { + data.AddRange(DataTypes.GetString(AllowedEntitiesTag)); + } + else if (AllowedEntitiesIds is not null) + { + foreach (var id in AllowedEntitiesIds) + data.AddRange(DataTypes.GetVarInt(id)); + } + } + + data.AddRange(DataTypes.GetBool(Dispensable)); + data.AddRange(DataTypes.GetBool(Swappable)); + data.AddRange(DataTypes.GetBool(DamageOnHurt)); + data.AddRange(DataTypes.GetBool(EquipOnInteract)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/InstrumentComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/InstrumentComponent1215.cs new file mode 100644 index 00000000..beba31d8 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/InstrumentComponent1215.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class InstrumentComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + // EitherHolder: Bool + (Holder OR ResourceLocation) + var isHolder = DataTypes.ReadNextBool(data); + if (isHolder) + { + var holderId = DataTypes.ReadNextVarInt(data); + if (holderId == 0) + { + // Inline Instrument: SoundEvent holder + Float useDuration + Float range + Component description + var soundHolderId = DataTypes.ReadNextVarInt(data); + if (soundHolderId == 0) + { + DataTypes.ReadNextString(data); // ResourceLocation + var hasFixedRange = DataTypes.ReadNextBool(data); + if (hasFixedRange) + DataTypes.ReadNextFloat(data); + } + DataTypes.ReadNextFloat(data); // useDuration + DataTypes.ReadNextFloat(data); // range + // ComponentSerialization.STREAM_CODEC is NBT-backed, not a plain string. + DataTypes.ReadNextNbt(data); + } + } + else + { + DataTypes.ReadNextString(data); // ResourceLocation key + } + } + + public override Queue Serialize() + { + return new Queue(); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/JukeBoxPlayableComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/JukeBoxPlayableComponent1215.cs new file mode 100644 index 00000000..10985e31 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/JukeBoxPlayableComponent1215.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class JukeBoxPlayableComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool IsHolder { get; set; } + public int HolderId { get; set; } + public string? ResourceKey { get; set; } + public SoundEventSubComponent? SoundEvent { get; set; } + public Dictionary? DescriptionNbt { get; set; } + public string Description { get; set; } = string.Empty; + public float Duration { get; set; } + public int ComparatorOutput { get; set; } + + public override void Parse(Queue data) + { + IsHolder = DataTypes.ReadNextBool(data); + + if (IsHolder) + { + HolderId = DataTypes.ReadNextVarInt(data); + if (HolderId == 0) + { + SoundEvent = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + DescriptionNbt = DataTypes.ReadNextNbt(data); + Description = ChatParser.ParseText(DescriptionNbt); + Duration = DataTypes.ReadNextFloat(data); + ComparatorOutput = DataTypes.ReadNextVarInt(data); + } + } + else + { + ResourceKey = DataTypes.ReadNextString(data); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetBool(IsHolder)); + + if (IsHolder) + { + data.AddRange(DataTypes.GetVarInt(HolderId)); + if (HolderId == 0) + { + if (SoundEvent is null) + throw new ArgumentNullException(nameof(SoundEvent), "Inline jukebox song requires a sound event."); + + if (DescriptionNbt is null) + throw new ArgumentNullException(nameof(DescriptionNbt), "Inline jukebox song requires a description."); + + data.AddRange(SoundEvent.Serialize()); + data.AddRange(DataTypes.GetNbt(DescriptionNbt)); + data.AddRange(DataTypes.GetFloat(Duration)); + data.AddRange(DataTypes.GetVarInt(ComparatorOutput)); + } + } + else + { + if (string.IsNullOrEmpty(ResourceKey)) + throw new ArgumentNullException(nameof(ResourceKey), "Resource key is required for key-backed jukebox songs."); + + data.AddRange(DataTypes.GetString(ResourceKey)); + } + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PaintingVariantHolderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PaintingVariantHolderComponent.cs new file mode 100644 index 00000000..4e061f13 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PaintingVariantHolderComponent.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class PaintingVariantHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + // Holder: VarInt discriminator + var holderId = DataTypes.ReadNextVarInt(data); + if (holderId == 0) + { + // Inline PaintingVariant: VarInt width + VarInt height + ResourceLocation assetId + DataTypes.ReadNextVarInt(data); // width + DataTypes.ReadNextVarInt(data); // height + DataTypes.ReadNextString(data); // assetId + + // Optional title + if (DataTypes.ReadNextBool(data)) + DataTypes.ReadNextNbt(data); + + // Optional author + if (DataTypes.ReadNextBool(data)) + DataTypes.ReadNextNbt(data); + } + } + + public override Queue Serialize() + { + return new Queue(); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PotionDurationScaleComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PotionDurationScaleComponent.cs new file mode 100644 index 00000000..88354b2c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/PotionDurationScaleComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class PotionDurationScaleComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public float Scale { get; set; } + + public override void Parse(Queue data) + { + Scale = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetFloat(Scale)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesBannerPatternsComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesBannerPatternsComponent.cs new file mode 100644 index 00000000..cc2356e1 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesBannerPatternsComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class ProvidesBannerPatternsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string TagKey { get; set; } = string.Empty; + + public override void Parse(Queue data) + { + TagKey = DataTypes.ReadNextString(data); // ResourceLocation + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetString(TagKey)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesTrimMaterialComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesTrimMaterialComponent.cs new file mode 100644 index 00000000..f3ad9fac --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ProvidesTrimMaterialComponent.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class ProvidesTrimMaterialComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + // EitherHolder: Bool + (Holder OR ResourceLocation) + var isHolder = DataTypes.ReadNextBool(data); + if (isHolder) + { + var holderId = DataTypes.ReadNextVarInt(data); + if (holderId == 0) + { + // Inline TrimMaterial: MaterialAssetGroup + Component description + // MaterialAssetGroup: string + map + DataTypes.ReadNextString(data); // base asset suffix + var overrideCount = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < overrideCount; i++) + { + DataTypes.ReadNextString(data); // ResourceKey + DataTypes.ReadNextString(data); // override suffix + } + // ComponentSerialization.STREAM_CODEC is NBT-backed, not a plain string. + DataTypes.ReadNextNbt(data); + } + } + else + { + DataTypes.ReadNextString(data); // ResourceLocation key + } + } + + public override Queue Serialize() + { + return new Queue(); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/SoundEventHolderComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/SoundEventHolderComponent.cs new file mode 100644 index 00000000..8fe14f7d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/SoundEventHolderComponent.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class SoundEventHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int HolderId { get; set; } + public string? SoundLocation { get; set; } + public bool HasFixedRange { get; set; } + public float FixedRange { get; set; } + + public override void Parse(Queue data) + { + HolderId = DataTypes.ReadNextVarInt(data); + if (HolderId == 0) + { + SoundLocation = DataTypes.ReadNextString(data); + HasFixedRange = DataTypes.ReadNextBool(data); + if (HasFixedRange) + FixedRange = DataTypes.ReadNextFloat(data); + } + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(HolderId)); + if (HolderId == 0) + { + bytes.AddRange(DataTypes.GetString(SoundLocation ?? "")); + bytes.AddRange(DataTypes.GetBool(HasFixedRange)); + if (HasFixedRange) + bytes.AddRange(DataTypes.GetFloat(FixedRange)); + } + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs new file mode 100644 index 00000000..8daf09ea --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs @@ -0,0 +1,7 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class StoredEnchantmentsComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EnchantmentsComponent1215(dataTypes, itemPalette, subComponentRegistry); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ToolComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ToolComponent1215.cs new file mode 100644 index 00000000..c617fabd --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/ToolComponent1215.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class ToolComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfRules { get; set; } + public List Rules { get; set; } = []; + public float DefaultMiningSpeed { get; set; } + public int DamagePerBlock { get; set; } + public bool CanDestroyBlocksInCreative { get; set; } + + public override void Parse(Queue data) + { + NumberOfRules = DataTypes.ReadNextVarInt(data); + Rules = new List(NumberOfRules); + + for (var i = 0; i < NumberOfRules; i++) + Rules.Add((RuleSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Rule, data)); + + DefaultMiningSpeed = DataTypes.ReadNextFloat(data); + DamagePerBlock = DataTypes.ReadNextVarInt(data); + CanDestroyBlocksInCreative = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfRules)); + + if (Rules.Count != NumberOfRules) + throw new ArgumentNullException(nameof(Rules), "Rules count must match NumberOfRules."); + + foreach (var rule in Rules) + data.AddRange(rule.Serialize()); + + data.AddRange(DataTypes.GetFloat(DefaultMiningSpeed)); + data.AddRange(DataTypes.GetVarInt(DamagePerBlock)); + data.AddRange(DataTypes.GetBool(CanDestroyBlocksInCreative)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TooltipDisplayComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TooltipDisplayComponent.cs new file mode 100644 index 00000000..859b3cbc --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TooltipDisplayComponent.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class TooltipDisplayComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public bool HideTooltip { get; set; } + public List HiddenComponentIds { get; set; } = []; + + public override void Parse(Queue data) + { + HideTooltip = DataTypes.ReadNextBool(data); + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + HiddenComponentIds.Add(DataTypes.ReadNextVarInt(data)); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetBool(HideTooltip)); + bytes.AddRange(DataTypes.GetVarInt(HiddenComponentIds.Count)); + foreach (var id in HiddenComponentIds) + bytes.AddRange(DataTypes.GetVarInt(id)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs new file mode 100644 index 00000000..e1b1ebc9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ trim uses ArmorTrim.STREAM_CODEC: +/// Holder<TrimMaterial> + Holder<TrimPattern>. +/// The holder codec is ByteBufCodecs.holder(), so 0 means direct inline data and non-zero means registry reference. +/// +public class TrimComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int MaterialHolderValue { get; set; } + public DirectTrimMaterial1215? DirectMaterial { get; set; } + public int PatternHolderValue { get; set; } + public DirectTrimPattern1215? DirectPattern { get; set; } + + public override void Parse(Queue data) + { + MaterialHolderValue = DataTypes.ReadNextVarInt(data); + if (MaterialHolderValue == 0) + DirectMaterial = ParseDirectMaterial(data); + + PatternHolderValue = DataTypes.ReadNextVarInt(data); + if (PatternHolderValue == 0) + DirectPattern = ParseDirectPattern(data); + } + + public override Queue Serialize() + { + var data = new List(); + + data.AddRange(DataTypes.GetVarInt(MaterialHolderValue)); + if (MaterialHolderValue == 0) + { + if (DirectMaterial is null) + throw new ArgumentNullException(nameof(DirectMaterial), "Direct trim material payload is required when holder value is 0."); + + data.AddRange(DataTypes.GetString(DirectMaterial.BaseSuffix)); + data.AddRange(DataTypes.GetVarInt(DirectMaterial.OverrideSuffixes.Count)); + foreach (var (assetKey, suffix) in DirectMaterial.OverrideSuffixes) + { + data.AddRange(DataTypes.GetString(assetKey)); + data.AddRange(DataTypes.GetString(suffix)); + } + + data.AddRange(DataTypes.GetNbt(DirectMaterial.DescriptionNbt)); + } + + data.AddRange(DataTypes.GetVarInt(PatternHolderValue)); + if (PatternHolderValue == 0) + { + if (DirectPattern is null) + throw new ArgumentNullException(nameof(DirectPattern), "Direct trim pattern payload is required when holder value is 0."); + + data.AddRange(DataTypes.GetString(DirectPattern.AssetId)); + data.AddRange(DataTypes.GetNbt(DirectPattern.DescriptionNbt)); + data.AddRange(DataTypes.GetBool(DirectPattern.Decal)); + } + + return new Queue(data); + } + + private DirectTrimMaterial1215 ParseDirectMaterial(Queue data) + { + var baseSuffix = DataTypes.ReadNextString(data); + var overrideCount = DataTypes.ReadNextVarInt(data); + var overrideSuffixes = new Dictionary(overrideCount, StringComparer.Ordinal); + + for (var i = 0; i < overrideCount; i++) + { + var assetKey = DataTypes.ReadNextString(data); + var suffix = DataTypes.ReadNextString(data); + overrideSuffixes[assetKey] = suffix; + } + + var descriptionNbt = DataTypes.ReadNextNbt(data); + var description = ChatParser.ParseText(descriptionNbt); + return new DirectTrimMaterial1215(baseSuffix, overrideSuffixes, descriptionNbt, description); + } + + private DirectTrimPattern1215 ParseDirectPattern(Queue data) + { + var assetId = DataTypes.ReadNextString(data); + var descriptionNbt = DataTypes.ReadNextNbt(data); + var description = ChatParser.ParseText(descriptionNbt); + var decal = DataTypes.ReadNextBool(data); + return new DirectTrimPattern1215(assetId, descriptionNbt, description, decal); + } +} + +public sealed record DirectTrimMaterial1215( + string BaseSuffix, + Dictionary OverrideSuffixes, + Dictionary DescriptionNbt, + string Description); + +public sealed record DirectTrimPattern1215( + string AssetId, + Dictionary DescriptionNbt, + string Description, + bool Decal); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/VarIntComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/VarIntComponent.cs new file mode 100644 index 00000000..38eebf96 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/VarIntComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class VarIntComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Value { get; set; } + + public override void Parse(Queue data) + { + Value = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(Value)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/WeaponComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/WeaponComponent.cs new file mode 100644 index 00000000..f831e82b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/WeaponComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class WeaponComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int ItemDamagePerAttack { get; set; } + public float DisableBlockingForSeconds { get; set; } + + public override void Parse(Queue data) + { + ItemDamagePerAttack = DataTypes.ReadNextVarInt(data); + DisableBlockingForSeconds = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(ItemDamagePerAttack)); + bytes.AddRange(DataTypes.GetFloat(DisableBlockingForSeconds)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/AttributeModifiersComponent1218.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/AttributeModifiersComponent1218.cs new file mode 100644 index 00000000..92ce146e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/AttributeModifiersComponent1218.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; + +public class AttributeModifiersComponent1218(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfAttributes { get; set; } + public List Attributes { get; set; } = []; + public List Displays { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfAttributes = DataTypes.ReadNextVarInt(data); + Attributes = new List(NumberOfAttributes); + Displays = new List(NumberOfAttributes); + + for (var i = 0; i < NumberOfAttributes; i++) + { + Attributes.Add(SubComponentRegistry.ParseSubComponent(SubComponents.Attribute, data)); + Displays.Add(ReadDisplay(data)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfAttributes)); + + if (Attributes.Count != NumberOfAttributes) + throw new ArgumentNullException(nameof(Attributes), "Attributes count must match NumberOfAttributes."); + + if (Displays.Count != NumberOfAttributes) + throw new ArgumentNullException(nameof(Displays), "Displays count must match NumberOfAttributes."); + + for (var i = 0; i < NumberOfAttributes; i++) + { + data.AddRange(Attributes[i].Serialize()); + data.AddRange(SerializeDisplay(Displays[i])); + } + + return new Queue(data); + } + + private AttributeModifierDisplay ReadDisplay(Queue data) + { + var displayType = DataTypes.ReadNextVarInt(data); + return displayType switch + { + 0 => new AttributeModifierDisplay(AttributeModifierDisplayType.Default), + 1 => new AttributeModifierDisplay(AttributeModifierDisplayType.Hidden), + 2 => ReadOverrideDisplay(data), + _ => throw new InvalidDataException($"Unknown attribute modifier display type: {displayType}") + }; + } + + private AttributeModifierDisplay ReadOverrideDisplay(Queue data) + { + var overrideTextNbt = DataTypes.ReadNextNbt(data); + var overrideText = ChatParser.ParseText(overrideTextNbt); + return new AttributeModifierDisplay(AttributeModifierDisplayType.Override, overrideTextNbt, overrideText); + } + + private Queue SerializeDisplay(AttributeModifierDisplay display) + { + var data = new List + { + }; + data.AddRange(DataTypes.GetVarInt((int)display.Type)); + + if (display.Type == AttributeModifierDisplayType.Override) + { + if (display.OverrideTextNbt is null) + throw new ArgumentNullException(nameof(display.OverrideTextNbt), "Override display requires component NBT."); + + data.AddRange(DataTypes.GetNbt(display.OverrideTextNbt)); + } + + return new Queue(data); + } +} + +public sealed record AttributeModifierDisplay( + AttributeModifierDisplayType Type, + Dictionary? OverrideTextNbt = null, + string? OverrideText = null); + +public enum AttributeModifierDisplayType +{ + Default = 0, + Hidden = 1, + Override = 2 +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/EquippableComponent1218.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/EquippableComponent1218.cs new file mode 100644 index 00000000..224e1b05 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_8/EquippableComponent1218.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; + +public class EquippableComponent1218(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Slot { get; set; } + public SoundEventSubComponent? EquipSound { get; set; } + public bool HasAssetId { get; set; } + public string? AssetId { get; set; } + public bool HasCameraOverlay { get; set; } + public string? CameraOverlay { get; set; } + public bool HasAllowedEntities { get; set; } + public int AllowedEntitiesType { get; set; } + public string? AllowedEntitiesTag { get; set; } + public List? AllowedEntitiesIds { get; set; } + public bool Dispensable { get; set; } + public bool Swappable { get; set; } + public bool DamageOnHurt { get; set; } + public bool EquipOnInteract { get; set; } + public bool CanBeSheared { get; set; } + public SoundEventSubComponent? ShearingSound { get; set; } + + public override void Parse(Queue data) + { + Slot = DataTypes.ReadNextVarInt(data); + EquipSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + + HasAssetId = DataTypes.ReadNextBool(data); + if (HasAssetId) + AssetId = DataTypes.ReadNextString(data); + + HasCameraOverlay = DataTypes.ReadNextBool(data); + if (HasCameraOverlay) + CameraOverlay = DataTypes.ReadNextString(data); + + HasAllowedEntities = DataTypes.ReadNextBool(data); + if (HasAllowedEntities) + { + AllowedEntitiesType = DataTypes.ReadNextVarInt(data); + if (AllowedEntitiesType == 0) + { + AllowedEntitiesTag = DataTypes.ReadNextString(data); + AllowedEntitiesIds = null; + } + else + { + AllowedEntitiesTag = null; + AllowedEntitiesIds = new List(Math.Max(AllowedEntitiesType - 1, 0)); + for (var i = 0; i < AllowedEntitiesType - 1; i++) + AllowedEntitiesIds.Add(DataTypes.ReadNextVarInt(data)); + } + } + + Dispensable = DataTypes.ReadNextBool(data); + Swappable = DataTypes.ReadNextBool(data); + DamageOnHurt = DataTypes.ReadNextBool(data); + EquipOnInteract = DataTypes.ReadNextBool(data); + CanBeSheared = DataTypes.ReadNextBool(data); + ShearingSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Slot)); + + if (EquipSound is null) + throw new ArgumentNullException(nameof(EquipSound), "EquipSound is required."); + + if (ShearingSound is null) + throw new ArgumentNullException(nameof(ShearingSound), "ShearingSound is required."); + + data.AddRange(EquipSound.Serialize()); + + data.AddRange(DataTypes.GetBool(HasAssetId)); + if (HasAssetId && AssetId is not null) + data.AddRange(DataTypes.GetString(AssetId)); + + data.AddRange(DataTypes.GetBool(HasCameraOverlay)); + if (HasCameraOverlay && CameraOverlay is not null) + data.AddRange(DataTypes.GetString(CameraOverlay)); + + data.AddRange(DataTypes.GetBool(HasAllowedEntities)); + if (HasAllowedEntities) + { + data.AddRange(DataTypes.GetVarInt(AllowedEntitiesType)); + if (AllowedEntitiesType == 0 && AllowedEntitiesTag is not null) + { + data.AddRange(DataTypes.GetString(AllowedEntitiesTag)); + } + else if (AllowedEntitiesIds is not null) + { + foreach (var id in AllowedEntitiesIds) + data.AddRange(DataTypes.GetVarInt(id)); + } + } + + data.AddRange(DataTypes.GetBool(Dispensable)); + data.AddRange(DataTypes.GetBool(Swappable)); + data.AddRange(DataTypes.GetBool(DamageOnHurt)); + data.AddRange(DataTypes.GetBool(EquipOnInteract)); + data.AddRange(DataTypes.GetBool(CanBeSheared)); + data.AddRange(ShearingSound.Serialize()); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_9/BeesComponent1219.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_9/BeesComponent1219.cs new file mode 100644 index 00000000..b8142caa --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_9/BeesComponent1219.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9; + +public class BeesComponent1219(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int NumberOfBees { get; set; } + public List Bees { get; set; } = []; + + public override void Parse(Queue data) + { + NumberOfBees = DataTypes.ReadNextVarInt(data); + Bees = new List(NumberOfBees); + + for (var i = 0; i < NumberOfBees; i++) + { + Bees.Add( + new TypedBee( + DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextNbt(data), + DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextVarInt(data))); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(NumberOfBees)); + + if (NumberOfBees != Bees.Count) + throw new InvalidOperationException("Can't serialize the BeesComponent1219 because NumberOfBees and Bees.Count differ!"); + + foreach (var bee in Bees) + { + data.AddRange(DataTypes.GetVarInt(bee.EntityTypeId)); + data.AddRange(DataTypes.GetNbt(bee.EntityDataNbt)); + data.AddRange(DataTypes.GetVarInt(bee.TicksInHive)); + data.AddRange(DataTypes.GetVarInt(bee.MinTicksInHive)); + } + + return new Queue(data); + } +} + +public sealed record TypedBee(int EntityTypeId, Dictionary? EntityDataNbt, int TicksInHive, int MinTicksInHive); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs new file mode 100644 index 00000000..87a83a4e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class ContainerComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextBool(data) ? DataTypes.ReadNextItemStackTemplate(data, ItemPalette) : null); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + foreach (var item in Items) + { + var hasItem = item is not null && !item.IsEmpty; + data.AddRange(DataTypes.GetBool(hasItem)); + if (hasItem) + data.AddRange(DataTypes.GetItemStackTemplate(item!, ItemPalette)); + } + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/HolderSetComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/HolderSetComponent261.cs new file mode 100644 index 00000000..516117ab --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/HolderSetComponent261.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class HolderSetComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public string? TagKey { get; set; } + public List HolderIds { get; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data) - 1; + if (count == -1) + { + TagKey = DataTypes.ReadNextString(data); + return; + } + + for (var i = 0; i < count; i++) + HolderIds.Add(DataTypes.ReadNextVarInt(data)); + } + + public override Queue Serialize() + { + var bytes = new List(); + if (TagKey is not null) + { + bytes.AddRange(DataTypes.GetVarInt(0)); + bytes.AddRange(DataTypes.GetString(TagKey)); + return new Queue(bytes); + } + + bytes.AddRange(DataTypes.GetVarInt(HolderIds.Count + 1)); + foreach (var holderId in HolderIds) + bytes.AddRange(DataTypes.GetVarInt(holderId)); + + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/InstrumentComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/InstrumentComponent261.cs new file mode 100644 index 00000000..0f91f0b3 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/InstrumentComponent261.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class InstrumentComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int HolderId { get; set; } + + public override void Parse(Queue data) + { + HolderId = DataTypes.ReadNextVarInt(data); + if (HolderId != 0) + return; + + var soundHolderId = DataTypes.ReadNextVarInt(data); + if (soundHolderId == 0) + { + DataTypes.ReadNextString(data); // ResourceLocation + var hasFixedRange = DataTypes.ReadNextBool(data); + if (hasFixedRange) + DataTypes.ReadNextFloat(data); + } + + DataTypes.ReadNextFloat(data); // useDuration + DataTypes.ReadNextFloat(data); // range + DataTypes.ReadNextNbt(data); // ComponentSerialization.STREAM_CODEC + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(HolderId)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs new file mode 100644 index 00000000..7c7fdc74 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class ItemStackTemplateListComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextItemStackTemplate(data, ItemPalette)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + + foreach (var item in Items) + data.AddRange(DataTypes.GetItemStackTemplate(item, ItemPalette)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs new file mode 100644 index 00000000..1c864dcf --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class NbtTagComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public object? Tag { get; set; } + + public override void Parse(Queue data) + { + Tag = DataTypes.ReadNextNbtTag(data); + } + + public override Queue Serialize() + { + return new Queue(DataTypes.GetNbtTag(Tag)); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ProvidesTrimMaterialComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ProvidesTrimMaterialComponent261.cs new file mode 100644 index 00000000..7c76b44a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ProvidesTrimMaterialComponent261.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class ProvidesTrimMaterialComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int HolderId { get; set; } + + public override void Parse(Queue data) + { + HolderId = DataTypes.ReadNextVarInt(data); + if (HolderId != 0) + return; + + DataTypes.ReadNextString(data); // base asset suffix + + var overrideCount = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < overrideCount; i++) + { + DataTypes.ReadNextString(data); // ResourceKey + DataTypes.ReadNextString(data); // override suffix + } + + DataTypes.ReadNextNbt(data); // ComponentSerialization.STREAM_CODEC + } + + public override Queue Serialize() + { + var bytes = new List(); + bytes.AddRange(DataTypes.GetVarInt(HolderId)); + return new Queue(bytes); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs new file mode 100644 index 00000000..d05b147a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs @@ -0,0 +1,13 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class TypedEntityDataComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : TypedEntityDataComponent(dataTypes, itemPalette, subComponentRegistry) +{ } + +public class BlockEntityDataComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : TypedBlockEntityDataComponent(dataTypes, itemPalette, subComponentRegistry) +{ } diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs new file mode 100644 index 00000000..e5b3323e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class UseRemainderComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Item? ConvertInto { get; set; } + + public override void Parse(Queue data) + { + ConvertInto = DataTypes.ReadNextItemStackTemplate(data, ItemPalette); + } + + public override Queue Serialize() + { + var data = new List(); + if (ConvertInto is not null && !ConvertInto.IsEmpty) + data.AddRange(DataTypes.GetItemStackTemplate(ConvertInto, ItemPalette)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/EmptyComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/EmptyComponent.cs new file mode 100644 index 00000000..8b49c908 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/EmptyComponent.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components; + +public class EmptyComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + } + + public override Queue Serialize() + { + return new Queue(); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/StructuredComponentCodecHelpers.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/StructuredComponentCodecHelpers.cs new file mode 100644 index 00000000..dc3fc218 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/StructuredComponentCodecHelpers.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components; + +internal static class StructuredComponentCodecHelpers +{ + public static HolderSetData ReadHolderSet(DataTypes dataTypes, Queue data) + { + var sizeOrTag = dataTypes.ReadNextVarInt(data); + if (sizeOrTag == 0) + return new HolderSetData(dataTypes.ReadNextString(data), []); + + var count = sizeOrTag - 1; + var holderIds = new List(count); + for (var i = 0; i < count; i++) + holderIds.Add(dataTypes.ReadNextVarInt(data)); + + return new HolderSetData(null, holderIds); + } + + public static void WriteHolderSet(DataTypes dataTypes, List bytes, HolderSetData holderSet) + { + if (holderSet.Tag is not null) + { + bytes.AddRange(DataTypes.GetVarInt(0)); + bytes.AddRange(dataTypes.GetString(holderSet.Tag)); + return; + } + + bytes.AddRange(DataTypes.GetVarInt(holderSet.HolderIds.Count + 1)); + foreach (var holderId in holderSet.HolderIds) + bytes.AddRange(DataTypes.GetVarInt(holderId)); + } + + public static SoundEventHolderData ReadSoundEventHolder(DataTypes dataTypes, Queue data) + { + var holderId = dataTypes.ReadNextVarInt(data); + if (holderId != 0) + return new SoundEventHolderData(holderId, null, false, 0); + + var soundLocation = dataTypes.ReadNextString(data); + var hasFixedRange = dataTypes.ReadNextBool(data); + var fixedRange = hasFixedRange ? dataTypes.ReadNextFloat(data) : 0; + return new SoundEventHolderData(holderId, soundLocation, hasFixedRange, fixedRange); + } + + public static void WriteSoundEventHolder(DataTypes dataTypes, List bytes, SoundEventHolderData soundEvent) + { + bytes.AddRange(DataTypes.GetVarInt(soundEvent.HolderId)); + if (soundEvent.HolderId != 0) + return; + + bytes.AddRange(dataTypes.GetString(soundEvent.SoundLocation ?? "")); + bytes.AddRange(dataTypes.GetBool(soundEvent.HasFixedRange)); + if (soundEvent.HasFixedRange) + bytes.AddRange(dataTypes.GetFloat(soundEvent.FixedRange)); + } + + public static SoundEventHolderData? ReadOptionalSoundEventHolder(DataTypes dataTypes, Queue data) + { + return dataTypes.ReadNextBool(data) ? ReadSoundEventHolder(dataTypes, data) : null; + } + + public static void WriteOptionalSoundEventHolder(DataTypes dataTypes, List bytes, SoundEventHolderData? soundEvent) + { + bytes.AddRange(dataTypes.GetBool(soundEvent is not null)); + if (soundEvent is not null) + WriteSoundEventHolder(dataTypes, bytes, soundEvent); + } +} + +public sealed record HolderSetData(string? Tag, List HolderIds); + +public sealed record SoundEventHolderData(int HolderId, string? SoundLocation, bool HasFixedRange, float FixedRange); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/AttributeSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/AttributeSubComponent.cs new file mode 100644 index 00000000..72905355 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/AttributeSubComponent.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class AttributeSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int TypeId { get; set; } + public Guid Uuid { get; set; } + public string? Name { get; set; } + public double Value { get; set; } + public int Operation { get; set; } + public int Slot { get; set; } + + protected override void Parse(Queue data) + { + TypeId = DataTypes.ReadNextVarInt(data); + Uuid = DataTypes.ReadNextUUID(data); + Name = DataTypes.ReadNextString(data); + Value = DataTypes.ReadNextDouble(data); + Operation = DataTypes.ReadNextVarInt(data); + Slot = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(TypeId)); + data.AddRange(DataTypes.GetUUID(Uuid)); + + if (string.IsNullOrEmpty(Name?.Trim())) + throw new ArgumentNullException($"Can not serialize AttributeSubComponent due to Name being null or empty!"); + + data.AddRange(DataTypes.GetString(Name)); + data.AddRange(DataTypes.GetDouble(Value)); + data.AddRange(DataTypes.GetVarInt(Operation)); + data.AddRange(DataTypes.GetVarInt(Slot)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockPredicateSubcomponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockPredicateSubcomponent.cs new file mode 100644 index 00000000..74639c6e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockPredicateSubcomponent.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class BlockPredicateSubcomponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public bool HasBlocks { get; set; } + public BlockSetSubcomponent? BlockSet { get; set; } + public bool HasProperities { get; set; } + public List? Properties { get; set; } + public bool HasNbt { get; set; } + public Dictionary? Nbt { get; set; } + + protected override void Parse(Queue data) + { + HasBlocks = DataTypes.ReadNextBool(data); + + if (HasBlocks) + BlockSet = (BlockSetSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockSet, data); + + HasProperities = DataTypes.ReadNextBool(data); + + if (HasProperities) + { + Properties = new(); + var numberOfProperties = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < numberOfProperties; i++) + Properties.Add((PropertySubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Property, data)); + } + + HasNbt = DataTypes.ReadNextBool(data); + + if (HasNbt) + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + + // Block Sets + data.AddRange(DataTypes.GetBool(HasBlocks)); + if (HasBlocks) + { + if (BlockSet is null) + throw new ArgumentNullException($"Can not serialize a BlockPredicate when the BlockSet is empty but HasBlocks is true!"); + + data.AddRange(BlockSet.Serialize()); + } + + // Properties + data.AddRange(DataTypes.GetBool(HasProperities)); + if (HasProperities) + { + if (Properties is null || Properties.Count == 0) + throw new ArgumentNullException($"Can not serialize a BlockPredicate when the Properties is empty but HasProperties is true!"); + + data.AddRange(DataTypes.GetVarInt(Properties.Count)); + foreach (var property in Properties) + data.AddRange(property.Serialize()); + } + + // NBT + data.AddRange(DataTypes.GetBool(HasNbt)); + if (HasNbt) + { + if (Nbt is null) + throw new ArgumentNullException($"Can not serialize a BlockPredicate when the Nbt is empty but HasNbt is true!"); + + data.AddRange(DataTypes.GetNbt(Nbt)); + } + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockSetSubcomponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockSetSubcomponent.cs new file mode 100644 index 00000000..2fe1be6d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/BlockSetSubcomponent.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class BlockSetSubcomponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int Type { get; set; } + public string? TagName { get; set; } + public List? BlockIds { get; set; } + + protected override void Parse(Queue data) + { + Type = DataTypes.ReadNextVarInt(data); + + if (Type == 0) + TagName = DataTypes.ReadNextString(data); + + if (Type == 0) return; + + BlockIds = []; + + for (var i = 0; i < Type - 1; i++) + BlockIds.Add(DataTypes.ReadNextVarInt(data)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Type)); + if (Type == 0) + { + if (string.IsNullOrEmpty(TagName?.Trim())) + throw new ArgumentNullException($"Can not serialize an empty tag name when the Block Set type is 0!"); + + data.AddRange(DataTypes.GetString(TagName)); + } + + if (Type == 0) return new Queue(data); + + if (BlockIds is null || BlockIds.Count == 0) + throw new ArgumentNullException($"Can not serialize an empty list of Block IDs in a Block Set when the type is not 0!"); + + for (var i = 0; i < Type - 1; i++) + data.AddRange(DataTypes.GetVarInt(BlockIds[i])); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/DetailsSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/DetailsSubComponent.cs new file mode 100644 index 00000000..05b3c918 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/DetailsSubComponent.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class DetailsSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int Amplifier { get; set; } + public int Duration { get; set; } + public bool Ambient { get; set; } + public bool ShowParticles { get; set; } + public bool ShowIcon { get; set; } + public bool HasHiddenEffects { get; set; } + public DetailsSubComponent? Detail { get; set; } + + protected override void Parse(Queue data) + { + Amplifier = DataTypes.ReadNextVarInt(data); + Duration = DataTypes.ReadNextVarInt(data); + Ambient = DataTypes.ReadNextBool(data); + ShowParticles = DataTypes.ReadNextBool(data); + ShowIcon = DataTypes.ReadNextBool(data); + HasHiddenEffects = DataTypes.ReadNextBool(data); + + if (HasHiddenEffects) + Detail = (DetailsSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Details, data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Amplifier)); + data.AddRange(DataTypes.GetVarInt(Duration)); + data.AddRange(DataTypes.GetBool(Ambient)); + data.AddRange(DataTypes.GetBool(ShowParticles)); + data.AddRange(DataTypes.GetBool(ShowIcon)); + data.AddRange(DataTypes.GetBool(HasHiddenEffects)); + + if (HasHiddenEffects) + { + if (Detail is null) + throw new ArgumentNullException($"Can not serialize a DetailSubComponent1206 when the Detail is empty but HasHiddenEffects is true!"); + + data.AddRange(Detail.Serialize()); + } + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/EffectSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/EffectSubComponent.cs new file mode 100644 index 00000000..d17d43f0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/EffectSubComponent.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class EffectSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public PotionEffectSubComponent TypeId { get; set; } = null!; + public float Probability { get; set; } + + protected override void Parse(Queue data) + { + TypeId = (PotionEffectSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.PotionEffect, data); + Probability = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(TypeId.Serialize()); + data.AddRange(DataTypes.GetFloat(Probability)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/FireworkExplosionSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/FireworkExplosionSubComponent.cs new file mode 100644 index 00000000..8073964c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/FireworkExplosionSubComponent.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class FireworkExplosionSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int Shape { get; set; } + public int NumberOfColors { get; set; } + public List Colors { get; set; } = []; + public int NumberOfFadeColors { get; set; } + public List FadeColors { get; set; } = []; + public bool HasTrail { get; set; } + public bool HasTwinkle { get; set; } + + protected override void Parse(Queue data) + { + Shape = DataTypes.ReadNextVarInt(data); + NumberOfColors = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfColors; i++) + Colors.Add(DataTypes.ReadNextInt(data)); + + NumberOfFadeColors = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfFadeColors; i++) + FadeColors.Add(DataTypes.ReadNextInt(data)); + + HasTrail = DataTypes.ReadNextBool(data); + HasTwinkle = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Shape)); + + data.AddRange(DataTypes.GetVarInt(NumberOfColors)); + if (NumberOfColors > 0) + { + if (NumberOfColors != Colors.Count) + throw new Exception("Can't serialize FireworkExplosionComponent because NumberOfColors and the length of Colors list differ!"); + + foreach (var color in Colors) + data.AddRange(DataTypes.GetInt(color)); + } + + data.AddRange(DataTypes.GetVarInt(NumberOfFadeColors)); + if (NumberOfFadeColors > 0) + { + if (NumberOfFadeColors != FadeColors.Count) + throw new Exception("Can't serialize FireworkExplosionComponent because NumberOfFadeColors and the length of FadeColors list differ!"); + + foreach (var fadeColor in FadeColors) + data.AddRange(DataTypes.GetInt(fadeColor)); + } + + data.AddRange(DataTypes.GetBool(HasTrail)); + data.AddRange(DataTypes.GetBool(HasTwinkle)); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PotionEffectSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PotionEffectSubComponent.cs new file mode 100644 index 00000000..dc9e31f0 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PotionEffectSubComponent.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class PotionEffectSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int TypeId { get; set; } + public DetailsSubComponent Details { get; set; } = null!; + + protected override void Parse(Queue data) + { + TypeId = DataTypes.ReadNextVarInt(data); + Details = (DetailsSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Details, data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(TypeId)); + data.AddRange(Details.Serialize()); + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PropertySubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PropertySubComponent.cs new file mode 100644 index 00000000..52e18770 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/PropertySubComponent.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class PropertySubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public string? Name { get; set; } + public bool IsExactMatch { get; set; } + public string? ExactValue { get; set; } + public string? MinValue { get; set; } + public string? MaxValue { get; set; } + + protected override void Parse(Queue data) + { + Name = DataTypes.ReadNextString(data); + IsExactMatch = DataTypes.ReadNextBool(data); + + if (IsExactMatch) + { + ExactValue = DataTypes.ReadNextString(data); + } + else + { + MinValue = DataTypes.ReadNextBool(data) ? DataTypes.ReadNextString(data) : null; + MaxValue = DataTypes.ReadNextBool(data) ? DataTypes.ReadNextString(data) : null; + } + } + + public override Queue Serialize() + { + var data = new List(); + + if (string.IsNullOrEmpty(Name?.Trim())) + throw new ArgumentNullException($"Can not serialize a Property sub-component if the Name is null or empty!"); + + data.AddRange(DataTypes.GetString(Name)); + data.AddRange(DataTypes.GetBool(IsExactMatch)); + + if (IsExactMatch) + { + if (string.IsNullOrEmpty(ExactValue?.Trim())) + throw new ArgumentNullException($"Can not serialize a Property sub-component if the ExactValue is null or empty when the type is Exact Match!"); + + data.AddRange(DataTypes.GetString(ExactValue)); + } + else + { + data.AddRange(DataTypes.GetBool(MinValue is not null)); + if (MinValue is not null) + data.AddRange(DataTypes.GetString(MinValue)); + + data.AddRange(DataTypes.GetBool(MaxValue is not null)); + if (MaxValue is not null) + data.AddRange(DataTypes.GetString(MaxValue)); + } + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/RuleSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/RuleSubComponent.cs new file mode 100644 index 00000000..74565c9b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_20_6/RuleSubComponent.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; + +public class RuleSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public BlockSetSubcomponent Blocks { get; set; } = null!; + public bool HasSpeed { get; set; } + public float Speed { get; set; } + public bool HasCorrectDropForBlocks { get; set; } + public bool CorrectDropForBlocks { get; set; } + + protected override void Parse(Queue data) + { + Blocks = (BlockSetSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockSet, data); + HasSpeed = DataTypes.ReadNextBool(data); + + if (HasSpeed) + Speed = DataTypes.ReadNextFloat(data); + + HasCorrectDropForBlocks = DataTypes.ReadNextBool(data); + + if (HasCorrectDropForBlocks) + CorrectDropForBlocks = DataTypes.ReadNextBool(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(Blocks.Serialize()); + data.AddRange(DataTypes.GetBool(HasSpeed)); + if (HasSpeed) + data.AddRange(DataTypes.GetFloat(Speed)); + + data.AddRange(DataTypes.GetBool(HasCorrectDropForBlocks)); + if (HasCorrectDropForBlocks) + data.AddRange(DataTypes.GetBool(CorrectDropForBlocks)); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs new file mode 100644 index 00000000..60bc94dc --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; + +public class AttributeSubComponent121(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int TypeId { get; set; } + public string? ResourceLocation { get; set; } + public double Value { get; set; } + public int Operation { get; set; } + public int Slot { get; set; } + + protected override void Parse(Queue data) + { + TypeId = DataTypes.ReadNextVarInt(data); + ResourceLocation = DataTypes.ReadNextString(data); + Value = DataTypes.ReadNextDouble(data); + Operation = DataTypes.ReadNextVarInt(data); + Slot = DataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(TypeId)); + + if (string.IsNullOrEmpty(ResourceLocation?.Trim())) + throw new ArgumentNullException($"Can not serialize AttributeSubComponent121 due to ResourceLocation being null or empty!"); + + data.AddRange(DataTypes.GetString(ResourceLocation)); + data.AddRange(DataTypes.GetDouble(Value)); + data.AddRange(DataTypes.GetVarInt(Operation)); + data.AddRange(DataTypes.GetVarInt(Slot)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/SoundEventSubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/SoundEventSubComponent.cs new file mode 100644 index 00000000..2c2885c8 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/SoundEventSubComponent.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; + +public class SoundEventSubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int Type { get; set; } + public string? SoundName { get; set; } + public bool HasFixedRange { get; set; } + public float FixedRange { get; set; } + + protected override void Parse(Queue data) + { + Type = DataTypes.ReadNextVarInt(data); + + if (Type != 0) return; + + SoundName = DataTypes.ReadNextString(data); + HasFixedRange = DataTypes.ReadNextBool(data); + + if (HasFixedRange) + FixedRange = DataTypes.ReadNextFloat(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Type)); + + if (Type != 0) return new Queue(data); + + if (string.IsNullOrEmpty(SoundName?.Trim())) + throw new ArgumentNullException($"Can not serialize SoundEventSubComponent due to SoundName being null or empty!"); + + data.AddRange(DataTypes.GetString(SoundName)); + data.AddRange(DataTypes.GetBool(HasFixedRange)); + + if (HasFixedRange) + data.AddRange(DataTypes.GetFloat(FixedRange)); + + return new Queue(data); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/SubComponents.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/SubComponents.cs new file mode 100644 index 00000000..9fdc974c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/SubComponents.cs @@ -0,0 +1,15 @@ +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; + +public abstract class SubComponents +{ + public const string BlockPredicate = "BlockPredicate"; + public const string BlockSet = "BlockSet"; + public const string Property = "Property"; + public const string Attribute = "Attribute"; + public const string Effect = "Effect"; + public const string PotionEffect = "PotionEffect"; + public const string Details = "Details"; + public const string Rule = "Rule"; + public const string FireworkExplosion = "FireworkExplosion"; + public const string SoundEvent = "SoundEvent"; +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/TypedEntityDataComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/TypedEntityDataComponent.cs new file mode 100644 index 00000000..ab7ae279 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/TypedEntityDataComponent.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components; + +public class TypedEntityDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int EntityTypeId { get; set; } + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + EntityTypeId = DataTypes.ReadNextVarInt(data); + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(EntityTypeId)); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} + +public class TypedBlockEntityDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : TypedEntityDataComponent(dataTypes, itemPalette, subComponentRegistry) +{ } diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponent.cs new file mode 100644 index 00000000..2c0b66e3 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponent.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +public abstract class StructuredComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) +{ + protected DataTypes DataTypes { get; private set; } = dataTypes; + protected SubComponentRegistry SubComponentRegistry { get; private set; } = subComponentRegistry; + protected ItemPalette ItemPalette { get; private set; } = itemPalette; + + /// + /// The registry type ID assigned during parsing, used for round-trip serialization. + /// + public int TypeId { get; set; } = -1; + + public abstract void Parse(Queue data); + public abstract Queue Serialize(); +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponentRegistry.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponentRegistry.cs new file mode 100644 index 00000000..9ff1f12c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/StructuredComponentRegistry.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +public abstract class StructuredComponentRegistry(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) +{ + private Dictionary ComponentParsers { get; } = new(); + private Dictionary IdToComponent { get; } = new(); + private Dictionary ComponentToId { get; } = new(); + + protected void RegisterComponent(int id, string name) where T : StructuredComponent + { + if (string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullException(nameof(name)); + + name = name.ToLower(); + + if (ComponentParsers.ContainsKey(name) || IdToComponent.ContainsValue(name) + || ComponentToId.ContainsKey(name) || IdToComponent.ContainsKey(id)) + throw new InvalidOperationException($"A component with name '{name}' or id '{id}' is already registered."); + + ComponentParsers[name] = typeof(T); + IdToComponent[id] = name; + ComponentToId[name] = id; + } + + public StructuredComponent ParseComponent(int id, Queue data) + { + if (IdToComponent.TryGetValue(id, out var name)) + { + if (ComponentParsers.TryGetValue(name, out var type)) + { + var component = + Activator.CreateInstance(type, dataTypes, itemPalette, subComponentRegistry) as StructuredComponent + ?? throw new InvalidOperationException($"Could not instantiate a parser for a structured component type {name}"); + + component.TypeId = id; + component.Parse(data); + return component; + } + } + + throw new Exception($"No parser found for component with ID {id}"); + } + + public string GetComponentNameById(int id) + { + if (IdToComponent.TryGetValue(id, out var value)) + return value; + + throw new Exception($"No component found for ID {id}"); + } + + public int GetComponentIdByName(string name) + { + name = name.ToLower(); + + if (ComponentToId.TryGetValue(name, out var value)) + return value; + + throw new Exception($"No ID found for component {name}"); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponent.cs new file mode 100644 index 00000000..be235a91 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponent.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +public abstract class SubComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) +{ + protected DataTypes DataTypes { get; private set; } = dataTypes; + protected SubComponentRegistry SubComponentRegistry { get; private set; } = subComponentRegistry; + + protected abstract void Parse(Queue data); + public abstract Queue Serialize(); +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs new file mode 100644 index 00000000..0da8120b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +public abstract class SubComponentRegistry(DataTypes dataTypes) +{ + private readonly Dictionary _subComponentParsers = new(); + + protected void RegisterSubComponent(string name) where T : SubComponent + { + if (_subComponentParsers.TryGetValue(name, out _)) + throw new Exception($"Sub component {name} already registered!"); + + _subComponentParsers.Add(name, typeof(T)); + } + + protected void ReplaceSubComponent(string name) where T : SubComponent + { + _subComponentParsers[name] = typeof(T); + } + + public SubComponent ParseSubComponent(string name, Queue data) + { + if (!_subComponentParsers.TryGetValue(name, out var subComponentParserType)) + throw new Exception($"Sub component {name} not registered!"); + + var instance = Activator.CreateInstance(subComponentParserType, dataTypes, this) as SubComponent ?? + throw new InvalidOperationException($"Could not create instance of a sub component parser type: {subComponentParserType.Name}"); + + var parseMethod = instance.GetType().GetMethod("Parse", BindingFlags.Instance | BindingFlags.NonPublic); + + if (parseMethod is null) + throw new InvalidOperationException($"Sub component parser type {subComponentParserType.Name} does not have a Parse method."); + + parseMethod.Invoke(instance, new object[] { data }); + return instance; + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1206.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1206.cs new file mode 100644 index 00000000..8f5dee2e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1206.cs @@ -0,0 +1,69 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry1206 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); + RegisterComponent(5, "minecraft:custom_name"); + RegisterComponent(6, "minecraft:item_name"); + RegisterComponent(7, "minecraft:lore"); + RegisterComponent(8, "minecraft:rarity"); + RegisterComponent(9, "minecraft:enchantments"); + RegisterComponent(10, "minecraft:can_place_on"); + RegisterComponent(11, "minecraft:can_break"); + RegisterComponent(12, "minecraft:attribute_modifiers"); + RegisterComponent(13, "minecraft:custom_model_data"); + RegisterComponent(14, "minecraft:hide_additional_tooltip"); + RegisterComponent(15, "minecraft:hide_tooltip"); + RegisterComponent(16, "minecraft:repair_cost"); + RegisterComponent(17, "minecraft:creative_slot_lock"); + RegisterComponent(18, "minecraft:enchantment_glint_override"); + RegisterComponent(19, "minecraft:intangible_projectile"); + RegisterComponent(20, "minecraft:food"); + RegisterComponent(21, "minecraft:fire_resistant"); + RegisterComponent(22, "minecraft:tool"); + RegisterComponent(23, "minecraft:stored_enchantments"); + RegisterComponent(24, "minecraft:dyed_color"); + RegisterComponent(25, "minecraft:map_color"); + RegisterComponent(26, "minecraft:map_id"); + RegisterComponent(27, "minecraft:map_decorations"); + RegisterComponent(28, "minecraft:map_post_processing"); + RegisterComponent(29, "minecraft:charged_projectiles"); + RegisterComponent(30, "minecraft:bundle_contents"); + RegisterComponent(31, "minecraft:potion_contents"); + RegisterComponent(32, "minecraft:suspicious_stew_effects"); + RegisterComponent(33, "minecraft:writable_book_content"); + RegisterComponent(34, "minecraft:written_book_content"); + RegisterComponent(35, "minecraft:trim"); + RegisterComponent(36, "minecraft:debug_stick_state"); + RegisterComponent(37, "minecraft:entity_data"); + RegisterComponent(38, "minecraft:bucket_entity_data"); + RegisterComponent(39, "minecraft:block_entity_data"); + RegisterComponent(40, "minecraft:instrument"); + RegisterComponent(41, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(42, "minecraft:recipes"); + RegisterComponent(43, "minecraft:lodestone_tracker"); + RegisterComponent(44, "minecraft:firework_explosion"); + RegisterComponent(45, "minecraft:fireworks"); + RegisterComponent(46, "minecraft:profile"); + RegisterComponent(47, "minecraft:note_block_sound"); + RegisterComponent(48, "minecraft:banner_patterns"); + RegisterComponent(49, "minecraft:base_color"); + RegisterComponent(50, "minecraft:pot_decorations"); + RegisterComponent(51, "minecraft:container"); + RegisterComponent(52, "minecraft:block_state"); + RegisterComponent(53, "minecraft:bees"); + RegisterComponent(54, "minecraft:lock"); + RegisterComponent(55, "minecraft:container_loot"); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry121.cs new file mode 100644 index 00000000..5e22bd78 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry121.cs @@ -0,0 +1,71 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry121 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry121(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); + RegisterComponent(5, "minecraft:custom_name"); + RegisterComponent(6, "minecraft:item_name"); + RegisterComponent(7, "minecraft:lore"); + RegisterComponent(8, "minecraft:rarity"); + RegisterComponent(9, "minecraft:enchantments"); + RegisterComponent(10, "minecraft:can_place_on"); + RegisterComponent(11, "minecraft:can_break"); + RegisterComponent(12, "minecraft:attribute_modifiers"); + RegisterComponent(13, "minecraft:custom_model_data"); + RegisterComponent(14, "minecraft:hide_additional_tooltip"); + RegisterComponent(15, "minecraft:hide_tooltip"); + RegisterComponent(16, "minecraft:repair_cost"); + RegisterComponent(17, "minecraft:creative_slot_lock"); + RegisterComponent(18, "minecraft:enchantment_glint_override"); + RegisterComponent(19, "minecraft:intangible_projectile"); + RegisterComponent(20, "minecraft:food"); + RegisterComponent(21, "minecraft:fire_resistant"); + RegisterComponent(22, "minecraft:tool"); + RegisterComponent(23, "minecraft:stored_enchantments"); + RegisterComponent(24, "minecraft:dyed_color"); + RegisterComponent(25, "minecraft:map_color"); + RegisterComponent(26, "minecraft:map_id"); + RegisterComponent(27, "minecraft:map_decorations"); + RegisterComponent(28, "minecraft:map_post_processing"); + RegisterComponent(29, "minecraft:charged_projectiles"); + RegisterComponent(30, "minecraft:bundle_contents"); + RegisterComponent(31, "minecraft:potion_contents"); + RegisterComponent(32, "minecraft:suspicious_stew_effects"); + RegisterComponent(33, "minecraft:writable_book_content"); + RegisterComponent(34, "minecraft:written_book_content"); + RegisterComponent(35, "minecraft:trim"); + RegisterComponent(36, "minecraft:debug_stick_state"); + RegisterComponent(37, "minecraft:entity_data"); + RegisterComponent(38, "minecraft:bucket_entity_data"); + RegisterComponent(39, "minecraft:block_entity_data"); + RegisterComponent(40, "minecraft:instrument"); + RegisterComponent(41, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(42, "minecraft:jukebox_playable"); + RegisterComponent(43, "minecraft:recipes"); + RegisterComponent(44, "minecraft:lodestone_tracker"); + RegisterComponent(45, "minecraft:firework_explosion"); + RegisterComponent(46, "minecraft:fireworks"); + RegisterComponent(47, "minecraft:profile"); + RegisterComponent(48, "minecraft:note_block_sound"); + RegisterComponent(49, "minecraft:banner_patterns"); + RegisterComponent(50, "minecraft:base_color"); + RegisterComponent(51, "minecraft:pot_decorations"); + RegisterComponent(52, "minecraft:container"); + RegisterComponent(53, "minecraft:block_state"); + RegisterComponent(54, "minecraft:bees"); + RegisterComponent(55, "minecraft:lock"); + RegisterComponent(56, "minecraft:container_loot"); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs new file mode 100644 index 00000000..0be0c7d2 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs @@ -0,0 +1,126 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry12111 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry12111(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); + RegisterComponent(5, "minecraft:use_effects"); + RegisterComponent(6, "minecraft:custom_name"); + RegisterComponent(7, "minecraft:minimum_attack_charge"); + RegisterComponent(8, "minecraft:damage_type"); + RegisterComponent(9, "minecraft:item_name"); + RegisterComponent(10, "minecraft:item_model"); + RegisterComponent(11, "minecraft:lore"); + RegisterComponent(12, "minecraft:rarity"); + RegisterComponent(13, "minecraft:enchantments"); + RegisterComponent(14, "minecraft:can_place_on"); + RegisterComponent(15, "minecraft:can_break"); + RegisterComponent(16, "minecraft:attribute_modifiers"); + RegisterComponent(17, "minecraft:custom_model_data"); + RegisterComponent(18, "minecraft:tooltip_display"); + RegisterComponent(19, "minecraft:repair_cost"); + RegisterComponent(20, "minecraft:creative_slot_lock"); + RegisterComponent(21, "minecraft:enchantment_glint_override"); + RegisterComponent(22, "minecraft:intangible_projectile"); + RegisterComponent(23, "minecraft:food"); + RegisterComponent(24, "minecraft:consumable"); + RegisterComponent(25, "minecraft:use_remainder"); + RegisterComponent(26, "minecraft:use_cooldown"); + RegisterComponent(27, "minecraft:damage_resistant"); + RegisterComponent(28, "minecraft:tool"); + RegisterComponent(29, "minecraft:weapon"); + RegisterComponent(30, "minecraft:attack_range"); + RegisterComponent(31, "minecraft:enchantable"); + RegisterComponent(32, "minecraft:equippable"); + RegisterComponent(33, "minecraft:repairable"); + RegisterComponent(34, "minecraft:glider"); + RegisterComponent(35, "minecraft:tooltip_style"); + RegisterComponent(36, "minecraft:death_protection"); + RegisterComponent(37, "minecraft:blocks_attacks"); + RegisterComponent(38, "minecraft:piercing_weapon"); + RegisterComponent(39, "minecraft:kinetic_weapon"); + RegisterComponent(40, "minecraft:swing_animation"); + RegisterComponent(41, "minecraft:stored_enchantments"); + RegisterComponent(42, "minecraft:dyed_color"); + RegisterComponent(43, "minecraft:map_color"); + RegisterComponent(44, "minecraft:map_id"); + RegisterComponent(45, "minecraft:map_decorations"); + RegisterComponent(46, "minecraft:map_post_processing"); + RegisterComponent(47, "minecraft:charged_projectiles"); + RegisterComponent(48, "minecraft:bundle_contents"); + RegisterComponent(49, "minecraft:potion_contents"); + RegisterComponent(50, "minecraft:potion_duration_scale"); + RegisterComponent(51, "minecraft:suspicious_stew_effects"); + RegisterComponent(52, "minecraft:writable_book_content"); + RegisterComponent(53, "minecraft:written_book_content"); + RegisterComponent(54, "minecraft:trim"); + RegisterComponent(55, "minecraft:debug_stick_state"); + RegisterComponent(56, "minecraft:entity_data"); + RegisterComponent(57, "minecraft:bucket_entity_data"); + RegisterComponent(58, "minecraft:block_entity_data"); + RegisterComponent(59, "minecraft:instrument"); + RegisterComponent(60, "minecraft:provides_trim_material"); + RegisterComponent(61, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(62, "minecraft:jukebox_playable"); + RegisterComponent(63, "minecraft:provides_banner_patterns"); + RegisterComponent(64, "minecraft:recipes"); + RegisterComponent(65, "minecraft:lodestone_tracker"); + RegisterComponent(66, "minecraft:firework_explosion"); + RegisterComponent(67, "minecraft:fireworks"); + RegisterComponent(68, "minecraft:profile"); + RegisterComponent(69, "minecraft:note_block_sound"); + RegisterComponent(70, "minecraft:banner_patterns"); + RegisterComponent(71, "minecraft:base_color"); + RegisterComponent(72, "minecraft:pot_decorations"); + RegisterComponent(73, "minecraft:container"); + RegisterComponent(74, "minecraft:block_state"); + RegisterComponent(75, "minecraft:bees"); + RegisterComponent(76, "minecraft:lock"); + RegisterComponent(77, "minecraft:container_loot"); + + RegisterComponent(78, "minecraft:break_sound"); + RegisterComponent(79, "minecraft:villager/variant"); + RegisterComponent(80, "minecraft:wolf/variant"); + RegisterComponent(81, "minecraft:wolf/sound_variant"); + RegisterComponent(82, "minecraft:wolf/collar"); + RegisterComponent(83, "minecraft:fox/variant"); + RegisterComponent(84, "minecraft:salmon/size"); + RegisterComponent(85, "minecraft:parrot/variant"); + RegisterComponent(86, "minecraft:tropical_fish/pattern"); + RegisterComponent(87, "minecraft:tropical_fish/base_color"); + RegisterComponent(88, "minecraft:tropical_fish/pattern_color"); + RegisterComponent(89, "minecraft:mooshroom/variant"); + RegisterComponent(90, "minecraft:rabbit/variant"); + RegisterComponent(91, "minecraft:pig/variant"); + RegisterComponent(92, "minecraft:cow/variant"); + RegisterComponent(93, "minecraft:chicken/variant"); + RegisterComponent(94, "minecraft:zombie_nautilus/variant"); + RegisterComponent(95, "minecraft:frog/variant"); + RegisterComponent(96, "minecraft:horse/variant"); + RegisterComponent(97, "minecraft:painting/variant"); + RegisterComponent(98, "minecraft:llama/variant"); + RegisterComponent(99, "minecraft:axolotl/variant"); + RegisterComponent(100, "minecraft:cat/variant"); + RegisterComponent(101, "minecraft:cat/collar"); + RegisterComponent(102, "minecraft:sheep/color"); + RegisterComponent(103, "minecraft:shulker/color"); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1212.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1212.cs new file mode 100644 index 00000000..ffe89d0f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1212.cs @@ -0,0 +1,85 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry1212 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry1212(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); + RegisterComponent(5, "minecraft:custom_name"); + RegisterComponent(6, "minecraft:item_name"); + RegisterComponent(7, "minecraft:item_model"); + RegisterComponent(8, "minecraft:lore"); + RegisterComponent(9, "minecraft:rarity"); + RegisterComponent(10, "minecraft:enchantments"); + RegisterComponent(11, "minecraft:can_place_on"); + RegisterComponent(12, "minecraft:can_break"); + RegisterComponent(13, "minecraft:attribute_modifiers"); + if (dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_4_Version) + RegisterComponent(14, "minecraft:custom_model_data"); + else + RegisterComponent(14, "minecraft:custom_model_data"); + RegisterComponent(15, "minecraft:hide_additional_tooltip"); + RegisterComponent(16, "minecraft:hide_tooltip"); + RegisterComponent(17, "minecraft:repair_cost"); + RegisterComponent(18, "minecraft:creative_slot_lock"); + RegisterComponent(19, "minecraft:enchantment_glint_override"); + RegisterComponent(20, "minecraft:intangible_projectile"); + RegisterComponent(21, "minecraft:food"); + RegisterComponent(22, "minecraft:consumable"); + RegisterComponent(23, "minecraft:use_remainder"); + RegisterComponent(24, "minecraft:use_cooldown"); + RegisterComponent(25, "minecraft:damage_resistant"); + RegisterComponent(26, "minecraft:tool"); + RegisterComponent(27, "minecraft:enchantable"); + RegisterComponent(28, "minecraft:equippable"); + RegisterComponent(29, "minecraft:repairable"); + RegisterComponent(30, "minecraft:glider"); + RegisterComponent(31, "minecraft:tooltip_style"); + RegisterComponent(32, "minecraft:death_protection"); + RegisterComponent(33, "minecraft:stored_enchantments"); + RegisterComponent(34, "minecraft:dyed_color"); + RegisterComponent(35, "minecraft:map_color"); + RegisterComponent(36, "minecraft:map_id"); + RegisterComponent(37, "minecraft:map_decorations"); + RegisterComponent(38, "minecraft:map_post_processing"); + RegisterComponent(39, "minecraft:charged_projectiles"); + RegisterComponent(40, "minecraft:bundle_contents"); + RegisterComponent(41, "minecraft:potion_contents"); + RegisterComponent(42, "minecraft:suspicious_stew_effects"); + RegisterComponent(43, "minecraft:writable_book_content"); + RegisterComponent(44, "minecraft:written_book_content"); + RegisterComponent(45, "minecraft:trim"); + RegisterComponent(46, "minecraft:debug_stick_state"); + RegisterComponent(47, "minecraft:entity_data"); + RegisterComponent(48, "minecraft:bucket_entity_data"); + RegisterComponent(49, "minecraft:block_entity_data"); + RegisterComponent(50, "minecraft:instrument"); + RegisterComponent(51, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(52, "minecraft:jukebox_playable"); + RegisterComponent(53, "minecraft:recipes"); + RegisterComponent(54, "minecraft:lodestone_tracker"); + RegisterComponent(55, "minecraft:firework_explosion"); + RegisterComponent(56, "minecraft:fireworks"); + RegisterComponent(57, "minecraft:profile"); + RegisterComponent(58, "minecraft:note_block_sound"); + RegisterComponent(59, "minecraft:banner_patterns"); + RegisterComponent(60, "minecraft:base_color"); + RegisterComponent(61, "minecraft:pot_decorations"); + RegisterComponent(62, "minecraft:container"); + RegisterComponent(63, "minecraft:block_state"); + RegisterComponent(64, "minecraft:bees"); + RegisterComponent(65, "minecraft:lock"); + RegisterComponent(66, "minecraft:container_loot"); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs new file mode 100644 index 00000000..d78d87b9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs @@ -0,0 +1,138 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry1215 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + var uses1216AttributeAndEquippableFormats = dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_6_Version; + var usesTypedBeesFormat = dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version; + var usesTypedEntityDataFormat = dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version; + + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); // Changed from Unbreakable (Bool) to Unit (empty) in 1.21.5 + RegisterComponent(5, "minecraft:custom_name"); + RegisterComponent(6, "minecraft:item_name"); + RegisterComponent(7, "minecraft:item_model"); + RegisterComponent(8, "minecraft:lore"); + RegisterComponent(9, "minecraft:rarity"); + RegisterComponent(10, "minecraft:enchantments"); + RegisterComponent(11, "minecraft:can_place_on"); + RegisterComponent(12, "minecraft:can_break"); + if (uses1216AttributeAndEquippableFormats) + RegisterComponent(13, "minecraft:attribute_modifiers"); + else + RegisterComponent(13, "minecraft:attribute_modifiers"); + RegisterComponent(14, "minecraft:custom_model_data"); + // 15: tooltip_display (NEW, replaces hide_additional_tooltip + hide_tooltip) + RegisterComponent(15, "minecraft:tooltip_display"); + RegisterComponent(16, "minecraft:repair_cost"); + RegisterComponent(17, "minecraft:creative_slot_lock"); + RegisterComponent(18, "minecraft:enchantment_glint_override"); + RegisterComponent(19, "minecraft:intangible_projectile"); + RegisterComponent(20, "minecraft:food"); + RegisterComponent(21, "minecraft:consumable"); + RegisterComponent(22, "minecraft:use_remainder"); + RegisterComponent(23, "minecraft:use_cooldown"); + RegisterComponent(24, "minecraft:damage_resistant"); + RegisterComponent(25, "minecraft:tool"); + RegisterComponent(26, "minecraft:weapon"); // NEW + RegisterComponent(27, "minecraft:enchantable"); + if (uses1216AttributeAndEquippableFormats) + RegisterComponent(28, "minecraft:equippable"); + else + RegisterComponent(28, "minecraft:equippable"); + RegisterComponent(29, "minecraft:repairable"); + RegisterComponent(30, "minecraft:glider"); + RegisterComponent(31, "minecraft:tooltip_style"); + RegisterComponent(32, "minecraft:death_protection"); + RegisterComponent(33, "minecraft:blocks_attacks"); // NEW + RegisterComponent(34, "minecraft:stored_enchantments"); + RegisterComponent(35, "minecraft:dyed_color"); + RegisterComponent(36, "minecraft:map_color"); + RegisterComponent(37, "minecraft:map_id"); + RegisterComponent(38, "minecraft:map_decorations"); + RegisterComponent(39, "minecraft:map_post_processing"); + RegisterComponent(40, "minecraft:charged_projectiles"); + RegisterComponent(41, "minecraft:bundle_contents"); + RegisterComponent(42, "minecraft:potion_contents"); + RegisterComponent(43, "minecraft:potion_duration_scale"); // NEW + RegisterComponent(44, "minecraft:suspicious_stew_effects"); + RegisterComponent(45, "minecraft:writable_book_content"); + RegisterComponent(46, "minecraft:written_book_content"); + RegisterComponent(47, "minecraft:trim"); + RegisterComponent(48, "minecraft:debug_stick_state"); + if (usesTypedEntityDataFormat) + RegisterComponent(49, "minecraft:entity_data"); + else + RegisterComponent(49, "minecraft:entity_data"); + RegisterComponent(50, "minecraft:bucket_entity_data"); + if (usesTypedEntityDataFormat) + RegisterComponent(51, "minecraft:block_entity_data"); + else + RegisterComponent(51, "minecraft:block_entity_data"); + RegisterComponent(52, "minecraft:instrument"); // Changed to EitherHolder in 1.21.5 + RegisterComponent(53, "minecraft:provides_trim_material"); // NEW + RegisterComponent(54, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(55, "minecraft:jukebox_playable"); + RegisterComponent(56, "minecraft:provides_banner_patterns"); // NEW + RegisterComponent(57, "minecraft:recipes"); + RegisterComponent(58, "minecraft:lodestone_tracker"); + RegisterComponent(59, "minecraft:firework_explosion"); + RegisterComponent(60, "minecraft:fireworks"); + RegisterComponent(61, "minecraft:profile"); + RegisterComponent(62, "minecraft:note_block_sound"); + RegisterComponent(63, "minecraft:banner_patterns"); + RegisterComponent(64, "minecraft:base_color"); + RegisterComponent(65, "minecraft:pot_decorations"); + RegisterComponent(66, "minecraft:container"); + RegisterComponent(67, "minecraft:block_state"); + if (usesTypedBeesFormat) + RegisterComponent(68, "minecraft:bees"); + else + RegisterComponent(68, "minecraft:bees"); + RegisterComponent(69, "minecraft:lock"); + RegisterComponent(70, "minecraft:container_loot"); + + // Entity variant components (NEW in 1.21.5) + RegisterComponent(71, "minecraft:break_sound"); + RegisterComponent(72, "minecraft:villager/variant"); + RegisterComponent(73, "minecraft:wolf/variant"); + RegisterComponent(74, "minecraft:wolf/sound_variant"); + RegisterComponent(75, "minecraft:wolf/collar"); // DyeColor as VarInt + RegisterComponent(76, "minecraft:fox/variant"); + RegisterComponent(77, "minecraft:salmon/size"); + RegisterComponent(78, "minecraft:parrot/variant"); + RegisterComponent(79, "minecraft:tropical_fish/pattern"); + RegisterComponent(80, "minecraft:tropical_fish/base_color"); // DyeColor + RegisterComponent(81, "minecraft:tropical_fish/pattern_color"); // DyeColor + RegisterComponent(82, "minecraft:mooshroom/variant"); + RegisterComponent(83, "minecraft:rabbit/variant"); + RegisterComponent(84, "minecraft:pig/variant"); + RegisterComponent(85, "minecraft:cow/variant"); + RegisterComponent(86, "minecraft:chicken/variant"); // EitherHolder + RegisterComponent(87, "minecraft:frog/variant"); + RegisterComponent(88, "minecraft:horse/variant"); + RegisterComponent(89, "minecraft:painting/variant"); // Holder + RegisterComponent(90, "minecraft:llama/variant"); + RegisterComponent(91, "minecraft:axolotl/variant"); + RegisterComponent(92, "minecraft:cat/variant"); + RegisterComponent(93, "minecraft:cat/collar"); // DyeColor + RegisterComponent(94, "minecraft:sheep/color"); // DyeColor + RegisterComponent(95, "minecraft:shulker/color"); // DyeColor + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs new file mode 100644 index 00000000..c0b4281d --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs @@ -0,0 +1,132 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; + +public class StructuredComponentsRegistry261 : StructuredComponentRegistry +{ + public StructuredComponentsRegistry261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : base(dataTypes, itemPalette, subComponentRegistry) + { + RegisterComponent(0, "minecraft:custom_data"); + RegisterComponent(1, "minecraft:max_stack_size"); + RegisterComponent(2, "minecraft:max_damage"); + RegisterComponent(3, "minecraft:damage"); + RegisterComponent(4, "minecraft:unbreakable"); + RegisterComponent(5, "minecraft:use_effects"); + RegisterComponent(6, "minecraft:custom_name"); + RegisterComponent(7, "minecraft:minimum_attack_charge"); + RegisterComponent(8, "minecraft:damage_type"); + RegisterComponent(9, "minecraft:item_name"); + RegisterComponent(10, "minecraft:item_model"); + RegisterComponent(11, "minecraft:lore"); + RegisterComponent(12, "minecraft:rarity"); + RegisterComponent(13, "minecraft:enchantments"); + RegisterComponent(14, "minecraft:can_place_on"); + RegisterComponent(15, "minecraft:can_break"); + RegisterComponent(16, "minecraft:attribute_modifiers"); + RegisterComponent(17, "minecraft:custom_model_data"); + RegisterComponent(18, "minecraft:tooltip_display"); + RegisterComponent(19, "minecraft:repair_cost"); + RegisterComponent(20, "minecraft:creative_slot_lock"); + RegisterComponent(21, "minecraft:enchantment_glint_override"); + RegisterComponent(22, "minecraft:intangible_projectile"); + RegisterComponent(23, "minecraft:food"); + RegisterComponent(24, "minecraft:consumable"); + RegisterComponent(25, "minecraft:use_remainder"); + RegisterComponent(26, "minecraft:use_cooldown"); + RegisterComponent(27, "minecraft:damage_resistant"); + RegisterComponent(28, "minecraft:tool"); + RegisterComponent(29, "minecraft:weapon"); + RegisterComponent(30, "minecraft:attack_range"); + RegisterComponent(31, "minecraft:enchantable"); + RegisterComponent(32, "minecraft:equippable"); + RegisterComponent(33, "minecraft:repairable"); + RegisterComponent(34, "minecraft:glider"); + RegisterComponent(35, "minecraft:tooltip_style"); + RegisterComponent(36, "minecraft:death_protection"); + RegisterComponent(37, "minecraft:blocks_attacks"); + RegisterComponent(38, "minecraft:piercing_weapon"); + RegisterComponent(39, "minecraft:kinetic_weapon"); + RegisterComponent(40, "minecraft:swing_animation"); + RegisterComponent(41, "minecraft:additional_trade_cost"); // New in 26.1 + RegisterComponent(42, "minecraft:stored_enchantments"); + RegisterComponent(43, "minecraft:dye"); // New in 26.1 + RegisterComponent(44, "minecraft:dyed_color"); + RegisterComponent(45, "minecraft:map_color"); + RegisterComponent(46, "minecraft:map_id"); + RegisterComponent(47, "minecraft:map_decorations"); + RegisterComponent(48, "minecraft:map_post_processing"); + RegisterComponent(49, "minecraft:charged_projectiles"); + RegisterComponent(50, "minecraft:bundle_contents"); + RegisterComponent(51, "minecraft:potion_contents"); + RegisterComponent(52, "minecraft:potion_duration_scale"); + RegisterComponent(53, "minecraft:suspicious_stew_effects"); + RegisterComponent(54, "minecraft:writable_book_content"); + RegisterComponent(55, "minecraft:written_book_content"); + RegisterComponent(56, "minecraft:trim"); + RegisterComponent(57, "minecraft:debug_stick_state"); + RegisterComponent(58, "minecraft:entity_data"); + RegisterComponent(59, "minecraft:bucket_entity_data"); + RegisterComponent(60, "minecraft:block_entity_data"); + RegisterComponent(61, "minecraft:instrument"); + RegisterComponent(62, "minecraft:provides_trim_material"); + RegisterComponent(63, "minecraft:ominous_bottle_amplifier"); + RegisterComponent(64, "minecraft:jukebox_playable"); + RegisterComponent(65, "minecraft:provides_banner_patterns"); + RegisterComponent(66, "minecraft:recipes"); + RegisterComponent(67, "minecraft:lodestone_tracker"); + RegisterComponent(68, "minecraft:firework_explosion"); + RegisterComponent(69, "minecraft:fireworks"); + RegisterComponent(70, "minecraft:profile"); + RegisterComponent(71, "minecraft:note_block_sound"); + RegisterComponent(72, "minecraft:banner_patterns"); + RegisterComponent(73, "minecraft:base_color"); + RegisterComponent(74, "minecraft:pot_decorations"); + RegisterComponent(75, "minecraft:container"); + RegisterComponent(76, "minecraft:block_state"); + RegisterComponent(77, "minecraft:bees"); + RegisterComponent(78, "minecraft:lock"); + RegisterComponent(79, "minecraft:container_loot"); + + RegisterComponent(80, "minecraft:break_sound"); + RegisterComponent(81, "minecraft:villager/variant"); + RegisterComponent(82, "minecraft:wolf/variant"); + RegisterComponent(83, "minecraft:wolf/sound_variant"); + RegisterComponent(84, "minecraft:wolf/collar"); + RegisterComponent(85, "minecraft:fox/variant"); + RegisterComponent(86, "minecraft:salmon/size"); + RegisterComponent(87, "minecraft:parrot/variant"); + RegisterComponent(88, "minecraft:tropical_fish/pattern"); + RegisterComponent(89, "minecraft:tropical_fish/base_color"); + RegisterComponent(90, "minecraft:tropical_fish/pattern_color"); + RegisterComponent(91, "minecraft:mooshroom/variant"); + RegisterComponent(92, "minecraft:rabbit/variant"); + RegisterComponent(93, "minecraft:pig/variant"); + RegisterComponent(94, "minecraft:pig/sound_variant"); // New in 26.1 + RegisterComponent(95, "minecraft:cow/variant"); + RegisterComponent(96, "minecraft:cow/sound_variant"); // New in 26.1 + RegisterComponent(97, "minecraft:chicken/variant"); + RegisterComponent(98, "minecraft:chicken/sound_variant"); // New in 26.1 + RegisterComponent(99, "minecraft:zombie_nautilus/variant"); + RegisterComponent(100, "minecraft:frog/variant"); + RegisterComponent(101, "minecraft:horse/variant"); + RegisterComponent(102, "minecraft:painting/variant"); + RegisterComponent(103, "minecraft:llama/variant"); + RegisterComponent(104, "minecraft:axolotl/variant"); + RegisterComponent(105, "minecraft:cat/variant"); + RegisterComponent(106, "minecraft:cat/sound_variant"); // New in 26.1 + RegisterComponent(107, "minecraft:cat/collar"); + RegisterComponent(108, "minecraft:sheep/color"); + RegisterComponent(109, "minecraft:shulker/color"); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1206.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1206.cs new file mode 100644 index 00000000..2e270842 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1206.cs @@ -0,0 +1,22 @@ +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents; + +public class SubComponentRegistry1206 : SubComponentRegistry +{ + public SubComponentRegistry1206(DataTypes dataTypes) : base(dataTypes) + { + RegisterSubComponent(SubComponents.BlockPredicate); + RegisterSubComponent(SubComponents.BlockSet); + RegisterSubComponent(SubComponents.Property); + RegisterSubComponent(SubComponents.Attribute); + RegisterSubComponent(SubComponents.Effect); + RegisterSubComponent(SubComponents.PotionEffect); + RegisterSubComponent(SubComponents.Details); + RegisterSubComponent(SubComponents.Rule); + RegisterSubComponent(SubComponents.FireworkExplosion); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs new file mode 100644 index 00000000..2afd2852 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs @@ -0,0 +1,16 @@ +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents; + +public class SubComponentRegistry121 : SubComponentRegistry1206 +{ + public SubComponentRegistry121(DataTypes dataTypes) : base(dataTypes) + { + ReplaceSubComponent(SubComponents.Attribute); + RegisterSubComponent(SubComponents.SoundEvent); + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1212.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1212.cs new file mode 100644 index 00000000..ba5df821 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry1212.cs @@ -0,0 +1,11 @@ +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents; + +public class SubComponentRegistry1212 : SubComponentRegistry121 +{ + public SubComponentRegistry1212(DataTypes dataTypes) : base(dataTypes) + { + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/StructuredComponentsHandler.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/StructuredComponentsHandler.cs new file mode 100644 index 00000000..48f0747a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/StructuredComponentsHandler.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents; + +public class StructuredComponentsHandler +{ + private StructuredComponentRegistry ComponentRegistry { get; } + + public StructuredComponentsHandler( + int protocolVersion, + DataTypes dataTypes, + ItemPalette itemPalette) + { + // Get the appropriate subcomponent registry type based on the protocol version and then instantiate it + var subcomponentRegistryType = protocolVersion switch + { + Protocol18Handler.MC_1_20_6_Version => typeof(SubComponentRegistry1206), + Protocol18Handler.MC_1_21_Version => typeof(SubComponentRegistry121), + >= Protocol18Handler.MC_1_21_2_Version => typeof(SubComponentRegistry1212), + _ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for subcomponent registries!") + }; + + var subcomponentRegistry = Activator.CreateInstance(subcomponentRegistryType, dataTypes) as SubComponentRegistry + ?? throw new InvalidOperationException($"Failed to instantiate a component registry for type {nameof(subcomponentRegistryType)}"); + + // Get the appropriate component registry type based on the protocol version and then instantiate it + var registryType = protocolVersion switch + { + Protocol18Handler.MC_1_20_6_Version => typeof(StructuredComponentsRegistry1206), + Protocol18Handler.MC_1_21_Version => typeof(StructuredComponentsRegistry121), + >= Protocol18Handler.MC_26_1_Version => typeof(StructuredComponentsRegistry261), + >= Protocol18Handler.MC_1_21_11_Version => typeof(StructuredComponentsRegistry12111), + >= Protocol18Handler.MC_1_21_5_Version => typeof(StructuredComponentsRegistry1215), + >= Protocol18Handler.MC_1_21_2_Version => typeof(StructuredComponentsRegistry1212), + _ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for structured component registries!") + }; + + ComponentRegistry = Activator.CreateInstance(registryType, dataTypes, itemPalette, subcomponentRegistry) as StructuredComponentRegistry + ?? throw new InvalidOperationException($"Failed to instantiate a component registry for type {nameof(registryType)}"); + } + + public StructuredComponent Parse(int componentId, Queue data) + { + return ComponentRegistry.ParseComponent(componentId, data); + } + + public string GetComponentName(int componentId) + { + try + { + return ComponentRegistry.GetComponentNameById(componentId); + } + catch + { + return ""; + } + } +} diff --git a/MinecraftClient/Protocol/Handlers/ZlibUtils.cs b/MinecraftClient/Protocol/Handlers/ZlibUtils.cs index 62f8bf85..bff4131d 100644 --- a/MinecraftClient/Protocol/Handlers/ZlibUtils.cs +++ b/MinecraftClient/Protocol/Handlers/ZlibUtils.cs @@ -1,12 +1,10 @@ -using Ionic.Zlib; +using System.IO; +using System.IO.Compression; namespace MinecraftClient.Protocol.Handlers { /// /// Quick Zlib compression handling for network packet compression. - /// Note: Underlying compression handling is taken from the DotNetZip Library. - /// This library is open source and provided under the Microsoft Public License. - /// More info about DotNetZip at dotnetzip.codeplex.com. /// public static class ZlibUtils { @@ -17,16 +15,13 @@ namespace MinecraftClient.Protocol.Handlers /// Compressed data as a byte array public static byte[] Compress(byte[] to_compress) { - byte[] data; - using (System.IO.MemoryStream memstream = new()) + using MemoryStream memstream = new(); + using (ZLibStream stream = new(memstream, CompressionMode.Compress, leaveOpen: true)) { - using (ZlibStream stream = new(memstream, CompressionMode.Compress)) - { - stream.Write(to_compress, 0, to_compress.Length); - } - data = memstream.ToArray(); + stream.Write(to_compress, 0, to_compress.Length); } - return data; + + return memstream.ToArray(); } /// @@ -37,10 +32,20 @@ namespace MinecraftClient.Protocol.Handlers /// Decompressed data as a byte array public static byte[] Decompress(byte[] to_decompress, int size_uncompressed) { - ZlibStream stream = new(new System.IO.MemoryStream(to_decompress, false), CompressionMode.Decompress); + using MemoryStream compressedStream = new(to_decompress, writable: false); + using ZLibStream stream = new(compressedStream, CompressionMode.Decompress); + byte[] packetData_decompressed = new byte[size_uncompressed]; - stream.Read(packetData_decompressed, 0, size_uncompressed); - stream.Close(); + int totalRead = 0; + while (totalRead < size_uncompressed) + { + int read = stream.Read(packetData_decompressed, totalRead, size_uncompressed - totalRead); + if (read <= 0) + break; + + totalRead += read; + } + return packetData_decompressed; } @@ -51,12 +56,14 @@ namespace MinecraftClient.Protocol.Handlers /// Decompressed data as byte array public static byte[] Decompress(byte[] to_decompress) { - ZlibStream stream = new(new System.IO.MemoryStream(to_decompress, false), CompressionMode.Decompress); + using MemoryStream compressedStream = new(to_decompress, writable: false); + using ZLibStream stream = new(compressedStream, CompressionMode.Decompress); byte[] buffer = new byte[16 * 1024]; - using System.IO.MemoryStream decompressedBuffer = new(); + using MemoryStream decompressedBuffer = new(); int read; while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) decompressedBuffer.Write(buffer, 0, read); + return decompressedBuffer.ToArray(); } } diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index eb5d03ed..ee604ceb 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -19,7 +19,7 @@ namespace MinecraftClient.Protocol /// Start the login procedure once connected to the server /// /// True if login was successful - bool Login(PlayerKeyPair? playerKeyPair, Session.SessionToken session); + bool Login(PlayerKeyPair? playerKeyPair, Session.SessionToken session, bool isTransfer = false); /// /// Disconnect from the server @@ -48,6 +48,14 @@ namespace MinecraftClient.Protocol /// True if successfully sent bool SendChatMessage(string message, PlayerKeyPair? playerKeyPair = null); + /// + /// Send a custom click action packet introduced for dialogs in Minecraft 1.21.6. + /// + /// Custom action resource location + /// Optional NBT payload + /// True if successfully sent + bool SendCustomClickAction(string id, Dictionary? payload); + /// /// Allow to respawn after death /// @@ -79,10 +87,11 @@ namespace MinecraftClient.Protocol /// /// The new location /// True if the player is on the ground + /// True if the player is colliding horizontally /// The new yaw (optional) /// The new pitch (optional) /// True if packet was successfully sent - bool SendLocationUpdate(Location location, bool onGround, float? yaw, float? pitch); + bool SendLocationUpdate(Location location, bool onGround, bool horizontalCollision, float? yaw, float? pitch); /// /// Send a plugin channel packet to the server. @@ -189,6 +198,26 @@ namespace MinecraftClient.Protocol bool ClickContainerButton(int windowId, int buttonId); + /// + /// Send a place recipe packet to the server for the active recipe book container. + /// + /// Id of the window being clicked + /// Recipe identifier to craft + /// True to craft as many items as possible + /// True if packet was successfully sent + bool SendPlaceRecipe(int windowId, string recipeId, bool makeAll); + + /// + /// Send a book edit/sign packet for the currently held writable book. + /// + /// Current held writable book + /// Book pages + /// Title when signing, otherwise null + /// Current player name when signing + /// Selected hotbar slot, 0-8 + /// True if packet was successfully sent + bool SendEditBook(Item currentBook, IReadOnlyList pages, string? title, string author, int selectedHotbarSlot); + /// /// Plays animation /// @@ -261,7 +290,7 @@ namespace MinecraftClient.Protocol /// /// bool SendPlayerSession(PlayerKeyPair? playerKeyPair); - + /// /// Send the server a command to type in the item name in the Anvil inventory when it's open. /// @@ -273,5 +302,18 @@ namespace MinecraftClient.Protocol /// /// Net read thread ID int GetNetMainThreadId(); + + /// + /// Send the server a requested cookie + /// + /// The cookie identifier/name + /// The cookie data byte array + bool SendCookieResponse(string name, byte[]? data); + + /// + /// Send the server known data packs + /// + /// The clist of tuples containing info about the kown data packs (namespace, id, version) + bool SendKnownDataPacks(List<(string, string, string)> knownDataPacks); } } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 138913ec..447c7287 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MinecraftClient.Dialogs; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; @@ -44,6 +45,11 @@ namespace MinecraftClient.Protocol int GetProtocolVersion(); Container? GetInventory(int inventoryID); ILogger GetLogger(); + void GetCookie(string key, out byte[]? data); + void SetCookie(string key, byte[] data); + void DeleteCookie(string key); + + void Transfer(string newHost, int newPort); /// /// Invoke a task on the main thread, wait for completion and retrieve return value. @@ -88,6 +94,31 @@ namespace MinecraftClient.Protocol /// Message received public void OnTextReceived(ChatMessage message); + /// + /// Called when the server synchronizes a dialog registry entry. + /// + void OnDialogRegistryData(int protocolId, string resourceId, DialogDefinition dialog); + + /// + /// Called when the server shows a custom dialog. + /// + void OnDialogShown(DialogDefinition dialog, DialogPhase phase); + + /// + /// Called when the server shows a custom dialog by registry protocol ID. + /// + void OnDialogRegistryReferenceShown(int protocolId, DialogPhase phase); + + /// + /// Called when the server clears the current custom dialog. + /// + void OnDialogCleared(); + + /// + /// Called when the server sends updated server links. + /// + void OnServerLinksUpdated(IReadOnlyList links); + /// /// Will be called every animations of the hit and place block /// @@ -183,7 +214,7 @@ namespace MinecraftClient.Protocol void OnConnectionLost(ChatBot.DisconnectReason reason, string message); /// - /// Called ~10 times per second (10 ticks per second) + /// Called 20 times per second (20 ticks per second) /// Useful for updating bots in other parts of the program /// void OnUpdate(); @@ -219,6 +250,12 @@ namespace MinecraftClient.Protocol /// The data from the channel void OnPluginChannelMessage(string channel, byte[] data); + /// + /// Called when the server asks the client to open a book UI. + /// + /// Book hand, 0 main hand, 1 off hand. + void OnBookOpen(int hand); + /// /// Called when an entity has spawned /// @@ -290,6 +327,16 @@ namespace MinecraftClient.Protocol /// TRUE if on ground void OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround); + /// + /// Called when an entity velocity update packet is received. + /// Velocity values are in blocks per tick. + /// + /// Entity ID + /// Velocity X + /// Velocity Y + /// Velocity Z + void OnEntityVelocity(int entityID, double velocityX, double velocityY, double velocityZ); + /// /// Called when additional properties have been received for an entity /// @@ -366,6 +413,17 @@ namespace MinecraftClient.Protocol /// Amount of affected blocks void OnExplosion(Location location, float strength, int affectedBlocks); + /// + /// Called when a sound packet is received. + /// + /// Sound key if available, otherwise null + /// Sound location for world sounds, or null if unavailable + /// Sound category id + /// Sound volume + /// Sound pitch + /// Source entity id for entity-sound packets, if any + void OnSoundEffect(string? soundName, Location? location, int category, float volume, float pitch, int? entityID); + /// /// Called when a player's game mode has changed /// @@ -429,6 +487,19 @@ namespace MinecraftClient.Protocol /// factorCodec void OnEntityEffect(int entityid, Effects effect, int amplifier, int duration, byte flags, bool hasFactorData, Dictionary? factorCodec); + /// + /// Called when an entity has an effect removed + /// + /// Entity ID + /// Effect that was removed + void OnRemoveEntityEffect(int entityid, Effects effect); + + /// + /// Get the player's active effects + /// + /// Dictionary of active effects + Dictionary GetPlayerEffects(); + /// /// Called when Soreboard Objective /// @@ -450,13 +521,30 @@ namespace MinecraftClient.Protocol /// Number format: 0 - blank, 1 - styled, 2 - fixed void OnUpdateScore(string entityName, int action, string objectiveName, string objectiveDisplayName, int objectiveValue, int numberFormat); + /// + /// Called when a Teams packet is received from the server. + /// + /// Internal team name (up to 16 chars) + /// 0=create, 1=remove, 2=update, 3=add players, 4=remove players + /// Display name (formatted). Present when method is 0 or 2. + /// Bit 0=allowFriendlyFire, bit 1=seeFriendlyInvisibles. Present when method is 0 or 2. + /// Nametag visibility rule string. Present when method is 0 or 2. + /// Collision rule string. Present when method is 0 or 2. + /// ChatFormatting color value (-1=none). Present when method is 0 or 2. + /// Member name prefix (formatted). Present when method is 0 or 2. + /// Member name suffix (formatted). Present when method is 0 or 2. + /// Player/entity names. Present when method is 0, 3, or 4. + void OnTeam(string teamName, byte method, string displayName, byte friendlyFlags, + string nameTagVisibility, string collisionRule, int color, + string prefix, string suffix, List players); + /// /// Called when the client received the Tab Header and Footer /// /// Header /// Footer void OnTabListHeaderAndFooter(string header, string footer); - + /// /// Called when tradeList is received from server /// @@ -490,6 +578,13 @@ namespace MinecraftClient.Protocol /// The block public void OnBlockChange(Location location, Block block); + /// + /// Called when block entity update data is received for a loaded block. + /// + /// The block location. + /// The block entity NBT payload. + public void OnBlockEntityData(Location location, Dictionary? nbt); + /// /// Called when "AutoComplete" completes. /// @@ -499,6 +594,33 @@ namespace MinecraftClient.Protocol public void SetCanSendMessage(bool canSendMessage); + /// + /// Called when recipe book recipes are added or replaced. + /// + /// Recipe entries to add + /// True to replace the currently tracked recipe book entries + public void OnRecipeBookAdd(RecipeBookRecipeEntry[] recipes, bool replace); + + /// + /// Called when recipe book recipes are removed. + /// + /// Recipe identifiers to remove + public void OnRecipeBookRemove(string[] recipeIds); + + /// + /// Called when achievement/advancement data is received from the server. + /// + /// Achievements that were added or updated + /// IDs of achievements that were removed + /// True if all existing state should be cleared before applying + public void OnAchievementsUpdate(IReadOnlyList added, IReadOnlyList removedIds, bool reset); + + /// + /// Called when the server selects an advancement tab. + /// + /// The tab identifier, or null if no tab is selected + public void OnSelectAdvancementTab(string? tabId); + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom @@ -508,7 +630,7 @@ namespace MinecraftClient.Protocol /// True if packet was successfully sent bool ClickContainerButton(int windowId, int buttonId); - + /// /// Send a rename item packet when the anvil inventory is open and there is an item in the first slot /// diff --git a/MinecraftClient/Protocol/Message/ChatMessage.cs b/MinecraftClient/Protocol/Message/ChatMessage.cs index 3088d85e..832fa19b 100644 --- a/MinecraftClient/Protocol/Message/ChatMessage.cs +++ b/MinecraftClient/Protocol/Message/ChatMessage.cs @@ -64,7 +64,7 @@ namespace MinecraftClient.Protocol.Message public LastSeenMessageList.AcknowledgedMessage? ToLastSeenMessageEntry() { - return signature != null ? new LastSeenMessageList.AcknowledgedMessage(senderUUID, signature, true) : null; + return signature is not null ? new LastSeenMessageList.AcknowledgedMessage(senderUUID, signature, true) : null; } public bool LacksSender() diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index bf3c1e74..27c5eab8 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -1,13 +1,18 @@ -using System; +using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Net.Http.Json; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Tomlet; +using Tomlet.Models; using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Message @@ -31,15 +36,58 @@ namespace MinecraftClient.Protocol.Message public static Dictionary? ChatId2Type; + // Used to store Chat Types in 1.20.6+ + public static void ReadChatType(Dictionary data) + { + var chatTypeDictionary = ChatId2Type ?? new Dictionary(); + + foreach (var (chatId, chatName) in data) + { + chatTypeDictionary[chatId] = chatName switch + { + "minecraft:chat" => MessageType.CHAT, + "minecraft:emote_command" => MessageType.EMOTE_COMMAND, + "minecraft:msg_command_incoming" => MessageType.MSG_COMMAND_INCOMING, + "minecraft:msg_command_outgoing" => MessageType.MSG_COMMAND_OUTGOING, + "minecraft:say_command" => MessageType.SAY_COMMAND, + "minecraft:team_msg_command_incoming" => MessageType.TEAM_MSG_COMMAND_INCOMING, + "minecraft:team_msg_command_outgoing" => MessageType.TEAM_MSG_COMMAND_OUTGOING, + _ => MessageType.CHAT, + }; + } + + ChatId2Type = chatTypeDictionary; + } + public static void ReadChatType(Dictionary registryCodec) { Dictionary chatTypeDictionary = ChatId2Type ?? new(); - var chatTypeListNbt = - (object[])(((Dictionary)registryCodec["minecraft:chat_type"])["value"]); + + // Check if the chat type registry is in the correct format + if (!registryCodec.ContainsKey("minecraft:chat_type")) + { + + // If not, then we force the registry to be in the correct format + if (registryCodec.ContainsKey("chat_type")) + { + + foreach (var key in registryCodec.Keys.ToArray()) + { + // Skip entries with a namespace already + if (key.Contains(':', StringComparison.OrdinalIgnoreCase)) continue; + + // Assume all other entries are in the minecraft namespace + registryCodec["minecraft:" + key] = registryCodec[key]; + registryCodec.Remove(key); + } + } + } + + var chatTypeListNbt = (object[])(((Dictionary)registryCodec["minecraft:chat_type"])["value"]); foreach (var (chatName, chatId) in from Dictionary chatTypeNbt in chatTypeListNbt - let chatName = (string)chatTypeNbt["name"] - let chatId = (int)chatTypeNbt["id"] - select (chatName, chatId)) + let chatName = (string)chatTypeNbt["name"] + let chatId = (int)chatTypeNbt["id"] + select (chatName, chatId)) { chatTypeDictionary[chatId] = chatName switch { @@ -86,7 +134,7 @@ namespace MinecraftClient.Protocol.Message { string sender = message.isSenderJson ? ParseText(message.displayName!) : message.displayName!; string content; - if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) + if (Config.Signature.ShowModifiedChat && message.unsignedContent is not null) { content = ParseText(message.unsignedContent!); if (string.IsNullOrEmpty(content)) @@ -163,7 +211,12 @@ namespace MinecraftClient.Protocol.Message /// Color code private static string Color2tag(string colorname) { - return colorname.ToLower() switch + string lower = colorname.ToLower(); + + if (lower.Length == 7 && lower[0] == '#' && IsHexColor(lower)) + return "§" + lower; + + return lower switch { #pragma warning disable format // @formatter:off @@ -190,6 +243,17 @@ namespace MinecraftClient.Protocol.Message }; } + private static bool IsHexColor(string s) + { + for (int i = 1; i < s.Length; i++) + { + char c = s[i]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) + return false; + } + return true; + } + /// /// Specify whether translation rules have been loaded /// @@ -200,12 +264,48 @@ namespace MinecraftClient.Protocol.Message /// private static Dictionary TranslationRules = new(); + private sealed class TranslationLayer(string identifier, Dictionary translations) + { + public string Identifier { get; } = identifier; + public Dictionary Translations { get; } = translations; + } + + private sealed class ResourcePackTranslationCacheEntry + { + public string CacheVersion { get; init; } = string.Empty; + public string Language { get; init; } = string.Empty; + public string SourceUrl { get; init; } = string.Empty; + public string SourceHash { get; init; } = string.Empty; + public Dictionary Translations { get; init; } = []; + } + + private sealed class ForgeModTranslationCacheEntry + { + public string CacheVersion { get; init; } = string.Empty; + public string Language { get; init; } = string.Empty; + public string SourceHash { get; init; } = string.Empty; + public Dictionary> TranslationsByModId { get; init; } = []; + } + + private const long MaxResourcePackDownloadBytes = 256L * 1024 * 1024; + private const int ResourcePackDownloadBufferSize = 81920; + private const string ResourcePackTranslationCacheVersion = "1"; + private const string ForgeModTranslationCacheVersion = "1"; + private const string LocalForgeModTranslationDirectory = "mods"; + + private static readonly List ForgeModTranslationLayers = []; + private static readonly List ResourcePackTranslationLayers = []; + private static readonly HttpClient ResourcePackHttpClient = new(); + /// /// Initialize translation rules. /// Necessary for properly printing some chat messages. /// public static void InitTranslations() { + ForgeModTranslationLayers.Clear(); + ResourcePackTranslationLayers.Clear(); + if (!RulesInitialized) { InitRules(); @@ -278,7 +378,7 @@ namespace MinecraftClient.Protocol.Message Task?> fetckFileTask = httpClient.GetFromJsonAsync>(translation_file_location); fetckFileTask.Wait(); - if (fetckFileTask.Result != null && fetckFileTask.Result.Count > 0) + if (fetckFileTask.Result is not null && fetckFileTask.Result.Count > 0) { TranslationRules = fetckFileTask.Result; TranslationRules["Version"] = TranslationsFile_Version; @@ -326,10 +426,123 @@ namespace MinecraftClient.Protocol.Message public static string? TranslateString(string rulename) { - if (TranslationRules.TryGetValue(rulename, out string? result)) - return result; - else - return null; + return TryGetTranslationRule(rulename, out string? result) ? result : null; + } + + public static void LoadResourcePackTranslations(string packIdentifier, string url, string hash) + { + ArgumentException.ThrowIfNullOrEmpty(packIdentifier); + ArgumentException.ThrowIfNullOrEmpty(url); + + if (!Config.Main.Advanced.LoadResourcePackTranslations) + return; + + if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? resourcePackUri) + || resourcePackUri.Scheme is not "http" and not "https") + { + return; + } + + string cacheFilePath = GetResourcePackTranslationCacheFilePath(resourcePackUri, hash); + if (TryLoadCachedResourcePackTranslations(cacheFilePath, resourcePackUri, hash, out Dictionary? cachedTranslations)) + { + ReplaceResourcePackTranslations(packIdentifier, cachedTranslations); + return; + } + + string temporaryFilePath = Path.GetTempFileName(); + try + { + DownloadResourcePack(resourcePackUri, hash, temporaryFilePath); + using FileStream resourcePackFile = File.OpenRead(temporaryFilePath); + Dictionary resourcePackTranslations = ExtractResourcePackTranslations(resourcePackFile); + ReplaceResourcePackTranslations(packIdentifier, resourcePackTranslations); + SaveCachedResourcePackTranslations(cacheFilePath, resourcePackUri, hash, resourcePackTranslations); + } + catch (HttpRequestException) + { + } + catch (IOException) + { + } + catch (InvalidDataException) + { + } + catch (JsonException) + { + } + finally + { + try + { + File.Delete(temporaryFilePath); + } + catch (IOException) + { + } + } + } + + public static void RemoveResourcePackTranslations(string packIdentifier) + { + ResourcePackTranslationLayers.RemoveAll(layer => + layer.Identifier.Equals(packIdentifier, StringComparison.Ordinal)); + } + + public static void ClearResourcePackTranslations() + { + ResourcePackTranslationLayers.Clear(); + } + + public static void LoadForgeModTranslations(IEnumerable modIds) + { + ArgumentNullException.ThrowIfNull(modIds); + + ForgeModTranslationLayers.Clear(); + + if (!Config.Main.Advanced.LoadForgeModTranslations) + return; + + HashSet requestedModIds = new( + modIds + .Where(static modId => !string.IsNullOrWhiteSpace(modId)) + .Select(static modId => NormalizeForgeModId(modId)), + StringComparer.OrdinalIgnoreCase); + + if (requestedModIds.Count == 0) + return; + + Dictionary> translationsByModId = + new(StringComparer.OrdinalIgnoreCase); + + foreach (string modDirectory in GetForgeModTranslationDirectories()) + { + foreach (string modJarPath in Directory.EnumerateFiles(modDirectory, "*.jar")) + { + try + { + MergeForgeModTranslations(modJarPath, requestedModIds, translationsByModId); + } + catch (IOException) + { + } + catch (InvalidDataException) + { + } + catch (JsonException) + { + } + } + } + + foreach (string modId in requestedModIds) + { + if (translationsByModId.TryGetValue(modId, out Dictionary? translations) + && translations.Count > 0) + { + ForgeModTranslationLayers.Add(new TranslationLayer(modId, translations)); + } + } } /// @@ -347,10 +560,9 @@ namespace MinecraftClient.Protocol.Message RulesInitialized = true; } - if (TranslationRules.ContainsKey(rulename)) + if (TryGetTranslationRule(rulename, out string? rule)) { int using_idx = 0; - string rule = TranslationRules[rulename]; StringBuilder result = new(); for (int i = 0; i < rule.Length; i++) { @@ -392,166 +604,765 @@ namespace MinecraftClient.Protocol.Message else return "[" + rulename + "] " + string.Join(" ", using_data); } + private static bool TryGetTranslationRule(string rulename, [NotNullWhen(true)] out string? result) + { + for (int i = ResourcePackTranslationLayers.Count - 1; i >= 0; i--) + { + if (ResourcePackTranslationLayers[i].Translations.TryGetValue(rulename, out result)) + return true; + } + + for (int i = ForgeModTranslationLayers.Count - 1; i >= 0; i--) + { + if (ForgeModTranslationLayers[i].Translations.TryGetValue(rulename, out result)) + return true; + } + + return TranslationRules.TryGetValue(rulename, out result); + } + + private static void DownloadResourcePack(Uri resourcePackUri, string hash, string temporaryFilePath) + { + using HttpResponseMessage response = + ResourcePackHttpClient.GetAsync(resourcePackUri, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); + response.EnsureSuccessStatusCode(); + + using Stream resourcePackStream = response.Content.ReadAsStream(); + using FileStream temporaryFile = File.Create(temporaryFilePath); + using IncrementalHash incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); + + byte[] buffer = new byte[ResourcePackDownloadBufferSize]; + long totalBytes = 0; + + while (true) + { + int bytesRead = resourcePackStream.Read(buffer, 0, buffer.Length); + if (bytesRead <= 0) + break; + + totalBytes += bytesRead; + if (totalBytes > MaxResourcePackDownloadBytes) + throw new InvalidDataException(); + + temporaryFile.Write(buffer, 0, bytesRead); + + if (hash.Length == 40) + incrementalHash.AppendData(buffer, 0, bytesRead); + } + + if (hash.Length == 40) + { + string downloadedHash = Convert.ToHexString(incrementalHash.GetHashAndReset()); + if (!downloadedHash.Equals(hash, StringComparison.OrdinalIgnoreCase)) + throw new InvalidDataException($"Resource pack hash mismatch for {resourcePackUri}. Expected {hash}, got {downloadedHash}."); + } + } + + private static Dictionary ExtractResourcePackTranslations(Stream resourcePackStream) + { + var mergedTranslations = new Dictionary(StringComparer.Ordinal); + var selectedLanguageTranslations = new Dictionary(StringComparer.Ordinal); + string selectedLanguage = Config.Main.Advanced.Language; + + using ZipArchive archive = new(resourcePackStream, ZipArchiveMode.Read, leaveOpen: true); + + foreach (ZipArchiveEntry entry in archive.Entries) + { + if (!TryGetResourcePackLanguage(entry.FullName, out string? language)) + continue; + + if (language.Equals("en_us", StringComparison.OrdinalIgnoreCase)) + { + MergeTranslationsFromZipEntry(entry, mergedTranslations); + } + else if (language.Equals(selectedLanguage, StringComparison.OrdinalIgnoreCase)) + { + MergeTranslationsFromZipEntry(entry, selectedLanguageTranslations); + } + } + + foreach (var entry in selectedLanguageTranslations) + mergedTranslations[entry.Key] = entry.Value; + + return mergedTranslations; + } + + private static bool TryGetResourcePackLanguage(string entryPath, [NotNullWhen(true)] out string? language) + { + language = null; + + string[] pathParts = entryPath + .Replace('\\', '/') + .Split('/', StringSplitOptions.RemoveEmptyEntries); + + if (pathParts.Length != 4 + || !pathParts[0].Equals("assets", StringComparison.OrdinalIgnoreCase) + || !pathParts[2].Equals("lang", StringComparison.OrdinalIgnoreCase) + || !pathParts[3].EndsWith(".json", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + language = Path.GetFileNameWithoutExtension(pathParts[3]); + return !string.IsNullOrEmpty(language); + } + + private static void MergeTranslationsFromZipEntry(ZipArchiveEntry entry, Dictionary translations) + { + using Stream entryStream = entry.Open(); + Dictionary? entryTranslations = + JsonSerializer.Deserialize>(entryStream); + + if (entryTranslations is null) + return; + + foreach (var (key, value) in entryTranslations) + translations[key] = value; + } + + private static void ReplaceResourcePackTranslations(string packIdentifier, Dictionary translations) + { + RemoveResourcePackTranslations(packIdentifier); + + if (translations.Count > 0) + ResourcePackTranslationLayers.Add(new TranslationLayer(packIdentifier, translations)); + } + + private static void MergeForgeModTranslations(string modJarPath, HashSet requestedModIds, + Dictionary> translationsByModId) + { + string sourceHash = ComputeFileSha256(modJarPath); + string cacheFilePath = GetForgeModTranslationCacheFilePath(sourceHash); + if (!TryLoadCachedForgeModTranslations(cacheFilePath, sourceHash, + out Dictionary>? cachedTranslations)) + { + using FileStream modJarStream = File.OpenRead(modJarPath); + cachedTranslations = ExtractForgeModTranslations(modJarStream); + SaveCachedForgeModTranslations(cacheFilePath, sourceHash, cachedTranslations); + } + + Dictionary> archiveTranslations = cachedTranslations + .Where(static entry => entry.Value.Count > 0) + .Where(entry => requestedModIds.Contains(entry.Key)) + .ToDictionary(static entry => entry.Key, static entry => entry.Value, StringComparer.OrdinalIgnoreCase); + + foreach (var (modId, translations) in archiveTranslations) + translationsByModId[modId] = translations; + } + + private static Dictionary> ExtractForgeModTranslations(Stream modJarStream) + { + using ZipArchive archive = new(modJarStream, ZipArchiveMode.Read, leaveOpen: true); + HashSet archiveModIds = GetForgeModIds(archive); + if (archiveModIds.Count == 0) + return new Dictionary>(StringComparer.OrdinalIgnoreCase); + + return ExtractForgeModTranslations(archive, archiveModIds); + } + + private static HashSet GetForgeModIds(ZipArchive archive) + { + ZipArchiveEntry? modsTomlEntry = archive.GetEntry("META-INF/mods.toml") + ?? archive.GetEntry("META-INF/MODS.TOML"); + + if (modsTomlEntry is null) + return []; + + using StreamReader reader = new(modsTomlEntry.Open(), Encoding.UTF8, detectEncodingFromByteOrderMarks: true); + TomlDocument document = new TomlParser().Parse(reader.ReadToEnd()); + if (!document.TryGetValue("mods", out TomlValue? modsValue) || modsValue is not TomlArray modsArray) + return []; + + HashSet modIds = new(StringComparer.OrdinalIgnoreCase); + foreach (TomlValue modValue in modsArray) + { + if (modValue is not TomlTable modTable || !modTable.ContainsKey("modId")) + continue; + + string modId = modTable.GetString("modId"); + if (!string.IsNullOrWhiteSpace(modId)) + modIds.Add(NormalizeForgeModId(modId)); + } + + return modIds; + } + + private static Dictionary> ExtractForgeModTranslations(ZipArchive archive, HashSet requestedModIds) + { + string selectedLanguage = NormalizeLanguageCode(Config.Main.Advanced.Language); + Dictionary> fallbackTranslations = + new(StringComparer.OrdinalIgnoreCase); + Dictionary> selectedTranslations = + new(StringComparer.OrdinalIgnoreCase); + + foreach (ZipArchiveEntry entry in archive.Entries) + { + if (!TryGetForgeModLanguage(entry.FullName, out string? modId, out string? language)) + continue; + + if (!requestedModIds.Contains(modId)) + continue; + + if (language.Equals("en_us", StringComparison.OrdinalIgnoreCase)) + { + if (!fallbackTranslations.TryGetValue(modId, out Dictionary? translations)) + { + translations = new Dictionary(StringComparer.Ordinal); + fallbackTranslations[modId] = translations; + } + + MergeTranslationsFromZipEntry(entry, translations); + } + else if (language.Equals(selectedLanguage, StringComparison.OrdinalIgnoreCase)) + { + if (!selectedTranslations.TryGetValue(modId, out Dictionary? translations)) + { + translations = new Dictionary(StringComparer.Ordinal); + selectedTranslations[modId] = translations; + } + + MergeTranslationsFromZipEntry(entry, translations); + } + } + + Dictionary> mergedTranslations = + new(StringComparer.OrdinalIgnoreCase); + + foreach (string modId in requestedModIds) + { + Dictionary modTranslations = new(StringComparer.Ordinal); + + if (fallbackTranslations.TryGetValue(modId, out Dictionary? fallback)) + { + foreach (var (key, value) in fallback) + modTranslations[key] = value; + } + + if (selectedTranslations.TryGetValue(modId, out Dictionary? selected)) + { + foreach (var (key, value) in selected) + modTranslations[key] = value; + } + + if (modTranslations.Count > 0) + mergedTranslations[modId] = modTranslations; + } + + return mergedTranslations; + } + + private static bool TryGetForgeModLanguage(string entryPath, [NotNullWhen(true)] out string? modId, + [NotNullWhen(true)] out string? language) + { + modId = null; + language = null; + + string[] pathParts = entryPath + .Replace('\\', '/') + .Split('/', StringSplitOptions.RemoveEmptyEntries); + + if (pathParts.Length != 4 + || !pathParts[0].Equals("assets", StringComparison.OrdinalIgnoreCase) + || !pathParts[2].Equals("lang", StringComparison.OrdinalIgnoreCase) + || !pathParts[3].EndsWith(".json", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + modId = NormalizeForgeModId(pathParts[1]); + language = NormalizeLanguageCode(Path.GetFileNameWithoutExtension(pathParts[3])); + return !string.IsNullOrEmpty(modId) && !string.IsNullOrEmpty(language); + } + + private static string NormalizeForgeModId(string modId) + { + return modId.Trim().ToLowerInvariant(); + } + + private static string NormalizeLanguageCode(string language) + { + return language.Trim().ToLowerInvariant().Replace('-', '_'); + } + + private static IEnumerable GetForgeModTranslationDirectories() + { + string? configuredPath = Config.Main.Advanced.ForgeModTranslationPath?.Trim(); + if (!string.IsNullOrWhiteSpace(configuredPath)) + { + string overridePath = Path.GetFullPath(configuredPath); + if (Directory.Exists(overridePath)) + yield return overridePath; + + yield break; + } + + HashSet yieldedPaths = new(PathComparer); + + foreach (string candidate in GetDefaultForgeModTranslationDirectories()) + { + string fullPath = Path.GetFullPath(candidate); + if (Directory.Exists(fullPath) && yieldedPaths.Add(fullPath)) + yield return fullPath; + } + + if (!Config.Main.Advanced.AutoDiscoverForgeModTranslationSources) + yield break; + + foreach (string candidate in DiscoverLauncherForgeModTranslationDirectories()) + { + string fullPath = Path.GetFullPath(candidate); + if (Directory.Exists(fullPath) && yieldedPaths.Add(fullPath)) + yield return fullPath; + } + } + + private static IEnumerable GetDefaultForgeModTranslationDirectories() + { + yield return LocalForgeModTranslationDirectory; + } + + private static IEnumerable DiscoverLauncherForgeModTranslationDirectories() + { + if (TryGetOfficialMinecraftModsDirectory(out string? officialModsDirectory)) + yield return officialModsDirectory; + + foreach (string prismModsDirectory in EnumerateInstanceModsDirectories(GetPrismLauncherInstancesDirectory())) + yield return prismModsDirectory; + + foreach (string curseForgeModsDirectory in EnumerateInstanceModsDirectories(GetCurseForgeInstancesDirectory(), "mods")) + yield return curseForgeModsDirectory; + } + + private static IEnumerable EnumerateInstanceModsDirectories(string? instancesDirectory, params string[] relativeModsPaths) + { + if (string.IsNullOrWhiteSpace(instancesDirectory) || !Directory.Exists(instancesDirectory)) + yield break; + + string[] instanceDirectories; + try + { + instanceDirectories = Directory.GetDirectories(instancesDirectory); + } + catch (IOException) + { + yield break; + } + catch (UnauthorizedAccessException) + { + yield break; + } + + foreach (string instanceDirectory in instanceDirectories) + { + foreach (string relativeModsPath in relativeModsPaths.Length > 0 + ? relativeModsPaths + : [Path.Combine(".minecraft", "mods"), Path.Combine("minecraft", "mods"), "mods"]) + { + string modsDirectory = Path.Combine(instanceDirectory, relativeModsPath); + if (Directory.Exists(modsDirectory)) + yield return modsDirectory; + } + } + } + + private static bool TryGetOfficialMinecraftModsDirectory([NotNullWhen(true)] out string? modsDirectory) + { + modsDirectory = null; + string? userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (string.IsNullOrWhiteSpace(userProfile)) + return false; + + string baseMinecraftDirectory = OperatingSystem.IsWindows() + ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ".minecraft") + : Path.Combine(userProfile, ".minecraft"); + + modsDirectory = Path.Combine(baseMinecraftDirectory, "mods"); + return true; + } + + private static string? GetPrismLauncherInstancesDirectory() + { + string? userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (string.IsNullOrWhiteSpace(userProfile)) + return null; + + if (OperatingSystem.IsWindows()) + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PrismLauncher", "instances"); + + if (OperatingSystem.IsMacOS()) + return Path.Combine(userProfile, "Library", "Application Support", "PrismLauncher", "instances"); + + return Path.Combine(userProfile, ".local", "share", "PrismLauncher", "instances"); + } + + private static string? GetCurseForgeInstancesDirectory() + { + string? userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (string.IsNullOrWhiteSpace(userProfile)) + return null; + + return Path.Combine(userProfile, "curseforge", "minecraft", "Instances"); + } + + private static readonly StringComparer PathComparer = + OperatingSystem.IsWindows() ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; + + private static string ComputeFileSha256(string filePath) + { + using FileStream stream = File.OpenRead(filePath); + using SHA256 sha256 = SHA256.Create(); + return Convert.ToHexString(sha256.ComputeHash(stream)).ToLowerInvariant(); + } + + private static bool TryLoadCachedForgeModTranslations(string cacheFilePath, string sourceHash, + [NotNullWhen(true)] out Dictionary>? translationsByModId) + { + translationsByModId = null; + + if (!File.Exists(cacheFilePath)) + return false; + + try + { + using FileStream cacheFile = File.OpenRead(cacheFilePath); + ForgeModTranslationCacheEntry? cacheEntry = + JsonSerializer.Deserialize(cacheFile); + + if (cacheEntry is not null + && cacheEntry.CacheVersion == ForgeModTranslationCacheVersion + && cacheEntry.Language.Equals(Config.Main.Advanced.Language, StringComparison.OrdinalIgnoreCase) + && cacheEntry.SourceHash.Equals(sourceHash, StringComparison.OrdinalIgnoreCase) + && cacheEntry.TranslationsByModId.Count > 0) + { + translationsByModId = cacheEntry.TranslationsByModId.ToDictionary( + static entry => entry.Key, + static entry => new Dictionary(entry.Value, StringComparer.Ordinal), + StringComparer.OrdinalIgnoreCase); + return true; + } + } + catch (IOException) + { + } + catch (JsonException) + { + } + + try + { + File.Delete(cacheFilePath); + } + catch (IOException) + { + } + + return false; + } + + private static void SaveCachedForgeModTranslations(string cacheFilePath, string sourceHash, + Dictionary> translationsByModId) + { + if (translationsByModId.Count == 0) + return; + + string? cacheDirectory = Path.GetDirectoryName(cacheFilePath); + if (string.IsNullOrEmpty(cacheDirectory)) + return; + + Directory.CreateDirectory(cacheDirectory); + + ForgeModTranslationCacheEntry cacheEntry = new() + { + CacheVersion = ForgeModTranslationCacheVersion, + Language = Config.Main.Advanced.Language, + SourceHash = sourceHash, + TranslationsByModId = translationsByModId.ToDictionary( + static entry => entry.Key, + static entry => new Dictionary(entry.Value, StringComparer.Ordinal), + StringComparer.OrdinalIgnoreCase) + }; + + File.WriteAllText(cacheFilePath, JsonSerializer.Serialize(cacheEntry), Encoding.UTF8); + } + + private static string GetForgeModTranslationCacheFilePath(string sourceHash) + { + return Path.Combine("lang", "forgemods", $"{sourceHash}.{NormalizeLanguageCode(Config.Main.Advanced.Language)}.json"); + } + + private static bool TryLoadCachedResourcePackTranslations(string cacheFilePath, Uri resourcePackUri, string hash, + [NotNullWhen(true)] out Dictionary? translations) + { + translations = null; + + if (!File.Exists(cacheFilePath)) + return false; + + try + { + using FileStream cacheFile = File.OpenRead(cacheFilePath); + ResourcePackTranslationCacheEntry? cacheEntry = + JsonSerializer.Deserialize(cacheFile); + + if (cacheEntry is not null + && cacheEntry.CacheVersion == ResourcePackTranslationCacheVersion + && cacheEntry.Language.Equals(Config.Main.Advanced.Language, StringComparison.OrdinalIgnoreCase) + && cacheEntry.SourceUrl.Equals(resourcePackUri.AbsoluteUri, StringComparison.Ordinal) + && cacheEntry.SourceHash.Equals(hash, StringComparison.OrdinalIgnoreCase) + && cacheEntry.Translations.Count > 0) + { + translations = new Dictionary(cacheEntry.Translations, StringComparer.Ordinal); + return true; + } + } + catch (IOException) + { + } + catch (JsonException) + { + } + + try + { + File.Delete(cacheFilePath); + } + catch (IOException) + { + } + + return false; + } + + private static void SaveCachedResourcePackTranslations(string cacheFilePath, Uri resourcePackUri, string hash, + Dictionary translations) + { + if (translations.Count == 0) + return; + + string? cacheDirectory = Path.GetDirectoryName(cacheFilePath); + if (string.IsNullOrEmpty(cacheDirectory)) + return; + + Directory.CreateDirectory(cacheDirectory); + + ResourcePackTranslationCacheEntry cacheEntry = new() + { + CacheVersion = ResourcePackTranslationCacheVersion, + Language = Config.Main.Advanced.Language, + SourceUrl = resourcePackUri.AbsoluteUri, + SourceHash = hash, + Translations = new Dictionary(translations, StringComparer.Ordinal) + }; + + File.WriteAllText(cacheFilePath, JsonSerializer.Serialize(cacheEntry), Encoding.UTF8); + } + + private static string GetResourcePackTranslationCacheFilePath(Uri resourcePackUri, string hash) + { + string cacheKey = GetResourcePackTranslationCacheKey(resourcePackUri, hash); + return Path.Combine("lang", "resourcepacks", $"{cacheKey}.{Config.Main.Advanced.Language}.json"); + } + + private static string GetResourcePackTranslationCacheKey(Uri resourcePackUri, string hash) + { + if (IsValidSha1(hash)) + return hash.ToLowerInvariant(); + + byte[] urlHash = SHA256.HashData(Encoding.UTF8.GetBytes(resourcePackUri.AbsoluteUri)); + return "url-" + Convert.ToHexString(urlHash).ToLowerInvariant(); + } + + private static bool IsValidSha1(string hash) + { + return hash.Length == 40 && hash.All(Uri.IsHexDigit); + } + + /// + /// Mapping from JSON/NBT property names to Minecraft formatting codes (without §). + /// Both "underlined" (canonical Minecraft name) and "underline" (alias) are supported. + /// + private static readonly Dictionary FormattingCodes = new() + { + { "obfuscated", "k" }, + { "bold", "l" }, + { "strikethrough", "m" }, + { "underlined", "n" }, + { "underline", "n" }, + { "italic", "o" }, + }; + + /// Matches a single color code (§0-§9, §a-§f) or a hex color (§#rrggbb). Used to strip color when replacing. + private static readonly Regex ColorCodeRegex = new(@"§(?:[0-9a-f]|#[0-9a-f]{6})", RegexOptions.Compiled); + /// /// Use a JSON Object to build the corresponding string /// /// JSON object to convert - /// Allow parent color code to affect child elements (set to "" for function init) + /// Inherited formatting codes from parent elements (set to "" for function init) /// Container for links from JSON serialized text /// returns the Minecraft-formatted string - private static string JSONData2String(Json.JSONData data, string colorcode, List? links) + private static string JSONData2String(System.Text.Json.Nodes.JsonNode? data, string formatting, List? links) { string extra_result = ""; - switch (data.Type) + switch (data) { - case Json.JSONData.DataType.Object: - if (data.Properties.ContainsKey("color")) + case System.Text.Json.Nodes.JsonObject obj: + if (obj.ContainsKey("color")) { - colorcode = Color2tag(JSONData2String(data.Properties["color"], "", links)); + formatting = ColorCodeRegex.Replace(formatting, ""); + formatting += Color2tag(JSONData2String(obj["color"], "", links)); } - if (data.Properties.ContainsKey("clickEvent") && links != null) + foreach (var (key, code) in FormattingCodes) { - Json.JSONData clickEvent = data.Properties["clickEvent"]; - if (clickEvent.Properties.ContainsKey("action") - && clickEvent.Properties.ContainsKey("value") - && clickEvent.Properties["action"].StringValue == "open_url" - && !string.IsNullOrEmpty(clickEvent.Properties["value"].StringValue)) + if (obj.ContainsKey(key)) { - links.Add(clickEvent.Properties["value"].StringValue); + string val = obj[key]!.GetStringValue(); + if (val == "true") + formatting += "§" + code; + else if (val == "false") + formatting = formatting.Replace("§" + code, ""); } } - if (data.Properties.ContainsKey("extra")) + if (obj.ContainsKey("clickEvent") && links is not null) { - Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray(); - foreach (Json.JSONData item in extras) - extra_result = extra_result + JSONData2String(item, colorcode, links) + "§r"; + var clickEvent = obj["clickEvent"]!.AsObject(); + if (clickEvent.ContainsKey("action") + && clickEvent.ContainsKey("value") + && clickEvent["action"]!.GetStringValue() == "open_url" + && !string.IsNullOrEmpty(clickEvent["value"]!.GetStringValue())) + { + links.Add(clickEvent["value"]!.GetStringValue()); + } } - if (data.Properties.ContainsKey("text")) + if (obj.ContainsKey("extra")) { - return colorcode + JSONData2String(data.Properties["text"], colorcode, links) + extra_result; + foreach (var item in obj["extra"]!.AsArray()) + extra_result += JSONData2String(item, "§r" + formatting, links); } - else if (data.Properties.ContainsKey("translate")) + + // Strip any formatting codes that appear before the last §r, since §r resets all + // prior formatting. The greedy .* matches up to the last §r in the string. + formatting = Regex.Replace(formatting, ".*(§r.*)", "$1"); + + if (obj.ContainsKey("text")) + { + // Pass "" to the leaf text node: formatting is already prepended here, + // and the default: case would add it a second time if we passed formatting. + return formatting + JSONData2String(obj["text"], "", links) + extra_result; + } + else if (obj.ContainsKey("translate")) { List using_data = new(); - if (data.Properties.ContainsKey("using") && !data.Properties.ContainsKey("with")) - data.Properties["with"] = data.Properties["using"]; - if (data.Properties.ContainsKey("with")) + if (obj.ContainsKey("using") && !obj.ContainsKey("with")) + obj["with"] = obj["using"]!.DeepClone(); + if (obj.ContainsKey("with")) { - Json.JSONData[] array = data.Properties["with"].DataArray.ToArray(); - for (int i = 0; i < array.Length; i++) + foreach (var item in obj["with"]!.AsArray()) { - using_data.Add(JSONData2String(array[i], colorcode, links)); + using_data.Add(JSONData2String(item, formatting, links)); } } - return colorcode + - TranslateString(JSONData2String(data.Properties["translate"], "", links), using_data) + + return formatting + + TranslateString(JSONData2String(obj["translate"], "", links), using_data) + extra_result; } else return extra_result; - case Json.JSONData.DataType.Array: + case System.Text.Json.Nodes.JsonArray arr: string result = ""; - foreach (Json.JSONData item in data.DataArray) + foreach (var item in arr) { - result += JSONData2String(item, colorcode, links); + result += JSONData2String(item, formatting, links); } return result; - case Json.JSONData.DataType.String: - return colorcode + data.StringValue; + default: + return formatting + data.GetStringValue(); } - - return ""; } - private static string NbtToString(Dictionary nbt) + private static string NbtToString(Dictionary nbt, string formatting = "") { if (nbt.Count == 1 && nbt.TryGetValue("", out object? rootMessage)) { - // Nameless root tag - return (string)rootMessage; + return formatting + (rootMessage?.ToString() ?? string.Empty); } string message = string.Empty; - string colorCode = string.Empty; - StringBuilder extraBuilder = new StringBuilder(); - foreach (var kvp in nbt) + StringBuilder extraBuilder = new(); + + // Build formatting from color and formatting flags first + if (nbt.TryGetValue("color", out object? color)) { - string key = kvp.Key; - object value = kvp.Value; + formatting = ColorCodeRegex.Replace(formatting, ""); + formatting += Color2tag((string)color); + } - switch (key) + foreach (var (key, code) in FormattingCodes) + { + if (nbt.TryGetValue(key, out object? flagValue)) { - case "text": + bool isActive = flagValue switch { - message = (string)value; - } - break; - case "extra": - { - object[] extras = (object[])value; - for (var i = 0; i < extras.Length; i++) - { - var extraDict = extras[i] switch - { - int => new Dictionary { { "text", $"{extras[i]}" } }, - string => new Dictionary - { - { "text", (string)extras[i] } - }, - _ => (Dictionary)extras[i] - }; - - extraBuilder.Append(NbtToString(extraDict) + "§r"); - } - } - break; - case "translate": - { - if (nbt.TryGetValue("translate", out object translate)) - { - var translateKey = (string)translate; - List translateString = new(); - if (nbt.TryGetValue("with", out object withComponent)) - { - var withs = (object[])withComponent; - for (var i = 0; i < withs.Length; i++) - { - var withDict = withs[i] switch - { - int => new Dictionary { { "text", $"{withs[i]}" } }, - string => new Dictionary - { - { "text", (string)withs[i] } - }, - _ => (Dictionary)withs[i] - }; - - translateString.Add(NbtToString(withDict)); - } - } - - message = TranslateString(translateKey, translateString); - } - } - break; - case "color": - { - if (nbt.TryGetValue("color", out object color)) - { - colorCode = Color2tag((string)color); - } - } - break; + byte b => b > 0, + bool b => b, + _ => flagValue?.ToString()?.ToLower() == "true" + }; + if (isActive) + formatting += "§" + code; + else + formatting = formatting.Replace("§" + code, ""); } } - return colorCode + message + extraBuilder.ToString(); + // Process text + if (nbt.TryGetValue("text", out object? textValue)) + message = textValue?.ToString() ?? string.Empty; + + // Process translate + if (nbt.TryGetValue("translate", out object? translate)) + { + var translateKey = (string)translate; + List translateString = new(); + if (nbt.TryGetValue("with", out object? withComponent)) + { + var withs = (object[])withComponent; + for (var i = 0; i < withs.Length; i++) + { + var withDict = withs[i] switch + { + int => new Dictionary { { "text", $"{withs[i]}" } }, + string => new Dictionary { { "text", (string)withs[i] } }, + _ => (Dictionary)withs[i] + }; + translateString.Add(NbtToString(withDict, formatting)); + } + } + message = TranslateString(translateKey, translateString); + } + + // Process extras, each starting with a reset then inheriting the current formatting + if (nbt.TryGetValue("extra", out object? extraValue)) + { + object[] extras = (object[])extraValue; + for (var i = 0; i < extras.Length; i++) + { + var extraDict = extras[i] switch + { + int => new Dictionary { { "text", $"{extras[i]}" } }, + string => new Dictionary { { "text", (string)extras[i] } }, + _ => (Dictionary)extras[i] + }; + extraBuilder.Append(NbtToString(extraDict, "§r" + formatting)); + } + } + + return formatting + message + extraBuilder.ToString(); } } } diff --git a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs index 62b1227e..f6603845 100644 --- a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs +++ b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs @@ -8,17 +8,12 @@ namespace MinecraftClient.Protocol.Message /// /// A list of messages a client has seen. /// - public class LastSeenMessageList + public class LastSeenMessageList(AcknowledgedMessage[] list) { public static readonly LastSeenMessageList EMPTY = new(Array.Empty()); public static readonly int MAX_ENTRIES = 5; - public AcknowledgedMessage[] entries; - - public LastSeenMessageList(AcknowledgedMessage[] list) - { - entries = list; - } + public AcknowledgedMessage[] entries = list; public void WriteForSign(List data) { @@ -56,16 +51,10 @@ namespace MinecraftClient.Protocol.Message /// A record of messages acknowledged by a client. /// This holds the messages the client has recently seen, as well as the last message they received, if any. /// - public class Acknowledgment + public class Acknowledgment(LastSeenMessageList lastSeenMessageList, AcknowledgedMessage? lastReceivedMessage) { - public LastSeenMessageList lastSeen; - public AcknowledgedMessage? lastReceived; - - public Acknowledgment(LastSeenMessageList lastSeenMessageList, AcknowledgedMessage? lastReceivedMessage) - { - lastSeen = lastSeenMessageList; - lastReceived = lastReceivedMessage; - } + public LastSeenMessageList lastSeen = lastSeenMessageList; + public AcknowledgedMessage? lastReceived = lastReceivedMessage; } } @@ -107,7 +96,7 @@ namespace MinecraftClient.Protocol.Message } } - if (lastEntry != null && messageCount < acknowledgedMessages.Length) + if (lastEntry is not null && messageCount < acknowledgedMessages.Length) acknowledgedMessages[messageCount++] = lastEntry; LastSeenMessageList.AcknowledgedMessage[] msgList = new LastSeenMessageList.AcknowledgedMessage[messageCount]; @@ -120,7 +109,7 @@ namespace MinecraftClient.Protocol.Message { // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.MessageSignatureData, boolean) // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.AcknowledgedMessage) - if (lastEntry != null && entry.signature.SequenceEqual(lastEntry.signature)) + if (lastEntry is not null && entry.signature.SequenceEqual(lastEntry.signature)) return false; lastEntry = entry; @@ -143,7 +132,7 @@ namespace MinecraftClient.Protocol.Message { int k = (nextIndex + j) % acknowledgedMessages.Length; AcknowledgedMessage? acknowledgedMessage = acknowledgedMessages[k]; - if (acknowledgedMessage == null) + if (acknowledgedMessage is null) continue; bitset[j / 8] |= (byte)(1 << (j % 8)); // bitSet.set(j, true); objectList.Add(acknowledgedMessage); diff --git a/MinecraftClient/Protocol/MicrosoftAuthentication.cs b/MinecraftClient/Protocol/MicrosoftAuthentication.cs index f3f81298..4c84ce47 100644 --- a/MinecraftClient/Protocol/MicrosoftAuthentication.cs +++ b/MinecraftClient/Protocol/MicrosoftAuthentication.cs @@ -1,13 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using static MinecraftClient.Settings; -using static MinecraftClient.Settings.MainConfigHelper.MainConfig.GeneralConfig; +using System.Threading; namespace MinecraftClient.Protocol { @@ -16,6 +14,7 @@ namespace MinecraftClient.Protocol private static readonly string clientId = "54473e32-df8f-42e9-a649-9419b0dab9d3"; private static readonly string signinUrl = string.Format("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id={0}&response_type=code&redirect_uri=https%3A%2F%2Fmccteam.github.io%2Fredirect.html&scope=XboxLive.signin%20offline_access%20openid%20email&prompt=select_account&response_mode=fragment", clientId); private static readonly string tokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; + private static readonly string deviceCodeUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"; public static string SignInUrl { get { return signinUrl; } } @@ -53,6 +52,121 @@ namespace MinecraftClient.Protocol return RequestToken(postData); } + /// + /// Initiate the OAuth 2.0 device code flow. + /// Returns a device code response containing the user code and verification URI. + /// + /// Device code response for user to complete authentication + public static DeviceCodeResponse RequestDeviceCode() + { + string postData = string.Format("client_id={0}&scope=XboxLive.signin%20offline_access%20openid%20email", clientId); + + var request = new ProxiedWebRequest(deviceCodeUrl) + { + UserAgent = "MCC/" + Program.Version + }; + var response = request.Post("application/x-www-form-urlencoded", postData); + var jsonData = Json.ParseJson(response.Body); + + if (jsonData?["error"] is not null) + { + throw new Exception(jsonData["error_description"]!.GetStringValue()); + } + + return new DeviceCodeResponse() + { + DeviceCode = jsonData!["device_code"]!.GetStringValue(), + UserCode = jsonData["user_code"]!.GetStringValue(), + VerificationUri = jsonData["verification_uri"]!.GetStringValue(), + ExpiresIn = int.Parse(jsonData["expires_in"]!.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture), + Interval = int.Parse(jsonData["interval"]!.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture), + Message = jsonData["message"]!.GetStringValue() + }; + } + + /// + /// Poll the token endpoint until the user completes device code authentication. + /// Handles authorization_pending, slow_down, and expiration. + /// + /// Device code from + /// Expiration time in seconds + /// Polling interval in seconds + /// Login response with access token and refresh token + public static LoginResponse PollDeviceCodeToken(string deviceCode, int expiresIn, int interval) + { + // Per OAuth 2.0 device code spec, server may respond with "slow_down" requiring + // the client to increase its polling interval by this amount + const int SlowDownIncrementSeconds = 5; + + string postData = string.Format( + "client_id={0}&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code={1}", + clientId, deviceCode); + + var stopwatch = Stopwatch.StartNew(); + int pollInterval = interval; + + while (stopwatch.Elapsed.TotalSeconds < expiresIn) + { + Thread.Sleep(pollInterval * 1000); + + var request = new ProxiedWebRequest(tokenUrl) + { + UserAgent = "MCC/" + Program.Version + }; + var response = request.Post("application/x-www-form-urlencoded", postData); + var jsonData = Json.ParseJson(response.Body); + + if (jsonData?["error"] is not null) + { + string error = jsonData["error"]!.GetStringValue(); + + if (error == "authorization_pending") + { + // User hasn't completed auth yet, keep polling + continue; + } + else if (error == "slow_down") + { + // Server asked us to slow down + pollInterval += SlowDownIncrementSeconds; + continue; + } + else if (error == "expired_token") + { + throw new Exception("Device code expired. Please try again."); + } + else if (error == "authorization_declined") + { + throw new Exception("Authorization was declined by the user."); + } + else + { + throw new Exception(jsonData["error_description"]!.GetStringValue()); + } + } + + // Success - parse the token response + string accessToken = jsonData!["access_token"]!.GetStringValue(); + string refreshToken = jsonData["refresh_token"]!.GetStringValue(); + int tokenExpiresIn = int.Parse(jsonData["expires_in"]!.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); + + // Extract email from JWT id_token + string payload = JwtPayloadDecode.GetPayload(jsonData["id_token"]!.GetStringValue()); + var jsonPayload = Json.ParseJson(payload); + string email = jsonPayload!["email"]!.GetStringValue(); + + return new LoginResponse() + { + Email = email, + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresIn = tokenExpiresIn + }; + } + + throw new Exception("Device code authentication timed out."); + } + /// /// Perform request to obtain access token by code or by refresh token /// @@ -68,20 +182,20 @@ namespace MinecraftClient.Protocol var jsonData = Json.ParseJson(response.Body); // Error handling - if (jsonData.Properties.ContainsKey("error")) + if (jsonData?["error"] is not null) { - throw new Exception(jsonData.Properties["error_description"].StringValue); + throw new Exception(jsonData["error_description"]!.GetStringValue()); } else { - string accessToken = jsonData.Properties["access_token"].StringValue; - string refreshToken = jsonData.Properties["refresh_token"].StringValue; - int expiresIn = int.Parse(jsonData.Properties["expires_in"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture); + string accessToken = jsonData!["access_token"]!.GetStringValue(); + string refreshToken = jsonData["refresh_token"]!.GetStringValue(); + int expiresIn = int.Parse(jsonData["expires_in"]!.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); // Extract email from JWT - string payload = JwtPayloadDecode.GetPayload(jsonData.Properties["id_token"].StringValue); + string payload = JwtPayloadDecode.GetPayload(jsonData["id_token"]!.GetStringValue()); var jsonPayload = Json.ParseJson(payload); - string email = jsonPayload.Properties["email"].StringValue; + string email = jsonPayload!["email"]!.GetStringValue(); return new LoginResponse() { Email = email, @@ -132,132 +246,25 @@ namespace MinecraftClient.Protocol public string RefreshToken; public int ExpiresIn; } + + public struct DeviceCodeResponse + { + public string DeviceCode; + public string UserCode; + public string VerificationUri; + public int ExpiresIn; + public int Interval; + public string Message; + } } static class XboxLive { - private static readonly string authorize = "https://login.live.com/oauth20_authorize.srf?client_id=000000004C12AE6F&redirect_uri=https://login.live.com/oauth20_desktop.srf&scope=service::user.auth.xboxlive.com::MBI_SSL&display=touch&response_type=token&locale=en"; private static readonly string xbl = "https://user.auth.xboxlive.com/user/authenticate"; private static readonly string xsts = "https://xsts.auth.xboxlive.com/xsts/authorize"; private static readonly string userAgent = "Mozilla/5.0 (XboxReplay; XboxLiveAuth/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"; - private static readonly Regex ppft = new("sFTTag:'.*value=\"(.*)\"\\/>'"); - private static readonly Regex urlPost = new("urlPost:'(.+?(?=\'))"); - private static readonly Regex confirm = new("identity\\/confirm"); - private static readonly Regex invalidAccount = new("Sign in to", RegexOptions.IgnoreCase); - private static readonly Regex twoFA = new("Help us protect your account", RegexOptions.IgnoreCase); - - public static string SignInUrl { get { return authorize; } } - - /// - /// Pre-authentication - /// - /// This step is to get the login page for later use - /// - public static PreAuthResponse PreAuth() - { - var request = new ProxiedWebRequest(authorize) - { - UserAgent = userAgent - }; - var response = request.Get(); - - string html = response.Body; - - string PPFT = ppft.Match(html).Groups[1].Value; - string urlPost = XboxLive.urlPost.Match(html).Groups[1].Value; - - if (string.IsNullOrEmpty(PPFT) || string.IsNullOrEmpty(urlPost)) - { - throw new Exception("Fail to extract PPFT or urlPost"); - } - //Console.WriteLine("PPFT: {0}", PPFT); - //Console.WriteLine(); - //Console.WriteLine("urlPost: {0}", urlPost); - - return new PreAuthResponse() - { - UrlPost = urlPost, - PPFT = PPFT, - Cookie = response.Cookies - }; - } - - /// - /// Perform login request - /// - /// This step is to send the login request by using the PreAuth response - /// Microsoft account email - /// Account password - /// - /// - public static Microsoft.LoginResponse UserLogin(string email, string password, PreAuthResponse preAuth) - { - var request = new ProxiedWebRequest(preAuth.UrlPost, preAuth.Cookie) - { - UserAgent = userAgent - }; - - string postData = "login=" + Uri.EscapeDataString(email) - + "&loginfmt=" + Uri.EscapeDataString(email) - + "&passwd=" + Uri.EscapeDataString(password) - + "&PPFT=" + Uri.EscapeDataString(preAuth.PPFT); - - var response = request.Post("application/x-www-form-urlencoded", postData); - - if (Settings.Config.Logging.DebugMessages) - { - ConsoleIO.WriteLine(response.ToString()); - } - - if (response.StatusCode >= 300 && response.StatusCode <= 399) - { - string url = response.Headers.Get("Location")!; - string hash = url.Split('#')[1]; - - var request2 = new ProxiedWebRequest(url); - var response2 = request2.Get(); - - if (response2.StatusCode != 200) - { - throw new Exception("Authentication failed"); - } - - if (string.IsNullOrEmpty(hash)) - { - throw new Exception("Cannot extract access token"); - } - var dict = Request.ParseQueryString(hash); - - //foreach (var pair in dict) - //{ - // Console.WriteLine("{0}: {1}", pair.Key, pair.Value); - //} - - return new Microsoft.LoginResponse() - { - Email = email, - AccessToken = dict["access_token"], - RefreshToken = dict["refresh_token"], - ExpiresIn = int.Parse(dict["expires_in"], NumberStyles.Any, CultureInfo.CurrentCulture) - }; - } - else - { - if (twoFA.IsMatch(response.Body)) - { - // TODO: Handle 2FA - throw new Exception("2FA enabled but not supported yet. Use browser sign-in method or try to disable 2FA in Microsoft account settings"); - } - else if (invalidAccount.IsMatch(response.Body)) - { - throw new Exception("Invalid credentials. Check your credentials"); - } - else throw new Exception("Unexpected response. Check your credentials. Response code: " + response.StatusCode); - } - } - /// /// Xbox Live Authenticate /// @@ -272,13 +279,8 @@ namespace MinecraftClient.Protocol }; request.Headers.Add("x-xbl-contract-version", "0"); - var accessToken = loginResponse.AccessToken; - if (Config.Main.General.Method == LoginMethod.browser) - { - // Our own client ID must have d= in front of the token or HTTP status 400 - // "Stolen" client ID must not have d= in front of the token or HTTP status 400 - accessToken = "d=" + accessToken; - } + // OAuth tokens from our own client ID require "d=" prefix for XBL authentication + var accessToken = "d=" + loginResponse.AccessToken; string payload = "{" + "\"Properties\": {" @@ -297,11 +299,9 @@ namespace MinecraftClient.Protocol if (response.StatusCode == 200) { string jsonString = response.Body; - //Console.WriteLine(jsonString); - - Json.JSONData json = Json.ParseJson(jsonString); - string token = json.Properties["Token"].StringValue; - string userHash = json.Properties["DisplayClaims"].Properties["xui"].DataArray[0].Properties["uhs"].StringValue; + var json = Json.ParseJson(jsonString); + string token = json!["Token"]!.GetStringValue(); + string userHash = json["DisplayClaims"]!["xui"]![0]!["uhs"]!.GetStringValue(); return new XblAuthenticateResponse() { Token = token, @@ -317,7 +317,7 @@ namespace MinecraftClient.Protocol /// /// XSTS Authenticate /// - /// (Don't ask me what is XSTS, I DONT KNOW) + /// Xbox Secure Token Service - exchanges XBL token for a service-specific XSTS token /// /// public static XSTSAuthenticateResponse XSTSAuthenticate(XblAuthenticateResponse xblResponse) @@ -347,9 +347,9 @@ namespace MinecraftClient.Protocol if (response.StatusCode == 200) { string jsonString = response.Body; - Json.JSONData json = Json.ParseJson(jsonString); - string token = json.Properties["Token"].StringValue; - string userHash = json.Properties["DisplayClaims"].Properties["xui"].DataArray[0].Properties["uhs"].StringValue; + var json = Json.ParseJson(jsonString); + string token = json!["Token"]!.GetStringValue(); + string userHash = json["DisplayClaims"]!["xui"]![0]!["uhs"]!.GetStringValue(); return new XSTSAuthenticateResponse() { Token = token, @@ -360,16 +360,16 @@ namespace MinecraftClient.Protocol { if (response.StatusCode == 401) { - Json.JSONData json = Json.ParseJson(response.Body); - if (json.Properties["XErr"].StringValue == "2148916233") + var json = Json.ParseJson(response.Body); + if (json!["XErr"]!.GetStringValue() == "2148916233") { throw new Exception("The account doesn't have an Xbox account"); } - else if (json.Properties["XErr"].StringValue == "2148916238") + else if (json["XErr"]!.GetStringValue() == "2148916238") { throw new Exception("The account is a child (under 18) and cannot proceed unless the account is added to a Family by an adult"); } - else throw new Exception("Unknown XSTS error code: " + json.Properties["XErr"].StringValue); + else throw new Exception("Unknown XSTS error code: " + json["XErr"]!.GetStringValue()); } else { @@ -378,13 +378,6 @@ namespace MinecraftClient.Protocol } } - public struct PreAuthResponse - { - public string UrlPost; - public string PPFT; - public NameValueCollection Cookie; - } - public struct XblAuthenticateResponse { public string Token; @@ -426,9 +419,9 @@ namespace MinecraftClient.Protocol } string jsonString = response.Body; - Json.JSONData json = Json.ParseJson(jsonString); + var json = Json.ParseJson(jsonString); - return json.Properties["access_token"].StringValue; + return json!["access_token"]!.GetStringValue(); } /// @@ -448,8 +441,8 @@ namespace MinecraftClient.Protocol } string jsonString = response.Body; - Json.JSONData json = Json.ParseJson(jsonString); - return json.Properties["items"].DataArray.Count > 0; + var json = Json.ParseJson(jsonString); + return json!["items"]!.AsArray().Count > 0; } public static UserProfile GetUserProfile(string accessToken) @@ -464,11 +457,11 @@ namespace MinecraftClient.Protocol } string jsonString = response.Body; - Json.JSONData json = Json.ParseJson(jsonString); + var json = Json.ParseJson(jsonString); return new UserProfile() { - UUID = json.Properties["id"].StringValue, - UserName = json.Properties["name"].StringValue + UUID = json!["id"]!.GetStringValue(), + UserName = json["name"]!.GetStringValue() }; } diff --git a/MinecraftClient/Protocol/MojangAPI.cs b/MinecraftClient/Protocol/MojangAPI.cs index 8ee28b31..b05e6a71 100644 --- a/MinecraftClient/Protocol/MojangAPI.cs +++ b/MinecraftClient/Protocol/MojangAPI.cs @@ -19,19 +19,7 @@ namespace MinecraftClient.Protocol /// Information about a players Skin. /// Empty string if not available. /// - public class SkinInfo - { - public readonly string SkinUrl; - public readonly string CapeUrl; - public readonly string SkinModel; - - public SkinInfo(string skinUrl = "", string capeUrl = "", string skinModel = "") - { - SkinUrl = skinUrl; - CapeUrl = capeUrl; - SkinModel = skinModel; - } - } + public record SkinInfo(string SkinUrl = "", string CapeUrl = "", string SkinModel = ""); /// /// Status of the single Mojang services @@ -121,7 +109,7 @@ namespace MinecraftClient.Protocol { Task fetchTask = httpClient.GetStringAsync("https://api.mojang.com/users/profiles/minecraft/" + name); fetchTask.Wait(); - string result = Json.ParseJson(fetchTask.Result).Properties["id"].StringValue; + string result = Json.ParseJson(fetchTask.Result)!["id"]!.GetStringValue(); fetchTask.Dispose(); return result; } @@ -140,11 +128,11 @@ namespace MinecraftClient.Protocol { Task fetchTask = httpClient.GetStringAsync("https://api.mojang.com/user/profiles/" + uuid + "/names"); fetchTask.Wait(); - var nameChanges = Json.ParseJson(fetchTask.Result).DataArray; + var nameChanges = Json.ParseJson(fetchTask.Result)!.AsArray(); fetchTask.Dispose(); // Names are sorted from past to most recent. We need to get the last name in the list - return nameChanges[^1].Properties["name"].StringValue; + return nameChanges[^1]!["name"]!.GetStringValue(); } catch (Exception) { return string.Empty; } } @@ -157,40 +145,32 @@ namespace MinecraftClient.Protocol public static Dictionary UuidToNameHistory(string uuid) { Dictionary tempDict = new(); - List jsonDataList; + System.Text.Json.Nodes.JsonArray jsonDataList; // Perform web request try { Task fetchTask = httpClient.GetStringAsync("https://api.mojang.com/user/profiles/" + uuid + "/names"); fetchTask.Wait(); - jsonDataList = Json.ParseJson(fetchTask.Result).DataArray; + jsonDataList = Json.ParseJson(fetchTask.Result)!.AsArray(); fetchTask.Dispose(); } catch (Exception) { return tempDict; } - foreach (Json.JSONData jsonData in jsonDataList) + foreach (var jsonData in jsonDataList) { - if (jsonData.Properties.Count > 1) + var obj = jsonData!.AsObject(); + if (obj.Count > 1) { - // Time is saved as long in the Unix format. - // Convert it to normal time, before adding it to the dictionary. - // - // !! FromUnixTimeMilliseconds does not exist in the current version. !! - // DateTimeOffset creationDate = DateTimeOffset.FromUnixTimeMilliseconds(Convert.ToInt64(jsonData.Properties["changedToAt"].StringValue)); - // + DateTimeOffset creationDate = UnixTimeStampToDateTime(Convert.ToDouble(jsonData["changedToAt"].GetStringValue())); - // Workaround for converting Unix time to normal time. - DateTimeOffset creationDate = UnixTimeStampToDateTime(Convert.ToDouble(jsonData.Properties["changedToAt"].StringValue)); - - // Add Keyvaluepair to dict. - tempDict.Add(jsonData.Properties["name"].StringValue, creationDate.DateTime); + tempDict.Add(jsonData["name"]!.GetStringValue(), creationDate.DateTime); } // The first entry does not contain a change date. - else if (jsonData.Properties.Count > 0) + else if (obj.Count > 0) { // Add an undefined time to it. - tempDict.Add(jsonData.Properties["name"].StringValue, new DateTime()); + tempDict.Add(jsonData["name"]!.GetStringValue(), new DateTime()); } } @@ -203,14 +183,14 @@ namespace MinecraftClient.Protocol /// Dictionary of the Mojang services public static MojangServiceStatus GetMojangServiceStatus() { - List jsonDataList; + System.Text.Json.Nodes.JsonArray jsonDataList; // Perform web request try { Task fetchTask = httpClient.GetStringAsync("https://status.mojang.com/check"); fetchTask.Wait(); - jsonDataList = Json.ParseJson(fetchTask.Result).DataArray; + jsonDataList = Json.ParseJson(fetchTask.Result)!.AsArray(); fetchTask.Dispose(); } catch (Exception) @@ -219,14 +199,14 @@ namespace MinecraftClient.Protocol } // Convert string to enum values and store them inside a MojangeServiceStatus object. - return new MojangServiceStatus(minecraftNet: StringToServiceStatus(jsonDataList[0].Properties["minecraft.net"].StringValue), - sessionMinecraftNet: StringToServiceStatus(jsonDataList[1].Properties["session.minecraft.net"].StringValue), - accountMojangCom: StringToServiceStatus(jsonDataList[2].Properties["account.mojang.com"].StringValue), - authserverMojangCom: StringToServiceStatus(jsonDataList[3].Properties["authserver.mojang.com"].StringValue), - sessionserverMojangCom: StringToServiceStatus(jsonDataList[4].Properties["sessionserver.mojang.com"].StringValue), - apiMojangCom: StringToServiceStatus(jsonDataList[5].Properties["api.mojang.com"].StringValue), - texturesMinecraftNet: StringToServiceStatus(jsonDataList[6].Properties["textures.minecraft.net"].StringValue), - mojangCom: StringToServiceStatus(jsonDataList[7].Properties["mojang.com"].StringValue) + return new MojangServiceStatus(minecraftNet: StringToServiceStatus(jsonDataList[0]!["minecraft.net"]!.GetStringValue()), + sessionMinecraftNet: StringToServiceStatus(jsonDataList[1]!["session.minecraft.net"]!.GetStringValue()), + accountMojangCom: StringToServiceStatus(jsonDataList[2]!["account.mojang.com"]!.GetStringValue()), + authserverMojangCom: StringToServiceStatus(jsonDataList[3]!["authserver.mojang.com"]!.GetStringValue()), + sessionserverMojangCom: StringToServiceStatus(jsonDataList[4]!["sessionserver.mojang.com"]!.GetStringValue()), + apiMojangCom: StringToServiceStatus(jsonDataList[5]!["api.mojang.com"]!.GetStringValue()), + texturesMinecraftNet: StringToServiceStatus(jsonDataList[6]!["textures.minecraft.net"]!.GetStringValue()), + mojangCom: StringToServiceStatus(jsonDataList[7]!["mojang.com"]!.GetStringValue()) ); } @@ -237,9 +217,9 @@ namespace MinecraftClient.Protocol /// Dictionary with a link to the skin and cape of a player. public static SkinInfo GetSkinInfo(string uuid) { - Dictionary textureDict; + System.Text.Json.Nodes.JsonObject textureObj; string base64SkinInfo; - Json.JSONData decodedJsonSkinInfo; + System.Text.Json.Nodes.JsonNode? decodedJsonSkinInfo; // Perform web request try @@ -247,7 +227,7 @@ namespace MinecraftClient.Protocol Task fetchTask = httpClient.GetStringAsync("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); fetchTask.Wait(); // Obtain the Base64 encoded skin information from the API. Discard the rest, since it can be obtained easier through other requests. - base64SkinInfo = Json.ParseJson(fetchTask.Result).Properties["properties"].DataArray[0].Properties["value"].StringValue; + base64SkinInfo = Json.ParseJson(fetchTask.Result)!["properties"]![0]!["value"]!.GetStringValue(); fetchTask.Dispose(); } catch (Exception) { return new SkinInfo(); } @@ -257,24 +237,19 @@ namespace MinecraftClient.Protocol // Assert temporary variable for readablity. // Contains skin and cape information. - textureDict = decodedJsonSkinInfo.Properties["textures"].Properties; + textureObj = decodedJsonSkinInfo!["textures"]!.AsObject(); // Can apparently be missing, if no custom skin is set. - // Probably for completely new accounts. - // (Still exists after changing back to Steve or Alex skin.) - if (textureDict.ContainsKey("SKIN")) + if (textureObj.ContainsKey("SKIN")) { - return new SkinInfo(skinUrl: textureDict["SKIN"].Properties.ContainsKey("url") ? textureDict["SKIN"].Properties["url"].StringValue : string.Empty, - capeUrl: textureDict.ContainsKey("CAPE") ? textureDict["CAPE"].Properties["url"].StringValue : string.Empty, - skinModel: textureDict["SKIN"].Properties.ContainsKey("metadata") ? "Alex" : "Steve"); + return new SkinInfo(SkinUrl: textureObj["SKIN"]!["url"] is not null ? textureObj["SKIN"]!["url"]!.GetStringValue() : string.Empty, + CapeUrl: textureObj.ContainsKey("CAPE") ? textureObj["CAPE"]!["url"]!.GetStringValue() : string.Empty, + SkinModel: textureObj["SKIN"]!["metadata"] is not null ? "Alex" : "Steve"); } - // Tested it on several players, this case never occured. else { - // This player has assumingly never changed their skin. - // Probably a completely new account. - return new SkinInfo(capeUrl: textureDict.ContainsKey("CAPE") ? textureDict["CAPE"].Properties["url"].StringValue : string.Empty, - skinModel: DefaultModelAlex(uuid) ? "Alex" : "Steve"); + return new SkinInfo(CapeUrl: textureObj.ContainsKey("CAPE") ? textureObj["CAPE"]!["url"]!.GetStringValue() : string.Empty, + SkinModel: DefaultModelAlex(uuid) ? "Alex" : "Steve"); } } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index 74134068..c0e211d6 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -22,6 +22,8 @@ namespace MinecraftClient.Protocol public bool Listed = true; + public int TabListOrder; + // Entity info public Mapping.Entity? entity; @@ -44,13 +46,13 @@ namespace MinecraftClient.Protocol { Uuid = uuid; Name = name; - if (property != null) + if (property is not null) Property = property; Gamemode = gamemode; Ping = ping; DisplayName = displayName; lastMessageVerified = false; - if (timeStamp != null && publicKey != null && signature != null) + if (timeStamp is not null && publicKey is not null && signature is not null) { DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds((long)timeStamp); KeyExpiresAt = dateTimeOffset.UtcDateTime; @@ -73,6 +75,7 @@ namespace MinecraftClient.Protocol Uuid = uuid; Gamemode = -1; Ping = 0; + TabListOrder = 0; lastMessageVerified = true; precedingSignature = null; } @@ -119,7 +122,7 @@ namespace MinecraftClient.Protocol /// Is this message vaild public bool VerifyMessage(string message, long timestamp, long salt, ref byte[] signature) { - if (PublicKey == null || IsKeyExpired()) + if (PublicKey is null || IsKeyExpired()) return false; else { @@ -146,12 +149,12 @@ namespace MinecraftClient.Protocol { if (lastMessageVerified == false) return false; - if (PublicKey == null || IsKeyExpired() || (this.precedingSignature != null && precedingSignature == null)) + if (PublicKey is null || IsKeyExpired() || (this.precedingSignature is not null && precedingSignature is null)) { lastMessageVerified = false; return false; } - if (this.precedingSignature != null && !this.precedingSignature.SequenceEqual(precedingSignature!)) + if (this.precedingSignature is not null && !this.precedingSignature.SequenceEqual(precedingSignature!)) { lastMessageVerified = false; return false; @@ -181,12 +184,12 @@ namespace MinecraftClient.Protocol { if (lastMessageVerified == false) return false; - if (PublicKey == null || IsKeyExpired() || (this.precedingSignature != null && precedingSignature == null)) + if (PublicKey is null || IsKeyExpired() || (this.precedingSignature is not null && precedingSignature is null)) { lastMessageVerified = false; return false; } - if (this.precedingSignature != null && !this.precedingSignature.SequenceEqual(precedingSignature!)) + if (this.precedingSignature is not null && !this.precedingSignature.SequenceEqual(precedingSignature!)) { lastMessageVerified = false; return false; @@ -212,7 +215,7 @@ namespace MinecraftClient.Protocol /// Is this message chain vaild public bool VerifyMessage(string message, Guid playerUuid, Guid chatUuid, int messageIndex, long timestamp, long salt, ref byte[] signature, Tuple[] previousMessageSignatures) { - if (PublicKey == null || IsKeyExpired()) + if (PublicKey is null || IsKeyExpired()) return false; // net.minecraft.server.network.ServerPlayNetworkHandler#validateMessage diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 708e99d9..381af81f 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -12,51 +12,113 @@ namespace MinecraftClient.Protocol.ProfileKey { private static readonly SHA256 sha256Hash = SHA256.Create(); - private static readonly string certificates = "https://api.minecraftservices.com/player/certificates"; - - public static PlayerKeyPair? GetNewProfileKeys(string accessToken, bool isYggdrasil) + /// + /// Check whether the authentication server supports player profile keys. + /// For Yggdrasil servers, this fetches the authlib-injector metadata and checks the + /// feature.enable_profile_key flag documented at + /// https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83 + /// + public static bool AuthServerSupportsProfileKeys(bool isYggdrasil) { + if (!isYggdrasil) + return true; + ProxiedWebRequest.Response? response = null; try { - if (!isYggdrasil) + var authServer = Settings.Config.Main.General.AuthServer; + var request = new ProxiedWebRequest( + (authServer.UseHttps ? "https" : "http") + "://" + authServer.Host + ":" + authServer.Port + authServer.AuthlibInjectorAPIPath) { - var request = new ProxiedWebRequest(certificates) - { - Accept = "application/json" - }; - request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken)); + Accept = "application/json" + }; - response = request.Post("application/json", ""); + response = request.Get(); + if (Settings.Config.Logging.DebugMessages) + ConsoleIO.WriteLine(response.Body.ToString()); - if (Settings.Config.Logging.DebugMessages) - { - ConsoleIO.WriteLine(response.Body.ToString()); - } - } - - // see https://github.com/yushijinhun/authlib-injector/blob/da910956eaa30d2f6c2c457222d188aeb53b0d1f/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java#L49 - // POST to "https://api.minecraftservices.com/player/certificates" with authlib-injector will get a dummy response - Json.JSONData json = isYggdrasil ? MakeDummyResponse() : Json.ParseJson(response!.Body); - // Error here - PublicKey publicKey = new(pemKey: json.Properties["keyPair"].Properties["publicKey"].StringValue, - sig: json.Properties["publicKeySignature"].StringValue, - sigV2: json.Properties["publicKeySignatureV2"].StringValue); - - PrivateKey privateKey = new(pemKey: json.Properties["keyPair"].Properties["privateKey"].StringValue); - - return new PlayerKeyPair(publicKey, privateKey, - expiresAt: json.Properties["expiresAt"].StringValue, - refreshedAfter: json.Properties["refreshedAfter"].StringValue); + var json = Json.ParseJson(response.Body); + bool enableProfileKey = json?["meta"]?["feature.enable_profile_key"]?.GetStringValue() == "true"; + return enableProfileKey; } catch (Exception e) { - int code = response == null ? 0 : response.StatusCode; + int code = response is null ? 0 : response.StatusCode; + ConsoleIO.WriteLineFormatted("§cFetch authlib-injector metadata failed: HttpCode = " + code + ", Error = " + e.Message); + if (Settings.Config.Logging.DebugMessages) + ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); + } + return false; + } + + public static PlayerKeyPair? GetNewProfileKeys(string accessToken, bool isYggdrasil) + { + if (string.IsNullOrWhiteSpace(accessToken)) + return null; + + if (!AuthServerSupportsProfileKeys(isYggdrasil)) + { + if (Settings.Config.Logging.DebugMessages) + ConsoleIO.WriteLine("AuthServer does not support profile keys, will not attempt to fetch them."); + return null; + } + + string certificatesURL = "https://api.minecraftservices.com/player/certificates"; + if (isYggdrasil) + { + var authServer = Settings.Config.Main.General.AuthServer; + certificatesURL = (authServer.UseHttps ? "https" : "http") + "://" + authServer.Host + ":" + authServer.Port + + authServer.AuthlibInjectorAPIPath + "/minecraftservices/player/certificates"; + } + + ProxiedWebRequest.Response? response = null; + try + { + var request = new ProxiedWebRequest(certificatesURL) + { + Accept = "application/json" + }; + request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken)); + + response = request.Post("application/json", ""); + + if (Settings.Config.Logging.DebugMessages) + ConsoleIO.WriteLine(response.Body.ToString()); + + if (response.StatusCode < 200 || response.StatusCode >= 300) + { + throw new InvalidOperationException(string.IsNullOrWhiteSpace(response.Body) + ? "Certificate endpoint returned an error response." + : response.Body); + } + + var json = Json.ParseJson(response.Body); + if (json?["keyPair"]?["publicKey"] is null + || json["keyPair"]?["privateKey"] is null + || json["publicKeySignature"] is null + || json["publicKeySignatureV2"] is null + || json["expiresAt"] is null + || json["refreshedAfter"] is null) + { + throw new InvalidOperationException("Certificate endpoint returned an unexpected payload."); + } + + PublicKey publicKey = new(pemKey: json!["keyPair"]!["publicKey"]!.GetStringValue(), + sig: json["publicKeySignature"]!.GetStringValue(), + sigV2: json["publicKeySignatureV2"]!.GetStringValue()); + + PrivateKey privateKey = new(pemKey: json["keyPair"]!["privateKey"]!.GetStringValue()); + + return new PlayerKeyPair(publicKey, privateKey, + expiresAt: json["expiresAt"]!.GetStringValue(), + refreshedAfter: json["refreshedAfter"]!.GetStringValue()); + } + catch (Exception e) + { + int code = response is null ? 0 : response.StatusCode; ConsoleIO.WriteLineFormatted("§cFetch profile key failed: HttpCode = " + code + ", Error = " + e.Message); if (Settings.Config.Logging.DebugMessages) - { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); - } return null; } } @@ -147,7 +209,7 @@ namespace MinecraftClient.Protocol.ProfileKey { List data = new(); - if (precedingSignature != null) + if (precedingSignature is not null) data.AddRange(precedingSignature); data.AddRange(sender.ToBigEndianBytes()); @@ -191,73 +253,7 @@ namespace MinecraftClient.Protocol.ProfileKey return data.ToArray(); } - // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs - public static string EscapeString(string src) - { - StringBuilder sb = new(); - - int start = 0; - for (int i = 0; i < src.Length; i++) - { - char c = src[i]; - bool needEscape = c < 32 || c == '"' || c == '\\'; - // Broken lead surrogate - needEscape = needEscape || c >= '\uD800' && c <= '\uDBFF' && - (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'); - // Broken tail surrogate - needEscape = needEscape || c >= '\uDC00' && c <= '\uDFFF' && - (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'); - // To produce valid JavaScript - needEscape = needEscape || c == '\u2028' || c == '\u2029'; - - if (needEscape) - { - sb.Append(src, start, i - start); - switch (src[i]) - { - case '\b': sb.Append("\\b"); break; - case '\f': sb.Append("\\f"); break; - case '\n': sb.Append("\\n"); break; - case '\r': sb.Append("\\r"); break; - case '\t': sb.Append("\\t"); break; - case '\"': sb.Append("\\\""); break; - case '\\': sb.Append("\\\\"); break; - default: - sb.Append("\\u"); - sb.Append(((int)src[i]).ToString("x04")); - break; - } - start = i + 1; - } - - } - sb.Append(src, start, src.Length - start); - return sb.ToString(); - } - - public static Json.JSONData MakeDummyResponse() - { - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); - var mimePublicKey = Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo()); - var mimePrivateKey = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey()); - string publicKeyPEM = $"-----BEGIN RSA PUBLIC KEY-----\n{mimePublicKey}\n-----END RSA PUBLIC KEY-----\n"; - string privateKeyPEM = $"-----BEGIN RSA PRIVATE KEY-----\n{mimePrivateKey}\n-----END RSA PRIVATE KEY-----\n"; - DateTime now = DateTime.UtcNow; - DateTime expiresAt = now.AddHours(48); - DateTime refreshedAfter = now.AddHours(36); - Json.JSONData response = new(Json.JSONData.DataType.Object); - Json.JSONData keyPairObj = new(Json.JSONData.DataType.Object); - keyPairObj.Properties["privateKey"] = new(Json.JSONData.DataType.String){ StringValue = privateKeyPEM }; - keyPairObj.Properties["publicKey"] = new(Json.JSONData.DataType.String){ StringValue = publicKeyPEM }; - - response.Properties["keyPair"] = keyPairObj; - response.Properties["publicKeySignature"] = new(Json.JSONData.DataType.String){ StringValue = "AA==" }; - response.Properties["publicKeySignatureV2"] = new(Json.JSONData.DataType.String){ StringValue = "AA==" }; - string format = "yyyy-MM-ddTHH:mm:ss.ffffffZ"; - response.Properties["expiresAt"] = new(Json.JSONData.DataType.String){ StringValue = expiresAt.ToString(format) }; - response.Properties["refreshedAfter"] = new(Json.JSONData.DataType.String){ StringValue = refreshedAfter.ToString(format) }; - - return response; - } + // Delegate to the shared Json.EscapeString backed by System.Text.Json + public static string EscapeString(string src) => Json.EscapeString(src); } } diff --git a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs index 8d524fa8..9af643c7 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using System.Timers; using static MinecraftClient.Settings; using static MinecraftClient.Settings.MainConfigHelper.MainConfig.AdvancedConfig; @@ -19,7 +18,6 @@ namespace MinecraftClient.Protocol.ProfileKey private static readonly Dictionary keys = new(); private static readonly Timer updatetimer = new(100); private static readonly List> pendingadds = new(); - private static readonly BinaryFormatter formatter = new(); /// /// Retrieve whether KeysCache contains a keys for the given login. diff --git a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs index 572b0d06..2c1fc589 100644 --- a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs +++ b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs @@ -73,11 +73,11 @@ namespace MinecraftClient.Protocol.ProfileKey { List datas = new(); datas.Add(Convert.ToBase64String(PublicKey.Key)); - if (PublicKey.Signature == null) + if (PublicKey.Signature is null) datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.Signature)); - if (PublicKey.SignatureV2 == null) + if (PublicKey.SignatureV2 is null) datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.SignatureV2)); diff --git a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs index 2208e04e..faa8ba33 100644 --- a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs @@ -25,10 +25,10 @@ namespace MinecraftClient.Protocol.ProfileKey if (!string.IsNullOrEmpty(sigV2)) SignatureV2 = Convert.FromBase64String(sigV2!); - if (SignatureV2 == null || SignatureV2.Length == 0) + if (SignatureV2 is null || SignatureV2.Length == 0) SignatureV2 = Signature; - if (Signature == null || Signature.Length == 0) + if (Signature is null || Signature.Length == 0) Signature = SignatureV2; } diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index cbf1b1cd..a8568f38 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -1,12 +1,12 @@ -using System; +using System; using System.Collections.Generic; using System.Data.Odbc; using System.Globalization; using System.Linq; -using System.Net.Security; +using System.Net.Http; using System.Net.Sockets; -using System.Security.Authentication; using System.Text; +using System.Text.RegularExpressions; using DnsClient; using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers.Forge; @@ -145,19 +145,22 @@ namespace MinecraftClient.Protocol public static IMinecraftCom GetProtocolHandler(TcpClient client, int protocolVersion, ForgeInfo? forgeInfo, IMinecraftComHandler handler) { + int normalizedVersion = NormalizeSnapshotProtocol(protocolVersion); + int[] suppoertedVersionsProtocol16 = { 51, 60, 61, 72, 73, 74, 78 }; - if (Array.IndexOf(suppoertedVersionsProtocol16, protocolVersion) > -1) - return new Protocol16Handler(client, protocolVersion, handler); + if (Array.IndexOf(suppoertedVersionsProtocol16, normalizedVersion) > -1) + return new Protocol16Handler(client, normalizedVersion, handler); int[] suppoertedVersionsProtocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, - 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765 + 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, + 769, 770, 771, 772, 773, 774, 775 }; - if (Array.IndexOf(suppoertedVersionsProtocol18, protocolVersion) > -1) - return new Protocol18Handler(client, protocolVersion, handler, forgeInfo); + if (Array.IndexOf(suppoertedVersionsProtocol18, normalizedVersion) > -1) + return new Protocol18Handler(client, normalizedVersion, handler, forgeInfo, protocolVersion); throw new NotSupportedException(string.Format(Translations.exception_version_unsupport, protocolVersion)); } @@ -345,6 +348,32 @@ namespace MinecraftClient.Protocol case "1.20.3": case "1.20.4": return 765; + case "1.20.5": + case "1.20.6": + return 766; + case "1.21": + case "1.21.1": + return 767; + case "1.21.2": + return 768; + case "1.21.3": + return 768; + case "1.21.4": + return 769; + case "1.21.5": + return 770; + case "1.21.6": + return 771; + case "1.21.7": + case "1.21.8": + return 772; + case "1.21.9": + case "1.21.10": + return 773; + case "1.21.11": + return 774; + case "26.1": + return 775; default: return 0; } @@ -360,6 +389,61 @@ namespace MinecraftClient.Protocol } } + private static readonly Regex VersionTokenRegex = new(@"\d+\.\d+(?:\.\d+)?", RegexOptions.Compiled); + + private static readonly int[] SupportedProtocols18 = + [ + 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, + 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, + 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, + 772, 773, 774, 775 + ]; + + /// + /// For multi-version servers (e.g. "Requires MC 1.8 / 1.21"), try to find the + /// highest protocol version that both the server and MCC support. + /// Returns true if the protocol was upgraded, with the new value in + /// . + /// + public static bool TryUpgradeProtocolVersion(string versionName, ref int protocolVersion) + { + if (string.IsNullOrEmpty(versionName)) + return false; + + var matches = VersionTokenRegex.Matches(versionName); + if (matches.Count < 2) + return false; + + int bestProtocol = protocolVersion; + string bestVersion = ""; + + foreach (Match m in matches) + { + int proto = MCVer2ProtocolVersion(m.Value); + if (proto <= 0) + continue; + if (Array.IndexOf(SupportedProtocols18, proto) < 0) + continue; + if (proto > bestProtocol) + { + bestProtocol = proto; + bestVersion = m.Value; + } + } + + if (bestProtocol > protocolVersion && bestVersion.Length > 0) + { + ConsoleIO.WriteLineFormatted("§8" + string.Format( + Translations.mcc_server_info_version_upgrade, + ProtocolVersion2MCVer(protocolVersion), protocolVersion, + "§a" + bestVersion + "§8", bestProtocol)); + protocolVersion = bestProtocol; + return true; + } + + return false; + } + /// /// Convert a network protocol version number to human-readable Minecraft version number /// @@ -424,10 +508,36 @@ namespace MinecraftClient.Protocol 763 => "1.20", 764 => "1.20.2", 765 => "1.20.4", + 766 => "1.20.6", + 767 => "1.21", + 768 => "1.21.2", + 769 => "1.21.4", + 770 => "1.21.5", + 771 => "1.21.6", + 772 => "1.21.7", + 773 => "1.21.9", + 774 => "1.21.11", + 775 => "26.1", _ => "0.0" }; } + /// + /// Normalize snapshot/pre-release protocol numbers (0x40000000 | data_version) to the + /// corresponding release protocol number. Unknown snapshot versions pass through unchanged. + /// + public static int NormalizeSnapshotProtocol(int protocol) + { + if ((protocol & 0x40000000) == 0) + return protocol; + + return protocol switch + { + 0x4000012E => 775, // 26.1-rc-2 → 26.1 + _ => protocol + }; + } + /// /// Check if we can force-enable Forge support for a Minecraft version without using server Ping /// @@ -526,16 +636,15 @@ namespace MinecraftClient.Protocol } else { - Json.JSONData loginResponse = Json.ParseJson(result); - if (loginResponse.Properties.ContainsKey("accessToken") - && loginResponse.Properties.ContainsKey("selectedProfile") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + var loginResponse = Json.ParseJson(result); + if (loginResponse?["accessToken"] is not null + && loginResponse["selectedProfile"]?["id"] is not null + && loginResponse["selectedProfile"]?["name"] is not null) { - session.ID = loginResponse.Properties["accessToken"].StringValue; - session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.ID = loginResponse["accessToken"]!.GetStringValue(); + session.PlayerID = loginResponse["selectedProfile"]!["id"]!.GetStringValue(); + session.PlayerName = loginResponse["selectedProfile"]!["name"]! + .GetStringValue(); return LoginResult.Success; } else return LoginResult.InvalidResponse; @@ -603,7 +712,8 @@ namespace MinecraftClient.Protocol JsonEncode(user) + "\", \"password\": \"" + JsonEncode(pass) + "\", \"clientToken\": \"" + JsonEncode(session.ClientID) + "\" }"; int code = DoHTTPSPost(Config.Main.General.AuthServer.Host, Config.Main.General.AuthServer.Port, - "/api/yggdrasil/authserver/authenticate", json_request, ref result); + Config.Main.General.AuthServer.AuthlibInjectorAPIPath + "/authserver/authenticate", json_request, + Config.Main.General.AuthServer.UseHttps, ref result); if (code == 200) { if (result.Contains("availableProfiles\":[]}")) @@ -612,47 +722,52 @@ namespace MinecraftClient.Protocol } else { - Json.JSONData loginResponse = Json.ParseJson(result); - if (loginResponse.Properties.ContainsKey("accessToken")) + var loginResponse = Json.ParseJson(result); + if (loginResponse?["accessToken"] is not null) { - session.ID = loginResponse.Properties["accessToken"].StringValue; - if (loginResponse.Properties.ContainsKey("selectedProfile") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + session.ID = loginResponse["accessToken"]!.GetStringValue(); + if (loginResponse["selectedProfile"]?["id"] is not null + && loginResponse["selectedProfile"]?["name"] is not null) { - session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"] - .StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.PlayerID = loginResponse["selectedProfile"]!["id"]! + .GetStringValue(); + session.PlayerName = loginResponse["selectedProfile"]!["name"]! + .GetStringValue(); return LoginResult.Success; } else { string availableProfiles = ""; - foreach (Json.JSONData profile in loginResponse.Properties["availableProfiles"] - .DataArray) + foreach (var profile in loginResponse["availableProfiles"]!.AsArray()) { - availableProfiles += " " + profile.Properties["name"].StringValue; + availableProfiles += " " + profile!["name"]!.GetStringValue(); } ConsoleIO.WriteLine(Translations.mcc_avaliable_profiles + availableProfiles); - ConsoleIO.WriteLine(Translations.mcc_select_profile); - string selectedProfileName = ConsoleIO.ReadLine(); - ConsoleIO.WriteLine(Translations.mcc_selected_profile + " " + selectedProfileName); - Json.JSONData? selectedProfile = null; - foreach (Json.JSONData profile in loginResponse.Properties["availableProfiles"] - .DataArray) + string selectedProfileName; + + if (String.IsNullOrEmpty(Config.Main.General.AuthUser) || String.IsNullOrWhiteSpace(Config.Main.General.AuthUser)) { - selectedProfile = profile.Properties["name"].StringValue == selectedProfileName + ConsoleIO.WriteLine(Translations.mcc_select_profile); + selectedProfileName = ConsoleIO.ReadLine(); + } + else selectedProfileName = Config.Main.General.AuthUser; + + ConsoleIO.WriteLine(Translations.mcc_selected_profile + " " + selectedProfileName); + + System.Text.Json.Nodes.JsonNode? selectedProfile = null; + foreach (var profile in loginResponse["availableProfiles"]!.AsArray()) + { + selectedProfile = profile!["name"]!.GetStringValue() == selectedProfileName ? profile : selectedProfile; } - if (selectedProfile != null) + if (selectedProfile is not null) { - session.PlayerID = selectedProfile.Properties["id"].StringValue; - session.PlayerName = selectedProfile.Properties["name"].StringValue; + session.PlayerID = selectedProfile["id"]!.GetStringValue(); + session.PlayerName = selectedProfile["name"]!.GetStringValue(); SessionToken currentsession = session; return GetNewYggdrasilToken(currentsession, out session); } @@ -717,20 +832,27 @@ namespace MinecraftClient.Protocol } /// - /// Sign-in to Microsoft Account without using browser. Only works if 2FA is disabled. - /// Might not work well in some rare cases. + /// Sign-in to Microsoft Account using OAuth 2.0 device code flow. + /// Supports accounts with 2FA enabled. /// - /// - /// + /// Email hint (unused in device code flow, kept for API compatibility) + /// Password (unused in device code flow, kept for API compatibility) /// /// private static LoginResult MicrosoftMCCLogin(string email, string password, out SessionToken session) { try { - var msaResponse = XboxLive.UserLogin(email, password, XboxLive.PreAuth()); - // Remove refresh token for MCC sign method - msaResponse.RefreshToken = string.Empty; + var deviceCode = Microsoft.RequestDeviceCode(); + + ConsoleIO.WriteLineFormatted(string.Format(Translations.mcc_device_code_prompt, deviceCode.VerificationUri, deviceCode.UserCode)); + + // Try to open the verification URL in the user's browser + Microsoft.OpenBrowser(deviceCode.VerificationUri); + + ConsoleIO.WriteLineFormatted(Translations.mcc_device_code_waiting); + + var msaResponse = Microsoft.PollDeviceCodeToken(deviceCode.DeviceCode, deviceCode.ExpiresIn, deviceCode.Interval); return MicrosoftLogin(msaResponse, out session); } catch (Exception e) @@ -742,7 +864,7 @@ namespace MinecraftClient.Protocol ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } - return LoginResult.WrongPassword; // Might not always be wrong password + return LoginResult.OtherError; } } @@ -826,7 +948,7 @@ namespace MinecraftClient.Protocol { var payload = JwtPayloadDecode.GetPayload(session.ID); var json = Json.ParseJson(payload); - var expTimestamp = long.Parse(json.Properties["exp"].StringValue, NumberStyles.Any, + var expTimestamp = long.Parse(json!["exp"]!.GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture); var now = DateTime.Now; var tokenExp = UnixTimeStampToDateTime(expTimestamp); @@ -866,22 +988,21 @@ namespace MinecraftClient.Protocol int code = DoHTTPSPost("authserver.mojang.com", 443, "/refresh", json_request, ref result); if (code == 200) { - if (result == null) + if (result is null) { return LoginResult.NullError; } else { - Json.JSONData loginResponse = Json.ParseJson(result); - if (loginResponse.Properties.ContainsKey("accessToken") - && loginResponse.Properties.ContainsKey("selectedProfile") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + var loginResponse = Json.ParseJson(result); + if (loginResponse?["accessToken"] is not null + && loginResponse["selectedProfile"]?["id"] is not null + && loginResponse["selectedProfile"]?["name"] is not null) { - session.ID = loginResponse.Properties["accessToken"].StringValue; - session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.ID = loginResponse["accessToken"]!.GetStringValue(); + session.PlayerID = loginResponse["selectedProfile"]!["id"]!.GetStringValue(); + session.PlayerName = loginResponse["selectedProfile"]!["name"]! + .GetStringValue(); return LoginResult.Success; } else return LoginResult.InvalidResponse; @@ -914,25 +1035,25 @@ namespace MinecraftClient.Protocol "\", \"selectedProfile\": { \"id\": \"" + JsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + JsonEncode(currentsession.PlayerName) + "\" } }"; int code = DoHTTPSPost(Config.Main.General.AuthServer.Host, Config.Main.General.AuthServer.Port, - "/api/yggdrasil/authserver/refresh", json_request, ref result); + Config.Main.General.AuthServer.AuthlibInjectorAPIPath + "/authserver/refresh", json_request, + Config.Main.General.AuthServer.UseHttps, ref result); if (code == 200) { - if (result == null) + if (result is null) { return LoginResult.NullError; } else { - Json.JSONData loginResponse = Json.ParseJson(result); - if (loginResponse.Properties.ContainsKey("accessToken") - && loginResponse.Properties.ContainsKey("selectedProfile") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") - && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + var loginResponse = Json.ParseJson(result); + if (loginResponse?["accessToken"] is not null + && loginResponse["selectedProfile"]?["id"] is not null + && loginResponse["selectedProfile"]?["name"] is not null) { - session.ID = loginResponse.Properties["accessToken"].StringValue; - session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.ID = loginResponse["accessToken"]!.GetStringValue(); + session.PlayerID = loginResponse["selectedProfile"]!["id"]!.GetStringValue(); + session.PlayerName = loginResponse["selectedProfile"]!["name"]! + .GetStringValue(); return LoginResult.Success; } else return LoginResult.InvalidResponse; @@ -974,10 +1095,11 @@ namespace MinecraftClient.Protocol : "sessionserver.mojang.com"; int port = type == LoginType.yggdrasil ? Config.Main.General.AuthServer.Port : 443; string endpoint = type == LoginType.yggdrasil - ? "/api/yggdrasil/sessionserver/session/minecraft/join" + ? Config.Main.General.AuthServer.AuthlibInjectorAPIPath + "/sessionserver/session/minecraft/join" : "/session/minecraft/join"; - int code = DoHTTPSPost(host, port, endpoint, json_request, ref result); + bool useHttps = type == LoginType.yggdrasil ? Config.Main.General.AuthServer.UseHttps : true; + int code = DoHTTPSPost(host, port, endpoint, json_request, useHttps, ref result); return (code >= 200 && code < 300); } catch @@ -1002,28 +1124,27 @@ namespace MinecraftClient.Protocol string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion); DoHTTPSGet("pc.realms.minecraft.net", 443, "/worlds", cookies, ref result); - Json.JSONData realmsWorlds = Json.ParseJson(result); - if (realmsWorlds.Properties.ContainsKey("servers") - && realmsWorlds.Properties["servers"].Type == Json.JSONData.DataType.Array - && realmsWorlds.Properties["servers"].DataArray.Count > 0) + var realmsWorlds = Json.ParseJson(result); + if (realmsWorlds?["servers"] is System.Text.Json.Nodes.JsonArray serversArray + && serversArray.Count > 0) { List availableWorlds = new(); // Store string to print int index = 0; - foreach (Json.JSONData realmsServer in realmsWorlds.Properties["servers"].DataArray) + foreach (var realmsServer in serversArray) { - if (realmsServer.Properties.ContainsKey("name") - && realmsServer.Properties.ContainsKey("owner") - && realmsServer.Properties.ContainsKey("id") - && realmsServer.Properties.ContainsKey("expired")) + if (realmsServer?["name"] is not null + && realmsServer["owner"] is not null + && realmsServer["id"] is not null + && realmsServer["expired"] is not null) { - if (realmsServer.Properties["expired"].StringValue == "false") + if (realmsServer["expired"].GetStringValue() == "false") { availableWorlds.Add(String.Format("[{0}] {2} ({3}) - {1}", index++, - realmsServer.Properties["id"].StringValue, - realmsServer.Properties["name"].StringValue, - realmsServer.Properties["owner"].StringValue)); - realmsWorldsResult.Add(realmsServer.Properties["id"].StringValue); + realmsServer["id"]!.GetStringValue(), + realmsServer["name"]!.GetStringValue(), + realmsServer["owner"]!.GetStringValue())); + realmsWorldsResult.Add(realmsServer["id"]!.GetStringValue()); } } } @@ -1069,9 +1190,9 @@ namespace MinecraftClient.Protocol cookies, ref result); if (statusCode == 200) { - Json.JSONData serverAddress = Json.ParseJson(result); - if (serverAddress.Properties.ContainsKey("address")) - return serverAddress.Properties["address"].StringValue; + var serverAddress = Json.ParseJson(result); + if (serverAddress?["address"] is not null) + return serverAddress["address"]!.GetStringValue(); else { ConsoleIO.WriteLine(Translations.error_realms_ip_error); @@ -1100,61 +1221,68 @@ namespace MinecraftClient.Protocol /// Make a HTTPS GET request to the specified endpoint of the Mojang API /// /// Host to connect to - /// Endpoint for making the request + /// Port to connect on + /// Path for making the request /// Cookies for making the request /// Request result /// HTTP Status code - private static int DoHTTPSGet(string host, int port, string endpoint, string cookies, ref string result) + private static int DoHTTPSGet(string host, int port, string path, string cookies, ref string result) { - List http_request = new() + Dictionary headers = new() { - "GET " + endpoint + " HTTP/1.1", - "Cookie: " + cookies, - "Cache-Control: no-cache", - "Pragma: no-cache", - "Host: " + host, - "User-Agent: Java/1.6.0_27", - "Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7", - "Connection: close", - "", - "" + { "Cookie", cookies }, + { "Cache-Control", "no-cache" }, + { "Pragma", "no-cache" }, + { "User-Agent", "Java/1.6.0_27" } }; - return DoHTTPSRequest(http_request, host, port, ref result); + return DoHTTPSRequest(HttpMethod.Get, host, port, path, headers, null, useHttps: true, ref result); } /// - /// Make a HTTPS POST request to the specified endpoint of the Mojang API + /// Make a POST request to the specified endpoint of the Mojang API /// /// Host to connect to - /// Endpoint for making the request - /// Request payload + /// Port to connect on + /// Path for making the request + /// Request payload /// Request result /// HTTP Status code - private static int DoHTTPSPost(string host, int port, string endpoint, string request, ref string result) + private static int DoHTTPSPost(string host, int port, string path, string body, ref string result) + => DoHTTPSPost(host, port, path, body, useHttps: true, ref result); + + /// + /// Make a POST request to the specified endpoint of the Mojang API + /// + /// Host to connect to + /// Port to connect on + /// Path for making the request + /// Request payload + /// Whether to use HTTPS (true) or plain HTTP (false) + /// Request result + /// HTTP Status code + private static int DoHTTPSPost(string host, int port, string path, string body, bool useHttps, ref string result) { - List http_request = new() + Dictionary headers = new() { - "POST " + endpoint + " HTTP/1.1", - "Host: " + host, - "User-Agent: MCC/" + Program.Version, - "Content-Type: application/json", - "Content-Length: " + Encoding.ASCII.GetBytes(request).Length, - "Connection: close", - "", - request + { "User-Agent", "MCC/" + Program.Version }, + { "Content-Type", "application/json" } }; - return DoHTTPSRequest(http_request, host, port, ref result); + return DoHTTPSRequest(HttpMethod.Post, host, port, path, headers, body, useHttps, ref result); } /// - /// Manual HTTPS request since we must directly use a TcpClient because of the proxy. - /// This method connects to the server, enables SSL, do the request and read the response. + /// This method connects to the server and performs an HTTP or HTTPS request via proxy if configured. /// - /// Request headers and optional body (POST) + /// HTTP method /// Host to connect to + /// Port to connect on + /// Request path + /// Request headers + /// Optional request body (POST) + /// Whether to use HTTPS (true) or plain HTTP (false) /// Request result /// HTTP Status code - private static int DoHTTPSRequest(List headers, string host, int port, ref string result) + private static int DoHTTPSRequest(HttpMethod method, string host, int port, string path, Dictionary headers, string? body, bool useHttps, ref string result) { string? postResult = null; int statusCode = 520; @@ -1166,40 +1294,45 @@ namespace MinecraftClient.Protocol if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.debug_request, host)); - TcpClient client = ProxyHandler.NewTcpClient(host, port, true); - SslStream stream = new(client.GetStream()); - stream.AuthenticateAsClient(host, null, SslProtocols.Tls12, - true); // Enable TLS 1.2. Hotfix for #1780 + using SocketsHttpHandler handler = new SocketsHttpHandler(); + handler.ConnectCallback = async (ctx, ct) => + { + TcpClient client = ProxyHandler.NewTcpClient(host, port, true); + return client.GetStream(); + }; + + using HttpClient client = new HttpClient(handler); + + string scheme = useHttps ? "https" : "http"; + var request = new HttpRequestMessage(method, scheme + "://" + host + ":" + port + path); + + var contentType = "text/plain"; + foreach (var header in headers) + { + request.Headers.TryAddWithoutValidation(header.Key, header.Value); + if (header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)) + contentType = header.Value; + } + + if (body is not null) + request.Content = new StringContent(body, Encoding.UTF8, contentType); if (Settings.Config.Logging.DebugMessages) - foreach (string line in headers) - ConsoleIO.WriteLineFormatted("§8> " + line); + ConsoleIO.WriteLineFormatted("§8> " + request); - stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray()))); - System.IO.StreamReader sr = new(stream); - string raw_result = sr.ReadToEnd(); + HttpResponseMessage response = client.SendAsync(request).GetAwaiter().GetResult(); + statusCode = (int)response.StatusCode; + + postResult = statusCode == 204 + ? "No Content" + : response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(""); - foreach (string line in raw_result.Split('\n')) + foreach (string line in postResult.Split('\n')) ConsoleIO.WriteLineFormatted("§8< " + line); } - - if (raw_result.StartsWith("HTTP/1.1")) - { - statusCode = int.Parse(raw_result.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture); - if (statusCode != 204) - { - var splited = raw_result[(raw_result.IndexOf("\r\n\r\n") + 4)..].Split("\r\n"); - postResult = splited[1] + splited[3]; - } - else - { - postResult = "No Content"; - } - } - else statusCode = 520; //Web server is returning an unknown error } catch (Exception e) { @@ -1209,9 +1342,9 @@ namespace MinecraftClient.Protocol } } }, TimeSpan.FromSeconds(30)); - if (postResult != null) + if (postResult is not null) result = postResult; - if (exception != null) + if (exception is not null) throw exception; return statusCode; } diff --git a/MinecraftClient/Protocol/ProxiedWebRequest.cs b/MinecraftClient/Protocol/ProxiedWebRequest.cs index 06e69a9d..220ac319 100644 --- a/MinecraftClient/Protocol/ProxiedWebRequest.cs +++ b/MinecraftClient/Protocol/ProxiedWebRequest.cs @@ -1,437 +1,211 @@ -using System; -using System.Collections.Generic; +using System; using System.Collections.Specialized; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Authentication; +using System.Net; +using System.Net.Http; using System.Text; -using System.Threading; using MinecraftClient.Proxy; namespace MinecraftClient.Protocol { /// - /// Create a new http request and optionally with proxy according to setting + /// HTTP client with automatic proxy support based on application settings. + /// Backed by System.Net.Http.HttpClient with SocketsHttpHandler. /// public class ProxiedWebRequest { - public interface ITcpFactory + private const int DefaultConnectTimeoutSeconds = 30; + + private readonly Uri _uri; + + public NameValueCollection Headers { get; } = new(); + + public string UserAgent { - TcpClient CreateTcpClient(string host, int port); - }; + get => Headers.Get("User-Agent") ?? string.Empty; + set => Headers.Set("User-Agent", value); + } - private readonly string httpVersion = "HTTP/1.1"; + public string Accept + { + get => Headers.Get("Accept") ?? string.Empty; + set => Headers.Set("Accept", value); + } - private ITcpFactory? tcpFactory; - private bool isProxied = false; // Send absolute Url in request if true + public string Cookie + { + set => Headers.Set("Cookie", value); + } - private readonly Uri uri; - private string Host { get { return uri.Host; } } - private int Port { get { return uri.Port; } } - private string Path { get { return uri.PathAndQuery; } } - private string AbsoluteUrl { get { return uri.AbsoluteUri; } } - private bool IsSecure { get { return uri.Scheme == "https"; } } - - public NameValueCollection Headers = new(); - - public string UserAgent { get { return Headers.Get("User-Agent") ?? String.Empty; } set { Headers.Set("User-Agent", value); } } - public string Accept { get { return Headers.Get("Accept") ?? String.Empty; } set { Headers.Set("Accept", value); } } - public string Cookie { set { Headers.Set("Cookie", value); } } + public bool Debug => Settings.Config.Logging.DebugMessages; /// - /// Set to true to tell the http client proxy is enabled - /// - public bool IsProxy { get { return isProxied; } set { isProxied = value; } } - public bool Debug { get { return Settings.Config.Logging.DebugMessages; } } - - /// - /// Create a new http request + /// Create a new HTTP request /// /// Target URL public ProxiedWebRequest(string url) { - uri = new Uri(url); + _uri = new Uri(url); SetupBasicHeaders(); } /// - /// Create a new http request with cookies + /// Create a new HTTP request with cookies /// /// Target URL - /// Cookies to use + /// Cookies to include in the request public ProxiedWebRequest(string url, NameValueCollection cookies) { - uri = new Uri(url); + _uri = new Uri(url); Headers.Add("Cookie", GetCookieString(cookies)); SetupBasicHeaders(); } - /// - /// Create a new http request with custom tcp client - /// - /// Tcp factory to be used - /// Target URL - public ProxiedWebRequest(ITcpFactory tcpFactory, string url) : this(url) - { - this.tcpFactory = tcpFactory; - } - - /// - /// Create a new http request with custom tcp client and cookies - /// - /// Tcp factory to be used - /// Target URL - /// Cookies to use - public ProxiedWebRequest(ITcpFactory tcpFactory, string url, NameValueCollection cookies) : this(url, cookies) - { - this.tcpFactory = tcpFactory; - } - - /// - /// Setup some basic headers - /// private void SetupBasicHeaders() { - Headers.Add("Host", Host); + Headers.Add("Host", _uri.Host); Headers.Add("User-Agent", "MCC/1.0"); Headers.Add("Accept", "*/*"); - Headers.Add("Connection", "close"); } /// - /// Perform GET request and get the response. Proxy is handled automatically + /// Perform GET request. Proxy is handled automatically. /// - /// - public Response Get() - { - return Send("GET"); - } + public Response Get() => Send(HttpMethod.Get); /// - /// Perform POST request and get the response. Proxy is handled automatically + /// Perform POST request. Proxy is handled automatically. /// /// The content type of request body /// Request body - /// - public Response Post(string contentType, string body) - { - Headers.Add("Content-Type", contentType); - // Calculate length - Headers.Add("Content-Length", Encoding.UTF8.GetBytes(body).Length.ToString()); - return Send("POST", body); - } + public Response Post(string contentType, string body) => Send(HttpMethod.Post, contentType, body); /// - /// Send a http request to the server. Proxy is handled automatically + /// Send an HTTP request. Proxy is configured automatically from Settings. /// - /// Method in string representation - /// Optional request body - /// - private Response Send(string method, string body = "") + private Response Send(HttpMethod method, string? contentType = null, string? body = null) { - List requestMessage = new() + using var handler = CreateHandler(); + using var client = new HttpClient(handler); + + using var request = new HttpRequestMessage(method, _uri); + + // Apply custom headers (skip content-level headers) + foreach (string key in Headers) { - string.Format("{0} {1} {2}", method.ToUpper(), isProxied ? AbsoluteUrl : Path, httpVersion) // Request line - }; - foreach (string key in Headers) // Headers - { - var value = Headers[key]; - requestMessage.Add(string.Format("{0}: {1}", key, value)); + if (key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) || + key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase) || + key.Equals("Host", StringComparison.OrdinalIgnoreCase)) + continue; + + request.Headers.TryAddWithoutValidation(key, Headers[key]); } - requestMessage.Add(""); // - if (body != "") + + if (body is not null) { - requestMessage.Add(body); + request.Content = new StringContent(body, Encoding.UTF8, contentType ?? "text/plain"); } - else requestMessage.Add(""); // + if (Debug) { - foreach (string l in requestMessage) - { - ConsoleIO.WriteLine("< " + l); - } + ConsoleIO.WriteLine($"< {method} {_uri}"); + foreach (string key in Headers) + ConsoleIO.WriteLine($"< {key}: {Headers[key]}"); } - Response response = Response.Empty(); - - // FIXME: Use TcpFactory interface to avoid direct usage of the ProxyHandler class - // TcpClient client = tcpFactory.CreateTcpClient(Host, Port); - TcpClient client = ProxyHandler.NewTcpClient(Host, Port, true); - Stream stream; - if (IsSecure) - { - stream = new SslStream(client.GetStream()); - ((SslStream)stream).AuthenticateAsClient(Host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1774 - } - else - { - stream = client.GetStream(); - } - string h = string.Join("\r\n", requestMessage.ToArray()); - byte[] data = Encoding.ASCII.GetBytes(h); - stream.Write(data, 0, data.Length); - stream.Flush(); - - // Read response - int statusCode = ReadHttpStatus(stream); - var headers = ReadHeader(stream); - string? rbody; - if (headers.Get("transfer-encoding") == "chunked") - { - rbody = ReadBodyChunked(stream); - } - else - { - rbody = ReadBody(stream, int.Parse(headers.Get("content-length") ?? "0")); - } - if (headers.Get("set-cookie") != null) - { - response.Cookies = ParseSetCookie(headers.GetValues("set-cookie") ?? Array.Empty()); - } - response.Body = rbody ?? ""; - response.StatusCode = statusCode; - response.Headers = headers; try { - stream.Close(); - client.Close(); - } - catch { } + using var httpResponse = client.Send(request); + using var stream = httpResponse.Content.ReadAsStream(); + using var reader = new System.IO.StreamReader(stream); + string responseBody = reader.ReadToEnd(); - return response; - } + var responseHeaders = new NameValueCollection(); + foreach (var header in httpResponse.Headers) + foreach (var val in header.Value) + responseHeaders.Add(header.Key.ToLowerInvariant(), val); + foreach (var header in httpResponse.Content.Headers) + foreach (var val in header.Value) + responseHeaders.Add(header.Key.ToLowerInvariant(), val); - /// - /// Read HTTP response line from a Stream - /// - /// Stream to read - /// - /// If server return unknown data - private static int ReadHttpStatus(Stream s) - { - var httpHeader = ReadLine(s); // http header line - if (httpHeader.StartsWith("HTTP/1.1") || httpHeader.StartsWith("HTTP/1.0")) - { - return int.Parse(httpHeader.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture); - } - else - { - throw new InvalidDataException("Unexpect data from server"); - } - } - - /// - /// Read HTTP headers from a Stream - /// - /// Stream to read - /// Headers in lower-case - private static NameValueCollection ReadHeader(Stream s) - { - var headers = new NameValueCollection(); - // Read headers - string header; - do - { - header = ReadLine(s); - if (!String.IsNullOrEmpty(header)) + var cookies = new NameValueCollection(); + foreach (System.Net.Cookie cookie in handler.CookieContainer.GetCookies(_uri)) { - var tmp = header.Split(new char[] { ':' }, 2); - var name = tmp[0].ToLower(); - var value = tmp[1].Trim(); - headers.Add(name, value); + if (!cookie.Expired) + cookies.Add(cookie.Name, cookie.Value); } + + return new Response((int)httpResponse.StatusCode, responseBody, responseHeaders, cookies); + } + catch (HttpRequestException ex) + { + if (Debug) + ConsoleIO.WriteLine("HTTP error: " + ex.Message); + return Response.Empty(); } - while (!String.IsNullOrEmpty(header)); - return headers; } /// - /// Read HTTP body from a Stream + /// Create a SocketsHttpHandler with proxy support from ProxyHandler settings. /// - /// Stream to read - /// Length of the body (the Content-Length header) - /// Body or null if length is zero - private static string? ReadBody(Stream s, int length) + private static SocketsHttpHandler CreateHandler() { - if (length > 0) + var handler = new SocketsHttpHandler { - byte[] buffer = new byte[length]; - int r = 0; - while (r < length) + UseCookies = true, + CookieContainer = new CookieContainer(), + AllowAutoRedirect = false, + ConnectTimeout = TimeSpan.FromSeconds(DefaultConnectTimeoutSeconds), + }; + + if (ProxyHandler.Config.Enabled_Login) + { + string proxyScheme = ProxyHandler.Config.Proxy_Type switch { - var read = s.Read(buffer, r, length - r); - r += read; - Thread.Sleep(50); + ProxyHandler.Configs.ProxyType.SOCKS4 => "socks4", + ProxyHandler.Configs.ProxyType.SOCKS4a => "socks4a", + ProxyHandler.Configs.ProxyType.SOCKS5 => "socks5", + _ => "http" + }; + + var proxyUri = new Uri($"{proxyScheme}://{ProxyHandler.Config.Server.Host}:{ProxyHandler.Config.Server.Port}"); + var proxy = new WebProxy(proxyUri); + + if (!string.IsNullOrWhiteSpace(ProxyHandler.Config.Username) && + !string.IsNullOrWhiteSpace(ProxyHandler.Config.Password)) + { + proxy.Credentials = new NetworkCredential( + ProxyHandler.Config.Username, + ProxyHandler.Config.Password); } - return Encoding.UTF8.GetString(buffer); - } - else - { - return null; + + handler.Proxy = proxy; + handler.UseProxy = true; } + + return handler; } /// - /// Read HTTP chunked body from a Stream + /// Build a cookie header value from a NameValueCollection. /// - /// Stream to read - /// Body or empty string if nothing is received - private static string ReadBodyChunked(Stream s) - { - List buffer1 = new(); - while (true) - { - string l = ReadLine(s); - int size = Int32.Parse(l, NumberStyles.HexNumber); - if (size == 0) - break; - byte[] buffer2 = new byte[size]; - int r = 0; - while (r < size) - { - var read = s.Read(buffer2, r, size - r); - r += read; - Thread.Sleep(50); - } - ReadLine(s); - buffer1.AddRange(buffer2); - } - return Encoding.UTF8.GetString(buffer1.ToArray()); - } - - /// - /// Parse the Set-Cookie header value into NameValueCollection. Cookie options are ignored - /// - /// Array of value strings - /// Parsed cookies - private static NameValueCollection ParseSetCookie(IEnumerable headerValue) - { - NameValueCollection cookies = new(); - foreach (var value in headerValue) - { - string[] cookie = value.Split(';'); // cookie options are ignored - string[] tmp = cookie[0].Split(new char[] { '=' }, 2); // Split first '=' only - string[] options = cookie[1..]; - string cname = tmp[0].Trim(); - string cvalue = tmp[1].Trim(); - // Check expire - bool isExpired = false; - foreach (var option in options) - { - var tmp2 = option.Trim().Split(new char[] { '=' }, 2); - // Check for Expires= and Max-Age= - if (tmp2.Length == 2) - { - var optName = tmp2[0].Trim().ToLower(); - var optValue = tmp2[1].Trim(); - switch (optName) - { - case "expires": - { - if (DateTime.TryParse(optValue, out var expDate)) - { - if (expDate < DateTime.Now) - isExpired = true; - } - break; - } - case "max-age": - { - if (int.TryParse(optValue, out var expInt)) - { - if (expInt <= 0) - isExpired = true; - } - break; - } - } - } - if (isExpired) - break; - } - if (!isExpired) - cookies.Add(cname, cvalue); - } - return cookies; - } - - /// - /// Read a line from a Stream - /// - /// - /// Line break by \r\n and they are not included in returned string - /// - /// Stream to read - /// String - private static string ReadLine(Stream s) - { - List buffer = new(); - byte c; - while (true) - { - int b = s.ReadByte(); - if (b == -1) - break; - c = (byte)b; - if (c == '\n') - { - if (buffer.Last() == '\r') - { - buffer.RemoveAt(buffer.Count - 1); - break; - } - } - buffer.Add(c); - } - return Encoding.UTF8.GetString(buffer.ToArray()); - } - - /// - /// Get the cookie string representation to use in header - /// - /// - /// private static string GetCookieString(NameValueCollection cookies) { var sb = new StringBuilder(); foreach (string key in cookies) { - var value = cookies[key]; - sb.Append(string.Format("{0}={1}; ", key, value)); + sb.Append($"{key}={cookies[key]}; "); } string result = sb.ToString(); - return result.Remove(result.Length - 2); // Remove "; " at the end + return result.Length >= 2 ? result[..^2] : result; } /// - /// Basic response object + /// Basic HTTP response object. /// - public class Response + public record Response(int StatusCode, string Body, NameValueCollection Headers, NameValueCollection Cookies) { - public int StatusCode; - public string Body; - public NameValueCollection Headers; - public NameValueCollection Cookies; - - public Response(int statusCode, string body, NameValueCollection headers, NameValueCollection cookies) - { - StatusCode = statusCode; - Body = body; - Headers = headers; - Cookies = cookies; - } - - /// - /// Get an empty response object - /// - /// - public static Response Empty() - { - return new Response(204 /* No content */, "", new NameValueCollection(), new NameValueCollection()); - } + public static Response Empty() => + new(204, "", new NameValueCollection(), new NameValueCollection()); public override string ToString() { @@ -439,26 +213,18 @@ namespace MinecraftClient.Protocol sb.AppendLine("Status code: " + StatusCode); sb.AppendLine("Headers:"); foreach (string key in Headers) - { - sb.AppendLine(string.Format(" {0}: {1}", key, Headers[key])); - } + sb.AppendLine($" {key}: {Headers[key]}"); if (Cookies.Count > 0) { sb.AppendLine(); sb.AppendLine("Cookies: "); foreach (string key in Cookies) - { - sb.AppendLine(string.Format(" {0}={1}", key, Cookies[key])); - } + sb.AppendLine($" {key}={Cookies[key]}"); } if (Body != "") { sb.AppendLine(); - if (Body.Length > 200) - { - sb.AppendLine("Body: (Truncated to 200 characters)"); - } - else sb.AppendLine("Body: "); + sb.AppendLine(Body.Length > 200 ? "Body: (Truncated to 200 characters)" : "Body: "); sb.AppendLine(Body.Length > 200 ? Body[..200] + "..." : Body); } return sb.ToString(); diff --git a/MinecraftClient/Protocol/ReplayHandler.cs b/MinecraftClient/Protocol/ReplayHandler.cs index 75184e26..81862638 100644 --- a/MinecraftClient/Protocol/ReplayHandler.cs +++ b/MinecraftClient/Protocol/ReplayHandler.cs @@ -1,8 +1,11 @@ -using System; +using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; -using System.Linq; -using Ionic.Zip; +using System.IO.Compression; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers.PacketPalettes; @@ -10,385 +13,445 @@ using MinecraftClient.Protocol.Handlers.PacketPalettes; namespace MinecraftClient.Protocol { /// - /// Record and save replay file that can be used by Replay mod + /// Record and save replay files that can be used by Replay Mod. /// - public class ReplayHandler + public class ReplayHandler : IDisposable { - public string ReplayFileName = @"whhhh.mcpr"; - public string ReplayFileDirectory = @"replay_recordings"; - public MetaDataHandler MetaData; - public bool RecordRunning { get { return !cleanedUp; } } - - private readonly string recordingTmpFileName = @"recording.tmcpr"; - private readonly string temporaryCache = @"recording_cache"; - private readonly DataTypes dataTypes; - private readonly PacketTypePalette packetType; - private readonly int protocolVersion; - private readonly BinaryWriter? recordStream; - private readonly DateTime recordStartTime; - private DateTime lastPacketTime; - private bool prepareCleanUp = false; - private bool cleanedUp = false; + private const string DefaultReplayDirectory = "replay_recordings"; + private const string WorkingRootDirectory = "recording_cache"; + private const string RecordingEntryName = "recording.tmcpr"; + private const string BackupFileName = "REPLAY_BACKUP.mcpr"; private static readonly bool logOutput = true; - private int playerEntityID; - private Guid playerUUID; - private Location playerLastPosition; - private float playerLastYaw; - private float playerLastPitch; + private readonly Lock _sync = new(); + private readonly DataTypes _dataTypes; + private readonly PacketTypePalette _packetType; + private readonly int _protocolVersion; + private readonly string _instanceToken; + private readonly string _workingDirectory; + private readonly string _recordingFilePath; + private readonly string _backupReplayPath; + private readonly EventHandler _processExitHandler; + private readonly FileStream _recordStream; + private readonly DateTime _recordStartTime; + + private ReplayRecordingState _state = ReplayRecordingState.Recording; + private bool _recordStreamClosed; + private bool _disposed; + private DateTime _lastPacketTime; + + private int _playerEntityId = -1; + private Guid _playerUuid; + private Location _playerLastPosition; + private float _playerLastYaw; + private float _playerLastPitch; + + public string ReplayFileName { get; private set; } = string.Empty; + public string ReplayFileDirectory { get; } + public MetaDataHandler MetaData { get; } + + public bool RecordRunning + { + get + { + lock (_sync) + return _state == ReplayRecordingState.Recording; + } + } public ReplayHandler(int protocolVersion) + : this(protocolVersion, null, DefaultReplayDirectory) { - dataTypes = new DataTypes(protocolVersion); - packetType = new PacketTypeHandler().GetTypeHandler(protocolVersion); - this.protocolVersion = protocolVersion; + } - if (!Directory.Exists(ReplayFileDirectory)) - Directory.CreateDirectory(ReplayFileDirectory); - if (!Directory.Exists(temporaryCache)) - Directory.CreateDirectory(temporaryCache); + public ReplayHandler(int protocolVersion, string? serverName, string recordingDirectory = DefaultReplayDirectory) + { + ArgumentException.ThrowIfNullOrWhiteSpace(recordingDirectory); - recordStream = new BinaryWriter(new FileStream(Path.Combine(temporaryCache, recordingTmpFileName), FileMode.Create, FileAccess.ReadWrite)); - recordStartTime = DateTime.Now; + _dataTypes = new DataTypes(protocolVersion); + _packetType = new PacketTypeHandler().GetTypeHandler(protocolVersion); + _protocolVersion = protocolVersion; + ReplayFileDirectory = recordingDirectory; + Directory.CreateDirectory(ReplayFileDirectory); - MetaData = new MetaDataHandler + _instanceToken = Path.GetRandomFileName().Replace(".", string.Empty, StringComparison.Ordinal); + _workingDirectory = Path.Combine(WorkingRootDirectory, $"{DateTime.UtcNow:yyyyMMdd_HHmmss_fff}_{Environment.ProcessId}_{_instanceToken}"); + Directory.CreateDirectory(_workingDirectory); + + _recordingFilePath = Path.Combine(_workingDirectory, RecordingEntryName); + _backupReplayPath = Path.Combine(_workingDirectory, BackupFileName); + _recordStream = new FileStream(_recordingFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); + _processExitHandler = (_, _) => FinalizeOnProcessExit(); + + _recordStartTime = DateTime.UtcNow; + _lastPacketTime = _recordStartTime; + + MetaData = new MetaDataHandler(_workingDirectory) { - date = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds, + serverName = serverName, + date = new DateTimeOffset(_recordStartTime).ToUnixTimeMilliseconds(), protocol = protocolVersion, mcversion = ProtocolHandler.ProtocolVersion2MCVer(protocolVersion) }; MetaData.SaveToFile(); - playerLastPosition = new Location(0, 0, 0); + _playerLastPosition = new Location(0, 0, 0); + AppDomain.CurrentDomain.ProcessExit += _processExitHandler; + WriteLog("Start recording."); } - public ReplayHandler(int protocolVersion, string serverName, string recordingDirectory = @"replay_recordings") - : this(protocolVersion) + public void Dispose() { - dataTypes = new DataTypes(protocolVersion); - packetType = new PacketTypeHandler().GetTypeHandler(protocolVersion); + if (_disposed) + return; - MetaData.serverName = serverName; - ReplayFileDirectory = recordingDirectory; - } - - ~ReplayHandler() - { - OnShutDown(); + try + { + OnShutDown(); + } + finally + { + AppDomain.CurrentDomain.ProcessExit -= _processExitHandler; + _disposed = true; + GC.SuppressFinalize(this); + } } public void SetClientEntityID(int entityID) { - playerEntityID = entityID; + lock (_sync) + { + _playerEntityId = entityID; + if (entityID >= 0) + MetaData.selfId = entityID; + } } public void SetClientPlayerUUID(Guid uuid) { - playerUUID = uuid; - } - - #region File and stream handling - - public void CloseRecordStream() - { - try + lock (_sync) { - recordStream!.Flush(); - recordStream.Close(); + _playerUuid = uuid; + MetaData.AddPlayerUUID(uuid); } - catch { } } + public string GetBackupReplayPath() => _backupReplayPath; + /// - /// Stop recording and save replay file. Should called once before program exit + /// Stop recording and save the replay file. /// public void OnShutDown() { - if (!cleanedUp) + lock (_sync) { - prepareCleanUp = true; - CloseRecordStream(); - CreateReplayFile(); - cleanedUp = true; + EnsureNotDisposed(); + + if (_state != ReplayRecordingState.Recording) + return; + + string replayFileName = GetReplayDefaultName(); + string replayFilePath = ResolveReplayPath(replayFileName); + + WriteLog("Creating replay file."); + _state = ReplayRecordingState.Finalizing; + try + { + CloseRecordStreamUnsafe(); + WriteReplayArchiveUnsafe(replayFilePath, readFromActiveStream: false); + ReplayFileName = replayFileName; + _state = ReplayRecordingState.Stopped; + CleanupWorkingFilesUnsafe(); + WriteLog("Replay file created."); + } + catch + { + _state = ReplayRecordingState.Stopped; + throw; + } } } /// - /// Create the replay file for Replay mod to read + /// Create a snapshot replay file while the recording is still running. /// - public void CreateReplayFile() - { - string replayFileName = GetReplayDefaultName(); - CreateReplayFile(replayFileName); - } - - /// - /// Create the replay file for Replay mod to read - /// - /// Replay file name - public void CreateReplayFile(string replayFileName) - { - WriteLog("Creating replay file."); - - MetaData.duration = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds); - MetaData.SaveToFile(); - - using (Stream recordingFile = new FileStream(Path.Combine(temporaryCache, recordingTmpFileName), FileMode.Open)) - { - using Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open); - using ZipOutputStream zs = new(Path.Combine(ReplayFileDirectory, replayFileName)); - zs.PutNextEntry(recordingTmpFileName); - recordingFile.CopyTo(zs); - zs.PutNextEntry(MetaData.MetaDataFileName); - metaDataFile.CopyTo(zs); - zs.Close(); - } - - File.Delete(Path.Combine(temporaryCache, recordingTmpFileName)); - File.Delete(Path.Combine(temporaryCache, MetaData.MetaDataFileName)); - - WriteLog("Replay file created."); - } - - /// - /// Create a backup replay file while recording - /// - /// public void CreateBackupReplay(string replayFileName) { - if (cleanedUp || prepareCleanUp) - return; - WriteDebugLog("Creating backup replay file."); - - MetaData.duration = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds); - MetaData.SaveToFile(); - - using (Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open)) + lock (_sync) { - using ZipOutputStream zs = new(replayFileName); - zs.PutNextEntry(recordingTmpFileName); - // .CopyTo() method start from stream current position - // We need to reset position in order to get full content - var lastPosition = recordStream!.BaseStream.Position; - recordStream.BaseStream.Position = 0; - recordStream.BaseStream.CopyTo(zs); - recordStream.BaseStream.Position = lastPosition; + EnsureNotDisposed(); - zs.PutNextEntry(MetaData.MetaDataFileName); - metaDataFile.CopyTo(zs); - zs.Close(); + if (_state != ReplayRecordingState.Recording) + return; + + WriteDebugLog("Creating backup replay file."); + WriteReplayArchiveUnsafe(ResolveReplayPath(replayFileName), readFromActiveStream: true); + WriteDebugLog("Backup replay file created."); } - - WriteDebugLog("Backup replay file created."); } /// - /// Get the default mcpr file name by current time + /// Get a default unique replay file name for the current recording. /// - /// public string GetReplayDefaultName() { - var now = DateTime.Now; - return string.Format("{0}_{1}_{2}_{3}_{4}_{5}.mcpr", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); // yyyy_mm_dd_hh_mm_ss + string version = ProtocolHandler.ProtocolVersion2MCVer(_protocolVersion).Replace('.', '_'); + return $"{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss_fff}_{version}_{Environment.ProcessId}_{_instanceToken}.mcpr"; } - #endregion - - #region Packet related method - /// - /// Add a packet from network + /// Add a packet from network capture. /// - /// - /// - /// - /// public void AddPacket(int packetID, IEnumerable packetData, bool isLogin, bool isInbound) { - try - { - if (isInbound) - HandleInBoundPacket(packetID, packetData, isLogin); - else return; + byte[] packetBytes = packetData as byte[] ?? [.. packetData]; - if (PacketShouldSave(packetID, isLogin, isInbound)) - AddPacket(packetID, packetData); - } - catch (Exception e) + lock (_sync) { - WriteDebugLog("Exception while adding packet: " + e.Message + "\n" + e.StackTrace); + if (_disposed || _state != ReplayRecordingState.Recording) + return; + + try + { + if (!isInbound) + return; + + HandleInBoundPacket(packetID, packetBytes, isLogin); + + if (PacketShouldSave(packetID, isLogin, isInbound)) + AddPacketUnsafe(packetID, packetBytes); + } + catch (Exception e) + { + WriteDebugLog("Exception while adding packet: " + e.Message + "\n" + e.StackTrace); + } } } /// - /// Add packet directly without checking (internal use only) + /// Add a player's UUID to the metadata. /// - /// - /// - private void AddPacket(int packetID, IEnumerable packetData) - { - lastPacketTime = DateTime.Now; - // build raw packet - // format: packetID + packetData - List rawPacket = new(); - rawPacket.AddRange(DataTypes.GetVarInt(packetID).ToArray()); - rawPacket.AddRange(packetData.ToArray()); - // build format - // format: timestamp + packetLength + RawPacket - List line = new(); - int nowTime = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds); - line.AddRange(BitConverter.GetBytes((Int32)nowTime).Reverse().ToArray()); - line.AddRange(BitConverter.GetBytes((Int32)rawPacket.Count).Reverse().ToArray()); - line.AddRange(rawPacket.ToArray()); - // Write out to the file - recordStream!.Write(line.ToArray()); - } - - /// - /// Add a player's UUID to the MetaData - /// - /// - /// public void OnPlayerSpawn(Guid uuid) { - // Metadata has a field for storing uuid for all players entered client render range - MetaData.AddPlayerUUID(uuid); + lock (_sync) + { + MetaData.AddPlayerUUID(uuid); + } + } + + private void AddPacketUnsafe(int packetID, byte[] packetData) + { + _lastPacketTime = DateTime.UtcNow; + + byte[] packetId = [.. DataTypes.GetVarInt(packetID)]; + byte[] rawPacket = new byte[packetId.Length + packetData.Length]; + packetId.CopyTo(rawPacket, 0); + packetData.CopyTo(rawPacket, packetId.Length); + + int elapsedMilliseconds = Math.Max(0, Convert.ToInt32((_lastPacketTime - _recordStartTime).TotalMilliseconds)); + Span header = stackalloc byte[8]; + BinaryPrimitives.WriteInt32BigEndian(header, elapsedMilliseconds); + BinaryPrimitives.WriteInt32BigEndian(header[4..], rawPacket.Length); + + _recordStream.Write(header); + _recordStream.Write(rawPacket); } - /// - /// Determine a packet should be saved - /// - /// - /// - /// - /// private bool PacketShouldSave(int packetID, bool isLogin, bool isInbound) { - if (!isInbound) // save inbound only + if (!isInbound) return false; - if (!isLogin) // save all play state packet - { + + if (!isLogin) return true; - } - else - { // is login - if (packetID == 0x02) // login success - { - return true; - } - else return false; - } + + return packetID == 0x02; } - /// - /// Used to gather information needed - /// - /// - /// Also for converting client side packet to server side packet - /// - /// - /// - /// - private void HandleInBoundPacket(int packetID, IEnumerable packetData, bool isLogin) + private void HandleInBoundPacket(int packetID, byte[] packetData, bool isLogin) { Queue p = new(packetData); - PacketTypesIn pType = packetType.GetIncomingTypeById(packetID); - // Login success. Get player UUID + PacketTypesIn pType = _packetType.GetIncomingTypeById(packetID); + if (isLogin && packetID == 0x02) { - if (protocolVersion < Protocol18Handler.MC_1_16_Version) + if (_protocolVersion < Protocol18Handler.MC_1_16_Version) { - if (Guid.TryParse(dataTypes.ReadNextString(p), out Guid uuid)) + if (Guid.TryParse(_dataTypes.ReadNextString(p), out Guid uuid)) { SetClientPlayerUUID(uuid); - WriteDebugLog("User UUID: " + uuid.ToString()); + WriteDebugLog("User UUID: " + uuid); } } else { - var uuid2 = dataTypes.ReadNextUUID(p); - SetClientPlayerUUID(uuid2); - WriteDebugLog("User UUID: " + uuid2.ToString()); + Guid uuid = _dataTypes.ReadNextUUID(p); + SetClientPlayerUUID(uuid); + WriteDebugLog("User UUID: " + uuid); } return; } if (!isLogin && pType == PacketTypesIn.JoinGame) { - // Get client player entity ID - SetClientEntityID(dataTypes.ReadNextInt(p)); + SetClientEntityID(_dataTypes.ReadNextInt(p)); return; } if (!isLogin && pType == PacketTypesIn.SpawnPlayer) { - dataTypes.ReadNextVarInt(p); - OnPlayerSpawn(dataTypes.ReadNextUUID(p)); + _dataTypes.ReadNextVarInt(p); + OnPlayerSpawn(_dataTypes.ReadNextUUID(p)); return; } - // Get client player location for calculating movement delta later if (pType == PacketTypesIn.PlayerPositionAndLook) { - double x = dataTypes.ReadNextDouble(p); - double y = dataTypes.ReadNextDouble(p); - double z = dataTypes.ReadNextDouble(p); - float yaw = dataTypes.ReadNextFloat(p); - float pitch = dataTypes.ReadNextFloat(p); - byte locMask = dataTypes.ReadNextByte(p); + double x = _dataTypes.ReadNextDouble(p); + double y = _dataTypes.ReadNextDouble(p); + double z = _dataTypes.ReadNextDouble(p); + float yaw = _dataTypes.ReadNextFloat(p); + float pitch = _dataTypes.ReadNextFloat(p); + byte locMask = _dataTypes.ReadNextByte(p); - playerLastPitch = pitch; - playerLastYaw = yaw; - if (protocolVersion >= Protocol18Handler.MC_1_8_Version) + _playerLastPitch = pitch; + _playerLastYaw = yaw; + if (_protocolVersion >= Protocol18Handler.MC_1_8_Version) { - playerLastPosition.X = (locMask & 1 << 0) != 0 ? playerLastPosition.X + x : x; - playerLastPosition.Y = (locMask & 1 << 1) != 0 ? playerLastPosition.Y + y : y; - playerLastPosition.Z = (locMask & 1 << 2) != 0 ? playerLastPosition.Z + z : z; + _playerLastPosition.X = (locMask & 1 << 0) != 0 ? _playerLastPosition.X + x : x; + _playerLastPosition.Y = (locMask & 1 << 1) != 0 ? _playerLastPosition.Y + y : y; + _playerLastPosition.Z = (locMask & 1 << 2) != 0 ? _playerLastPosition.Z + z : z; } else { - playerLastPosition.X = x; - playerLastPosition.Y = y; - playerLastPosition.Z = z; + _playerLastPosition.X = x; + _playerLastPosition.Y = y; + _playerLastPosition.Z = z; } - return; } } - /// - /// Handle outbound packet (i.e. client player movement) - /// - /// - /// - /// - private void HandleOutBoundPacket(int packetID, IEnumerable packetData, bool isLogin) + private void WriteReplayArchiveUnsafe(string replayFilePath, bool readFromActiveStream) { - var packetType = this.packetType.GetOutgoingTypeById(packetID); - if (packetType == PacketTypesOut.PlayerPosition - || packetType == PacketTypesOut.PlayerPositionAndRotation) + Directory.CreateDirectory(Path.GetDirectoryName(replayFilePath) ?? "."); + + MetaData.duration = GetCurrentDurationMillisecondsUnsafe(); + if (_playerEntityId >= 0) + MetaData.selfId = _playerEntityId; + if (_playerUuid != Guid.Empty) + MetaData.AddPlayerUUID(_playerUuid); + + MetaData.SaveToFile(); + + using FileStream replayArchiveFile = new(replayFilePath, FileMode.Create, FileAccess.Write); + using ZipArchive replayArchive = new(replayArchiveFile, ZipArchiveMode.Create); + + ZipArchiveEntry recordingEntry = replayArchive.CreateEntry(RecordingEntryName); + using (Stream recordingEntryStream = recordingEntry.Open()) { - // translate them to incoming entitymovement packet then save them + if (readFromActiveStream) + CopyActiveRecordingUnsafe(recordingEntryStream); + else + using (FileStream recordingFile = new(_recordingFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + recordingFile.CopyTo(recordingEntryStream); + } + + ZipArchiveEntry metadataEntry = replayArchive.CreateEntry(MetaData.MetaDataFileName); + using Stream metadataEntryStream = metadataEntry.Open(); + using FileStream metadataFile = new(Path.Combine(_workingDirectory, MetaData.MetaDataFileName), FileMode.Open, FileAccess.Read, FileShare.Read); + metadataFile.CopyTo(metadataEntryStream); + } + + private void CopyActiveRecordingUnsafe(Stream destination) + { + _recordStream.Flush(); + long position = _recordStream.Position; + try + { + _recordStream.Position = 0; + _recordStream.CopyTo(destination); + } + finally + { + _recordStream.Position = position; } } - private byte[] GetSpawnPlayerPacket(int entityID, Guid playerUUID, Location location, double pitch, double yaw) + private int GetCurrentDurationMillisecondsUnsafe() => + Math.Max(0, Convert.ToInt32((_lastPacketTime - _recordStartTime).TotalMilliseconds)); + + private bool HasCapturedPacketsUnsafe() => _lastPacketTime > _recordStartTime; + + private void CloseRecordStreamUnsafe() { - List packet = new(); - packet.AddRange(DataTypes.GetVarInt(entityID)); - packet.AddRange(playerUUID.ToBigEndianBytes()); - packet.AddRange(dataTypes.GetDouble(location.X)); - packet.AddRange(dataTypes.GetDouble(location.Y)); - packet.AddRange(dataTypes.GetDouble(location.Z)); - packet.Add((byte)0); - packet.Add((byte)0); - return packet.ToArray(); + if (_recordStreamClosed) + return; + + _recordStream.Flush(); + _recordStream.Dispose(); + _recordStreamClosed = true; } - #endregion + private void CleanupWorkingFilesUnsafe() + { + DeleteFileIfExists(_backupReplayPath); + DeleteFileIfExists(_recordingFilePath); + DeleteFileIfExists(Path.Combine(_workingDirectory, MetaData.MetaDataFileName)); - #region Helper method + if (Directory.Exists(_workingDirectory) && Directory.GetFileSystemEntries(_workingDirectory).Length == 0) + Directory.Delete(_workingDirectory); + } + + private void FinalizeOnProcessExit() + { + lock (_sync) + { + if (_disposed || _state != ReplayRecordingState.Recording) + return; + + try + { + _state = ReplayRecordingState.Finalizing; + CloseRecordStreamUnsafe(); + + if (HasCapturedPacketsUnsafe()) + { + string replayFileName = GetReplayDefaultName(); + WriteDebugLog("Process exit detected, finalizing replay file."); + WriteReplayArchiveUnsafe(ResolveReplayPath(replayFileName), readFromActiveStream: false); + ReplayFileName = replayFileName; + } + + _state = ReplayRecordingState.Stopped; + CleanupWorkingFilesUnsafe(); + } + catch (Exception e) + { + _state = ReplayRecordingState.Stopped; + WriteDebugLog("Exception while finalizing replay on process exit: " + e.Message + "\n" + e.StackTrace); + } + } + } + + private string ResolveReplayPath(string replayFileName) + { + ArgumentException.ThrowIfNullOrWhiteSpace(replayFileName); + + if (Path.IsPathRooted(replayFileName) || !string.IsNullOrEmpty(Path.GetDirectoryName(replayFileName))) + return replayFileName; + + return Path.Combine(ReplayFileDirectory, replayFileName); + } + + private void EnsureNotDisposed() => ObjectDisposedException.ThrowIf(_disposed, this); + + private static void DeleteFileIfExists(string path) + { + if (File.Exists(path)) + File.Delete(path); + } private static void WriteLog(string t) { @@ -402,88 +465,96 @@ namespace MinecraftClient.Protocol WriteLog(t); } - #endregion + private enum ReplayRecordingState + { + Recording, + Finalizing, + Stopped + } } /// - /// Handle MetaData used by Replay mod + /// Metadata used by Replay Mod. /// public class MetaDataHandler { - public readonly string MetaDataFileName = @"metaData.json"; - public readonly string temporaryCache = @"recording_cache"; + private static readonly JsonSerializerOptions s_jsonOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + private readonly HashSet _players = new(StringComparer.OrdinalIgnoreCase); + + public string MetaDataFileName { get; } = "metaData.json"; + public string temporaryCache { get; } public bool singlePlayer = false; public string? serverName; - public int duration = 0; // duration of the whole replay - public long date; // start time of the recording in unix timestamp milliseconds - public string mcversion = "0.0"; // e.g. 1.15.2 + public string? customServerName; + public int duration; + public long date; + public string mcversion = "0.0"; public string fileFormat = "MCPR"; - public int fileFormatVersion = 14; // 14 is what I found in metadata generated in 1.15.2 replay mod + public int fileFormatVersion = 14; public int protocol; - public string generator = "MCC"; // The program which generated the file (MCC have more popularity now :P) - public int selfId = -1; // I saw -1 in medaData file generated by Replay mod. Not sure what is this for - public List players; // Array of UUIDs of all players which can be seen in the replay + public string generator = "MCC"; + public int selfId = -1; - public MetaDataHandler() + public MetaDataHandler(string temporaryCache) { - players = new List(); + this.temporaryCache = temporaryCache; } - /// - /// Add a player's UUID who appeared in the replay - /// - /// public void AddPlayerUUID(Guid uuid) { - players.Add(uuid.ToString()); + _players.Add(uuid.ToString()); } - /// - /// Export metadata to JSON string - /// - /// JSON string public string ToJson() { - return String.Concat(new[] { "{" - , "\"singleplayer\":" , singlePlayer.ToString().ToLower() , "," - , "\"serverName\":\"" , serverName , "\"," - , "\"duration\":" , duration.ToString() , "," - , "\"date\":" , date.ToString() , "," - , "\"mcversion\":\"" , mcversion , "\"," - , "\"fileFormat\":\"" , fileFormat , "\"," - , "\"fileFormatVersion\":" , fileFormatVersion.ToString() , "," - , "\"protocol\":" , protocol.ToString() , "," - , "\"generator\":\"" , generator , "\"," - , "\"selfId\":" , selfId.ToString() + "," - , "\"player\":" , GetPlayersJsonArray() - , "}" - }); + ReplayMetaDataModel metaData = new() + { + Singleplayer = singlePlayer, + ServerName = serverName, + CustomServerName = customServerName, + Duration = duration, + Date = date, + Mcversion = mcversion, + FileFormat = fileFormat, + FileFormatVersion = fileFormatVersion, + Protocol = protocol, + Generator = generator, + SelfId = selfId, + Players = [.. _players] + }; + + return JsonSerializer.Serialize(metaData, s_jsonOptions); } - /// - /// Save metadata to disk file - /// public void SaveToFile() { + Directory.CreateDirectory(temporaryCache); File.WriteAllText(Path.Combine(temporaryCache, MetaDataFileName), ToJson()); } - /// - /// Get players UUID JSON array string - /// - /// - private string GetPlayersJsonArray() + private sealed class ReplayMetaDataModel { - if (players.Count == 0) - return "[]"; + public bool Singleplayer { get; init; } + public string? ServerName { get; init; } + public string? CustomServerName { get; init; } + public int Duration { get; init; } + public long Date { get; init; } - // Place between brackets the comma-separated list of player names placed between quotes - return String.Format("[{0}]", - String.Join(",", - players.Select(player => String.Format("\"{0}\"", player)) - ) - ); + [JsonPropertyName("mcversion")] + public string Mcversion { get; init; } = "0.0"; + + public string FileFormat { get; init; } = "MCPR"; + public int FileFormatVersion { get; init; } + public int Protocol { get; init; } + public string Generator { get; init; } = "MCC"; + public int SelfId { get; init; } = -1; + public string[] Players { get; init; } = []; } } } diff --git a/MinecraftClient/Protocol/ServerStatusDisplay.cs b/MinecraftClient/Protocol/ServerStatusDisplay.cs new file mode 100644 index 00000000..291745e5 --- /dev/null +++ b/MinecraftClient/Protocol/ServerStatusDisplay.cs @@ -0,0 +1,119 @@ +using System; +using System.Text; +using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; + +namespace MinecraftClient.Protocol +{ + internal static class ServerStatusDisplay + { + private const int MaxSamplePlayers = 10; + + internal static void Show(ServerStatusInfo info) + { + if (ConsoleIO.Backend is Tui.TuiConsoleBackend tuiBackend) + ShowTui(info, tuiBackend); + else + ShowClassic(info); + } + + private static void ShowClassic(ServerStatusInfo info) + { + var sb = new StringBuilder(); + + sb.AppendLine(); + sb.Append("§8§m"); + sb.Append(new string('-', 50)); + sb.AppendLine("§r"); + + if (!string.IsNullOrEmpty(info.MotdRaw)) + { + try + { + sb.AppendLine(ChatParser.ParseText(info.MotdRaw)); + } + catch + { + sb.AppendLine(info.MotdRaw); + } + } + + sb.Append("§f"); + sb.Append(Translations.mcc_server_info_label_server); + sb.Append(" §b"); + sb.Append(info.Host); + sb.Append("§7:§b"); + sb.AppendLine(info.Port.ToString()); + + sb.Append("§f"); + sb.Append(Translations.mcc_server_info_label_version); + sb.Append(" §b"); + sb.Append(ChatBot.GetVerbatim(info.VersionName)); + sb.Append(" §7("); + sb.Append(string.Format(Translations.mcc_server_info_label_protocol, "§e" + info.ProtocolVersion + "§7")); + sb.AppendLine(")"); + + if (info.ResolvedProtocol != 0) + { + string resolvedMcVer = ProtocolHandler.ProtocolVersion2MCVer(info.ResolvedProtocol); + sb.Append("§f"); + sb.Append(Translations.mcc_server_info_label_connecting_as); + sb.Append(" §a"); + sb.Append(resolvedMcVer); + sb.Append(" §7("); + sb.Append(string.Format(Translations.mcc_server_info_label_protocol, "§a" + info.ResolvedProtocol + "§7")); + sb.AppendLine(")"); + } + + if (info.PingMs >= 0) + { + sb.Append("§f"); + sb.Append(Translations.mcc_server_info_label_ping); + sb.Append(" §a"); + sb.AppendLine(string.Format(Translations.mcc_server_info_label_ping_ms, info.PingMs)); + } + + sb.Append("§f"); + sb.Append(Translations.mcc_server_info_label_players); + sb.Append(" §a"); + sb.Append(info.OnlinePlayers); + sb.Append("§7/§c"); + sb.AppendLine(info.MaxPlayers.ToString()); + + if (info.SamplePlayers.Count > 0) + { + sb.Append("§f"); + sb.AppendLine(Translations.mcc_server_info_label_online); + + int shown = Math.Min(info.SamplePlayers.Count, MaxSamplePlayers); + for (int i = 0; i < shown; i++) + sb.AppendLine($" §a{info.SamplePlayers[i].Name}"); + + if (info.SamplePlayers.Count > shown) + sb.AppendLine($" §7{string.Format(Translations.mcc_server_info_sample_more, info.SamplePlayers.Count - shown)}"); + } + + sb.Append("§8§m"); + sb.Append(new string('-', 50)); + sb.Append("§r"); + + ConsoleIO.WriteLineFormatted(sb.ToString(), acceptnewlines: true); + } + + private static void ShowTui(ServerStatusInfo info, Tui.TuiConsoleBackend backend) + { + var view = backend.GetView(); + if (view is null) + { + ShowClassic(info); + return; + } + + Avalonia.Threading.Dispatcher.UIThread.Post(() => + { + var panel = Tui.ServerStatusPanelBuilder.Build(info); + view.AppendControlToLog(panel); + }); + } + } +} diff --git a/MinecraftClient/Protocol/ServerStatusInfo.cs b/MinecraftClient/Protocol/ServerStatusInfo.cs new file mode 100644 index 00000000..b864e64d --- /dev/null +++ b/MinecraftClient/Protocol/ServerStatusInfo.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient.Protocol +{ + /// + /// Holds the structured result of a Minecraft server status (SLP) ping, + /// including MOTD, player counts, sample player list, version, and favicon. + /// + public sealed class ServerStatusInfo + { + public string Host { get; init; } = string.Empty; + public int Port { get; init; } + public string VersionName { get; init; } = string.Empty; + public int ProtocolVersion { get; init; } + public int ResolvedProtocol { get; set; } + public int OnlinePlayers { get; init; } + public int MaxPlayers { get; init; } + public List SamplePlayers { get; init; } = []; + public string MotdRaw { get; init; } = string.Empty; + public string? FaviconBase64 { get; init; } + public long PingMs { get; init; } + + public sealed class SamplePlayer + { + public string Name { get; init; } = string.Empty; + public string Id { get; init; } = string.Empty; + } + } +} diff --git a/MinecraftClient/Protocol/Session/SessionCache.cs b/MinecraftClient/Protocol/Session/SessionCache.cs index 956f6ab2..f3d00df9 100644 --- a/MinecraftClient/Protocol/Session/SessionCache.cs +++ b/MinecraftClient/Protocol/Session/SessionCache.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; using System.Timers; +using MessagePack; using static MinecraftClient.Settings; using static MinecraftClient.Settings.MainConfigHelper.MainConfig.AdvancedConfig; @@ -14,7 +13,6 @@ namespace MinecraftClient.Protocol.Session /// public static class SessionCache { - private const string SessionCacheFilePlaintext = "SessionCache.ini"; private const string SessionCacheFileSerialized = "SessionCache.db"; private static readonly string SessionCacheFileMinecraft = String.Concat( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @@ -28,7 +26,6 @@ namespace MinecraftClient.Protocol.Session private static readonly Dictionary sessions = new(); private static readonly Timer updatetimer = new(100); private static readonly List> pendingadds = new(); - private static readonly BinaryFormatter formatter = new(); /// /// Retrieve whether SessionCache contains a session for the given login. @@ -82,7 +79,7 @@ namespace MinecraftClient.Protocol.Session /// TRUE if session tokens are seeded from file public static bool InitializeDiskCache() { - cachemonitor = new FileMonitor(AppDomain.CurrentDomain.BaseDirectory, SessionCacheFilePlaintext, new FileSystemEventHandler(OnChanged)); + cachemonitor = new FileMonitor(AppDomain.CurrentDomain.BaseDirectory, SessionCacheFileSerialized, new FileSystemEventHandler(OnChanged)); updatetimer.Elapsed += HandlePending; return LoadFromDisk(); } @@ -121,40 +118,41 @@ namespace MinecraftClient.Protocol.Session /// True if data is successfully loaded private static bool LoadFromDisk() { - //Grab sessions in the Minecraft directory + // Grab sessions in the Minecraft directory if (File.Exists(SessionCacheFileMinecraft)) { if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading, Path.GetFileName(SessionCacheFileMinecraft))); - Json.JSONData mcSession = new(Json.JSONData.DataType.String); + System.Text.Json.Nodes.JsonNode? mcSession = null; try { mcSession = Json.ParseJson(File.ReadAllText(SessionCacheFileMinecraft)); } catch (IOException) { /* Failed to read file from disk -- ignoring */ } - if (mcSession.Type == Json.JSONData.DataType.Object - && mcSession.Properties.ContainsKey("clientToken") - && mcSession.Properties.ContainsKey("authenticationDatabase")) + if (mcSession is System.Text.Json.Nodes.JsonObject mcSessionObj + && mcSessionObj.ContainsKey("clientToken") + && mcSessionObj.ContainsKey("authenticationDatabase")) { - string clientID = mcSession.Properties["clientToken"].StringValue.Replace("-", ""); - Dictionary sessionItems = mcSession.Properties["authenticationDatabase"].Properties; - foreach (string key in sessionItems.Keys) + string clientID = mcSession["clientToken"]!.GetStringValue().Replace("-", ""); + var sessionItems = mcSession["authenticationDatabase"]!.AsObject(); + foreach (var kvp in sessionItems) { + string key = kvp.Key; if (Guid.TryParseExact(key, "N", out Guid temp)) { - Dictionary sessionItem = sessionItems[key].Properties; + var sessionItem = kvp.Value!.AsObject(); if (sessionItem.ContainsKey("displayName") && sessionItem.ContainsKey("accessToken") && sessionItem.ContainsKey("username") && sessionItem.ContainsKey("uuid")) { - string login = Settings.ToLowerIfNeed(sessionItem["username"].StringValue); + string login = Settings.ToLowerIfNeed(sessionItem["username"]!.GetStringValue()); try { SessionToken session = SessionToken.FromString(String.Join(",", - sessionItem["accessToken"].StringValue, - sessionItem["displayName"].StringValue, - sessionItem["uuid"].StringValue.Replace("-", ""), + sessionItem["accessToken"]!.GetStringValue(), + sessionItem["displayName"]!.GetStringValue(), + sessionItem["uuid"]!.GetStringValue().Replace("-", ""), clientID )); if (Config.Logging.DebugMessages) @@ -168,7 +166,7 @@ namespace MinecraftClient.Protocol.Session } } - //Serialized session cache file in binary format + // Serialized session cache file in binary format if (File.Exists(SessionCacheFileSerialized)) { if (Config.Logging.DebugMessages) @@ -177,10 +175,8 @@ namespace MinecraftClient.Protocol.Session try { using FileStream fs = new(SessionCacheFileSerialized, FileMode.Open, FileAccess.Read, FileShare.Read); -#pragma warning disable SYSLIB0011 // BinaryFormatter.Deserialize() is obsolete - // Possible risk of information disclosure or remote code execution. The impact of this vulnerability is limited to the user side only. - Dictionary sessionsTemp = (Dictionary)formatter.Deserialize(fs); -#pragma warning restore SYSLIB0011 // BinaryFormatter.Deserialize() is obsolete + // Deserialize using MessagePack + Dictionary sessionsTemp = MessagePackSerializer.Deserialize>(fs); foreach (KeyValuePair item in sessionsTemp) { if (Config.Logging.DebugMessages) @@ -192,54 +188,12 @@ namespace MinecraftClient.Protocol.Session { ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail, ex.Message)); } - catch (SerializationException ex2) + catch (MessagePackSerializationException ex2) { ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_malformed, ex2.Message)); } } - //User-editable session cache file in text format - if (File.Exists(SessionCacheFilePlaintext)) - { - if (Config.Logging.DebugMessages) - ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading_session, SessionCacheFilePlaintext)); - - try - { - foreach (string line in FileMonitor.ReadAllLinesWithRetries(SessionCacheFilePlaintext)) - { - if (!line.Trim().StartsWith("#")) - { - string[] keyValue = line.Split('='); - if (keyValue.Length == 2) - { - try - { - string login = Settings.ToLowerIfNeed(keyValue[0]); - SessionToken session = SessionToken.FromString(keyValue[1]); - if (Config.Logging.DebugMessages) - ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded, login, session.ID)); - sessions[login] = session; - } - catch (InvalidDataException e) - { - if (Config.Logging.DebugMessages) - ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string, keyValue[1], e.Message)); - } - } - else if (Config.Logging.DebugMessages) - { - ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_line, line)); - } - } - } - } - catch (IOException e) - { - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail_plain, e.Message)); - } - } - return sessions.Count > 0; } @@ -251,17 +205,11 @@ namespace MinecraftClient.Protocol.Session if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + Translations.cache_saving, acceptnewlines: true); - List sessionCacheLines = new() - { - "# Generated by MCC v" + Program.Version + " - Keep it secret & Edit at own risk!", - "# Login=SessionID,PlayerName,UUID,ClientID,RefreshToken,ServerIDhash,ServerPublicKey" - }; - foreach (KeyValuePair entry in sessions) - sessionCacheLines.Add(entry.Key + '=' + entry.Value.ToString()); - try { - FileMonitor.WriteAllLinesWithRetries(SessionCacheFilePlaintext, sessionCacheLines); + using FileStream fs = new(SessionCacheFileSerialized, FileMode.Create, FileAccess.Write, FileShare.None); + // Serialize using MessagePack + MessagePackSerializer.Serialize(fs, sessions); } catch (IOException e) { diff --git a/MinecraftClient/Protocol/Session/SessionToken.cs b/MinecraftClient/Protocol/Session/SessionToken.cs index 812d3fa0..a5d9f35c 100644 --- a/MinecraftClient/Protocol/Session/SessionToken.cs +++ b/MinecraftClient/Protocol/Session/SessionToken.cs @@ -2,24 +2,34 @@ using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; +using MessagePack; using MinecraftClient.Scripting; using static MinecraftClient.Settings.MainConfigHelper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol.Session { [Serializable] + [MessagePackObject] public class SessionToken { private static readonly Regex JwtRegex = new("^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"); + [Key(0)] public string ID { get; set; } + [Key(1)] public string PlayerName { get; set; } + [Key(2)] public string PlayerID { get; set; } + [Key(3)] public string ClientID { get; set; } + [Key(4)] public string RefreshToken { get; set; } + [Key(5)] public string ServerIDhash { get; set; } + [Key(6)] public byte[]? ServerPublicKey { get; set; } + [IgnoreMember] public Task? SessionPreCheckTask = null; public SessionToken() @@ -35,7 +45,7 @@ namespace MinecraftClient.Protocol.Session public bool SessionPreCheck(LoginType type) { - if (ID == string.Empty || PlayerID == String.Empty || ServerPublicKey == null) + if (ID == string.Empty || PlayerID == String.Empty || ServerPublicKey is null) return false; Crypto.CryptoHandler.ClientAESPrivateKey ??= Crypto.CryptoHandler.GenerateAESPrivateKey(); string serverHash = Crypto.CryptoHandler.GetServerHash(ServerIDhash, ServerPublicKey, Crypto.CryptoHandler.ClientAESPrivateKey); @@ -47,7 +57,7 @@ namespace MinecraftClient.Protocol.Session public override string ToString() { return String.Join(",", ID, PlayerName, PlayerID, ClientID, RefreshToken, ServerIDhash, - (ServerPublicKey == null) ? String.Empty : Convert.ToBase64String(ServerPublicKey)); + (ServerPublicKey is null) ? String.Empty : Convert.ToBase64String(ServerPublicKey)); } public static SessionToken FromString(string tokenString) diff --git a/MinecraftClient/RecipeBookRecipeEntry.cs b/MinecraftClient/RecipeBookRecipeEntry.cs new file mode 100644 index 00000000..a5648ba7 --- /dev/null +++ b/MinecraftClient/RecipeBookRecipeEntry.cs @@ -0,0 +1,4 @@ +namespace MinecraftClient +{ + public readonly record struct RecipeBookRecipeEntry(string CommandId, string DisplayText); +} diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index 65bb9a0b..cdd5d4b1 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1,2015 +1,2288 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MinecraftClient { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class ConfigComments { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal ConfigComments() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.Resources.ConfigComments.ConfigComments", typeof(ConfigComments).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to can be used in some other fields as %yourvar% - ///%username% and %serverip% are reserved variables.. - /// - internal static string AppVars_Variables { - get { - return ResourceManager.GetString("AppVars.Variables", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to =============================== # - /// Minecraft Console Client Bots # - ///=============================== #. - /// - internal static string ChatBot { - get { - return ResourceManager.GetString("ChatBot", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get alerted when specified words are detected in chat - ///Useful for moderating your server or detecting when someone is talking to you. - /// - internal static string ChatBot_Alerts { - get { - return ResourceManager.GetString("ChatBot.Alerts", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. - /// - internal static string ChatBot_Alerts_Beep_Enabled { - get { - return ResourceManager.GetString("ChatBot.Alerts.Beep_Enabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List of words/strings to NOT alert you on.. - /// - internal static string ChatBot_Alerts_Excludes { - get { - return ResourceManager.GetString("ChatBot.Alerts.Excludes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The name of a file where alers logs will be written.. - /// - internal static string ChatBot_Alerts_Log_File { - get { - return ResourceManager.GetString("ChatBot.Alerts.Log_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log alerts info a file.. - /// - internal static string ChatBot_Alerts_Log_To_File { - get { - return ResourceManager.GetString("ChatBot.Alerts.Log_To_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List of words/strings to alert you on.. - /// - internal static string ChatBot_Alerts_Matches { - get { - return ResourceManager.GetString("ChatBot.Alerts.Matches", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. - /// - internal static string ChatBot_Alerts_Trigger_By_Rain { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Rain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. - /// - internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Thunderstorm", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. - /// - internal static string ChatBot_Alerts_Trigger_By_Words { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Words", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection - /// /!\ Make sure your server rules do not forbid anti-AFK mechanisms! - /// /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5). - /// - internal static string ChatBot_AntiAfk { - get { - return ResourceManager.GetString("ChatBot.AntiAfk", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Command to send to the server.. - /// - internal static string ChatBot_AntiAfk_Command { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Command", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The time interval for execution. (in seconds). - /// - internal static string ChatBot_AntiAfk_Delay { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sneak when sending the command.. - /// - internal static string ChatBot_AntiAfk_Use_Sneak { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Use_Sneak", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. - /// - internal static string ChatBot_AntiAfk_Use_Terrain_Handling { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Use_Terrain_Handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). - /// - internal static string ChatBot_AntiAfk_Walk_Range { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Range", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. - /// - internal static string ChatBot_AntiAfk_Walk_Retries { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Retries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically attack hostile mobs around you - ///You need to enable Entity Handling to use this bot - /// /!\ Make sure server rules allow your planned use of AutoAttack - /// /!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES!. - /// - internal static string ChatBot_AutoAttack { - get { - return ResourceManager.GetString("ChatBot.AutoAttack", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allow attacking hostile mobs.. - /// - internal static string ChatBot_AutoAttack_Attack_Hostile { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Hostile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allow attacking passive mobs.. - /// - internal static string ChatBot_AutoAttack_Attack_Passive { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Passive", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Capped between 1 to 4. - /// - internal static string ChatBot_AutoAttack_Attack_Range { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Range", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. - /// - internal static string ChatBot_AutoAttack_Cooldown_Time { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Cooldown_Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. - /// - internal static string ChatBot_AutoAttack_Entites_List { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Entites_List", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. - /// - internal static string ChatBot_AutoAttack_Interaction { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Interaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. - /// - internal static string ChatBot_AutoAttack_List_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.List_Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. - /// - internal static string ChatBot_AutoAttack_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. - /// - internal static string ChatBot_AutoAttack_Priority { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Priority", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically craft items in your inventory - ///See https://mccteam.github.io/g/bots/#auto-craft for how to use - ///You need to enable Inventory Handling to use this bot - ///You should also enable Terrain and Movements if you need to use a crafting table. - /// - internal static string ChatBot_AutoCraft { - get { - return ResourceManager.GetString("ChatBot.AutoCraft", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. - /// - internal static string ChatBot_AutoCraft_CraftingTable { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.CraftingTable", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. - /// - internal static string ChatBot_AutoCraft_OnFailure { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.OnFailure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. - ///Recipes.Type: crafting table type: "player" or "table" - ///Recipes.Result: the resulting item - ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. - ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. - /// - internal static string ChatBot_AutoCraft_Recipes { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.Recipes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Auto-digging blocks. - ///You need to enable Terrain Handling to use this bot - ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. - ///Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead. - ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. - /// - internal static string ChatBot_AutoDig { - get { - return ResourceManager.GetString("ChatBot.AutoDig", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. - /// - internal static string ChatBot_AutoDig_Auto_Start_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Auto_Start_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically switch to the appropriate tool.. - /// - internal static string ChatBot_AutoDig_Auto_Tool_Switch { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Auto_Tool_Switch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. - /// - internal static string ChatBot_AutoDig_Dig_Timeout { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Dig_Timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. - /// - internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Drop_Low_Durability_Tools", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. - /// - internal static string ChatBot_AutoDig_Durability_Limit { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Durability_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. - /// - internal static string ChatBot_AutoDig_List_Type { - get { - return ResourceManager.GetString("ChatBot.AutoDig.List_Type", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. - /// - internal static string ChatBot_AutoDig_Location_Order { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Location_Order", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. - /// - internal static string ChatBot_AutoDig_Locations { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Locations", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to output logs when digging blocks.. - /// - internal static string ChatBot_AutoDig_Log_Block_Dig { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Log_Block_Dig", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "lookat", "fixedpos" or "both". Digging the block being looked at, the block in a fixed position, or the block that needs to be all met.. - /// - internal static string ChatBot_AutoDig_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically drop items in inventory - ///You need to enable Inventory Handling to use this bot - ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. - /// - internal static string ChatBot_AutoDrop { - get { - return ResourceManager.GetString("ChatBot.AutoDrop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. - /// - internal static string ChatBot_AutoDrop_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoDrop.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically eat food when your Hunger value is low - ///You need to enable Inventory Handling to use this bot. - /// - internal static string ChatBot_AutoEat { - get { - return ResourceManager.GetString("ChatBot.AutoEat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically catch fish using a fishing rod - ///Guide: https://mccteam.github.io/g/bots/#auto-fishing - ///You can use "/fish" to control the bot manually. - /// /!\ Make sure server rules allow automated farming before using this bot. - /// - internal static string ChatBot_AutoFishing { - get { - return ResourceManager.GetString("ChatBot.AutoFishing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Keep it as false if you have not changed it before.. - /// - internal static string ChatBot_AutoFishing_Antidespawn { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Antidespawn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. - /// - internal static string ChatBot_AutoFishing_Auto_Rod_Switch { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Rod_Switch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. - /// - internal static string ChatBot_AutoFishing_Auto_Start { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Start", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How soon to re-cast after successful fishing.. - /// - internal static string ChatBot_AutoFishing_Cast_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Cast_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. - /// - internal static string ChatBot_AutoFishing_Durability_Limit { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Durability_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. - /// - internal static string ChatBot_AutoFishing_Enable_Move { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Enable_Move", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. - /// - internal static string ChatBot_AutoFishing_Fishing_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. - /// - internal static string ChatBot_AutoFishing_Fishing_Timeout { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. - /// - internal static string ChatBot_AutoFishing_Hook_Threshold { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Hook_Threshold", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Used to adjust the above two thresholds, which when enabled will print the change in the position of the fishhook entity upon receipt of its movement packet.. - /// - internal static string ChatBot_AutoFishing_Log_Fish_Bobber { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Log_Fish_Bobber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. - /// - internal static string ChatBot_AutoFishing_Mainhand { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Mainhand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It will move in order "1->2->3->4->3->2->1->2->..." and can change position or facing or both each time. It is recommended to change the facing only.. - /// - internal static string ChatBot_AutoFishing_Movements { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Movements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. - /// - internal static string ChatBot_AutoFishing_Stationary_Threshold { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Stationary_Threshold", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating - /// /!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. - /// - internal static string ChatBot_AutoRelog { - get { - return ResourceManager.GetString("ChatBot.AutoRelog", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The delay time before joining the server. (in seconds). - /// - internal static string ChatBot_AutoRelog_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. - /// - internal static string ChatBot_AutoRelog_Ignore_Kick_Message { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Ignore_Kick_Message", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. - /// - internal static string ChatBot_AutoRelog_Kick_Messages { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Kick_Messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. - /// - internal static string ChatBot_AutoRelog_Retries { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Retries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Run commands or send messages automatically when a specified pattern is detected in chat - ///Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules - /// /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam. - /// - internal static string ChatBot_AutoRespond { - get { - return ResourceManager.GetString("ChatBot.AutoRespond", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). - /// - internal static string ChatBot_AutoRespond_Match_Colors { - get { - return ResourceManager.GetString("ChatBot.AutoRespond.Match_Colors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logs chat messages in a file on disk.. - /// - internal static string ChatBot_ChatLog { - get { - return ResourceManager.GetString("ChatBot.ChatLog", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. - ///For Setup you can either use the documentation or read here (Documentation has images). - ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge - ///Setup: - ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . - /// /!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. - /// - internal static string ChatBot_DiscordBridge { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. - /// - internal static string ChatBot_DiscordBridge_ChannelId { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.ChannelId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message formats - ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! - ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. - /// - internal static string ChatBot_DiscordBridge_Formats { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.Formats", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. - /// - internal static string ChatBot_DiscordBridge_GuildId { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.GuildId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. - /// - internal static string ChatBot_DiscordBridge_MessageSendTimeout { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.MessageSendTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. - /// - internal static string ChatBot_DiscordBridge_OwnersIds { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.OwnersIds", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Your Discord Bot token.. - /// - internal static string ChatBot_DiscordBridge_Token { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.Token", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them). - ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. - ///Usage: "/farmer start" command and "/farmer stop" command. - ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. - ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. - /// - internal static string ChatBot_Farmer { - get { - return ResourceManager.GetString("ChatBot.Farmer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). - /// - internal static string ChatBot_Farmer_Delay_Between_Tasks { - get { - return ResourceManager.GetString("ChatBot.Farmer.Delay_Between_Tasks", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enabled you to make the bot follow you - ///NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you - ///It's similar to making animals follow you when you're holding food in your hand. - ///This is due to a slow pathfinding algorithm, we're working on getting a better one - ///You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, /// [rest of string was truncated]";. - /// - internal static string ChatBot_FollowPlayer { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not follow the player if he is in the range of 3 blocks (prevents the bot from pushing a player in an infinite loop). - /// - internal static string ChatBot_FollowPlayer_Stop_At_Distance { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer.Stop_At_Distance", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). - /// - internal static string ChatBot_FollowPlayer_Update_Limit { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer.Update_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time. - ///You need to have ChatFormat working correctly and add yourself in botowners to start the game with /tell <bot username> start - /// /!\ This bot may get a bit spammy if many players are interacting with it. - /// - internal static string ChatBot_HangmanGame { - get { - return ResourceManager.GetString("ChatBot.HangmanGame", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A Chat Bot that collects items on the ground. - /// - internal static string ChatBot_ItemsCollector { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If set to true, the bot will return to it's starting position after there are no items to collect. - /// - internal static string ChatBot_ItemsCollector_Always_Return_To_Start { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Always_Return_To_Start", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If set to true, the bot will collect all items, regardless of their type. If you want to use the whitelisted item types, disable this by setting it to false. - /// - internal static string ChatBot_ItemsCollector_Collect_All_Item_Types { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Collect_All_Item_Types", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The radius in which bot will look for items to collect (Default: 30). - /// - internal static string ChatBot_ItemsCollector_Collection_Radius { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Collection_Radius", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delay in milliseconds between bot scanning items (Recommended: 300-500). - /// - internal static string ChatBot_ItemsCollector_Delay_Between_Tasks { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Delay_Between_Tasks", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to In this list you can specify which items the bot will collect. To enable this, set the Collect_All_Item_Types to false. (NOTE: This does not prevent the bot from accidentally picking up other items, it only goes to positions where it finds the whitelisted items)\nYou can see the list of item types here: https://raw.githubusercontent.com/MCCTeam/Minecraft-Console-Client/master/MinecraftClient/Inventory/ItemType.cs. - /// - internal static string ChatBot_ItemsCollector_Items_Whitelist { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Items_Whitelist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If set to true, the bot will go after clustered items instead for the closest ones. - /// - internal static string ChatBot_ItemsCollector_Prioritize_Clusters { - get { - return ResourceManager.GetString("ChatBot.ItemsCollector.Prioritize_Clusters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Relay messages between players and servers, like a mail plugin - ///This bot can store messages when the recipients are offline, and send them when they join the server - /// /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins. - /// - internal static string ChatBot_Mailer { - get { - return ResourceManager.GetString("ChatBot.Mailer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) - ///This is useful for solving captchas which use maps - ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. - ///NOTE: - ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. - /// /!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. - /// - internal static string ChatBot_Map { - get { - return ResourceManager.GetString("ChatBot.Map", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. - /// - internal static string ChatBot_Map_Auto_Render_On_Update { - get { - return ResourceManager.GetString("ChatBot.Map.Auto_Render_On_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. - /// - internal static string ChatBot_Map_Delete_All_On_Unload { - get { - return ResourceManager.GetString("ChatBot.Map.Delete_All_On_Unload", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. - /// - internal static string ChatBot_Map_Notify_On_First_Update { - get { - return ResourceManager.GetString("ChatBot.Map.Notify_On_First_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. - /// - internal static string ChatBot_Map_Rasize_Rendered_Image { - get { - return ResourceManager.GetString("ChatBot.Map.Rasize_Rendered_Image", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to render the map in the console.. - /// - internal static string ChatBot_Map_Render_In_Console { - get { - return ResourceManager.GetString("ChatBot.Map.Render_In_Console", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. - /// - internal static string ChatBot_Map_Resize_To { - get { - return ResourceManager.GetString("ChatBot.Map.Resize_To", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. - /// - internal static string ChatBot_Map_Save_To_File { - get { - return ResourceManager.GetString("ChatBot.Map.Save_To_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) - ///You need to enable Save_To_File in order for this to work. - ///We also recommend turning on resizing.. - /// - internal static string ChatBot_Map_Send_Rendered_To_Bridges { - get { - return ResourceManager.GetString("ChatBot.Map.Send_Rendered_To_Bridges", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log the list of players periodically into a textual file.. - /// - internal static string ChatBot_PlayerListLogger { - get { - return ResourceManager.GetString("ChatBot.PlayerListLogger", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (In seconds). - /// - internal static string ChatBot_PlayerListLogger_Delay { - get { - return ResourceManager.GetString("ChatBot.PlayerListLogger.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Send MCC console commands to your bot through server PMs (/tell) - ///You need to have ChatFormat working correctly and add yourself in botowners to use the bot - /// /!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins. - /// - internal static string ChatBot_RemoteControl { - get { - return ResourceManager.GetString("ChatBot.RemoteControl", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/) - ///Please note that due to technical limitations, the client player (you) will not be shown in the replay file - /// /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT!. - /// - internal static string ChatBot_ReplayCapture { - get { - return ResourceManager.GetString("ChatBot.ReplayCapture", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. - /// - internal static string ChatBot_ReplayCapture_Backup_Interval { - get { - return ResourceManager.GetString("ChatBot.ReplayCapture.Backup_Interval", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval - ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. - /// - internal static string ChatBot_ScriptScheduler { - get { - return ResourceManager.GetString("ChatBot.ScriptScheduler", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. - /// /!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. - ///----------------------------------------------------------- - ///Setup: - ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather - ///Click on "Start" button and re [rest of string was truncated]";. - /// - internal static string ChatBot_TelegramBridge { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. - /// - internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Authorized_Chat_Ids", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. - /// - internal static string ChatBot_TelegramBridge_ChannelId { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.ChannelId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message formats - ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! - ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. - /// - internal static string ChatBot_TelegramBridge_Formats { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Formats", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. - /// - internal static string ChatBot_TelegramBridge_MessageSendTimeout { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.MessageSendTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Your Telegram Bot token.. - /// - internal static string ChatBot_TelegramBridge_Token { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Token", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Remotely control the client using Web Sockets.\n# This is useful if you want to implement an application that can remotely and asynchronously execute procedures in MCC.\n# Example implementation written in JavaScript: https://github.com/milutinke/MCC.js.git\n# The protocol specification will be available in the documentation soon.. - /// - internal static string ChatBot_WebSocketBot { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allow IP aliases, such as "localhost" or if using containers then the container name can be used.... - /// - internal static string ChatBot_WebSocketBot_AllowIpAlias { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot.AllowIpAlias", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions.. - /// - internal static string ChatBot_WebSocketBot_DebugMode { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot.DebugMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The IP address that Websocket server will be bound to.. - /// - internal static string ChatBot_WebSocketBot_Ip { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot.Ip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A password that will be used to authenticate on thw Websocket server (It is recommended to change the default password and to set a strong one).. - /// - internal static string ChatBot_WebSocketBot_Password { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot.Password", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The Port that Websocket server will be bounded to.. - /// - internal static string ChatBot_WebSocketBot_Port { - get { - return ResourceManager.GetString("ChatBot.WebSocketBot.Port", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MCC does it best to detect chat messages, but some server have unusual chat formats - ///When this happens, you'll need to configure chat format below, see https://mccteam.github.io/g/conf/#chat-format-section. - /// - internal static string ChatFormat { - get { - return ResourceManager.GetString("ChatFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. - /// - internal static string ChatFormat_Builtins { - get { - return ResourceManager.GetString("ChatFormat.Builtins", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. - /// - internal static string ChatFormat_UserDefined { - get { - return ResourceManager.GetString("ChatFormat.UserDefined", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Console-related settings.. - /// - internal static string Console { - get { - return ResourceManager.GetString("Console", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The settings for command completion suggestions. - ///Custom colors are only available when using "vt100_24bit" color mode.. - /// - internal static string Console_CommandSuggestion { - get { - return ResourceManager.GetString("Console.CommandSuggestion", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to display command suggestions in the console.. - /// - internal static string Console_CommandSuggestion_Enable { - get { - return ResourceManager.GetString("Console.CommandSuggestion.Enable", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable this option if the arrows in the command suggestions are not displayed properly in your terminal.. - /// - internal static string Console_CommandSuggestion_Use_Basic_Arrow { - get { - return ResourceManager.GetString("Console.CommandSuggestion.Use_Basic_Arrow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "disable", "legacy_4bit", "vt100_4bit", "vt100_8bit" or "vt100_24bit". If a garbled code like "←[0m" appears on the terminal, you can try switching to "legacy_4bit" mode, or just disable it.. - /// - internal static string Console_General_ConsoleColorMode { - get { - return ResourceManager.GetString("Console.General.ConsoleColorMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You can use "Ctrl+P" to print out the current input and cursor position.. - /// - internal static string Console_General_Display_Input { - get { - return ResourceManager.GetString("Console.General.Display_Input", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Startup Config File - ///Please do not record extraneous data in this file as it will be overwritten by MCC. - /// - ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html - ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. - /// - internal static string Head { - get { - return ResourceManager.GetString("Head", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This setting affects only the messages in the console.. - /// - internal static string Logging { - get { - return ResourceManager.GetString("Logging", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Regex for filtering chat message.. - /// - internal static string Logging_ChatFilter { - get { - return ResourceManager.GetString("Logging.ChatFilter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show server chat messages.. - /// - internal static string Logging_ChatMessages { - get { - return ResourceManager.GetString("Logging.ChatMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Regex for filtering debug message.. - /// - internal static string Logging_DebugFilter { - get { - return ResourceManager.GetString("Logging.DebugFilter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. - /// - internal static string Logging_DebugMessages { - get { - return ResourceManager.GetString("Logging.DebugMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show error messages.. - /// - internal static string Logging_ErrorMessages { - get { - return ResourceManager.GetString("Logging.ErrorMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. - /// - internal static string Logging_FilterMode { - get { - return ResourceManager.GetString("Logging.FilterMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). - /// - internal static string Logging_InfoMessages { - get { - return ResourceManager.GetString("Logging.InfoMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log file name.. - /// - internal static string Logging_LogFile { - get { - return ResourceManager.GetString("Logging.LogFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Write log messages to file.. - /// - internal static string Logging_LogToFile { - get { - return ResourceManager.GetString("Logging.LogToFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prepend timestamp to messages in log file.. - /// - internal static string Logging_PrependTimestamp { - get { - return ResourceManager.GetString("Logging.PrependTimestamp", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). - /// - internal static string Logging_SaveColorCodes { - get { - return ResourceManager.GetString("Logging.SaveColorCodes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show warning messages.. - /// - internal static string Logging_WarningMessages { - get { - return ResourceManager.GetString("Logging.WarningMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. - /// - internal static string Main_Advanced { - get { - return ResourceManager.GetString("Main.Advanced", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials - ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". - /// - internal static string Main_Advanced_account_list { - get { - return ResourceManager.GetString("Main.Advanced.account_list", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. - /// - internal static string Main_Advanced_auto_respawn { - get { - return ResourceManager.GetString("Main.Advanced.auto_respawn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. - /// - internal static string Main_Advanced_bot_owners { - get { - return ResourceManager.GetString("Main.Advanced.bot_owners", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. - /// - internal static string Main_Advanced_brand_info { - get { - return ResourceManager.GetString("Main.Advanced.brand_info", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Leave empty for no logfile.. - /// - internal static string Main_Advanced_chatbot_log_file { - get { - return ResourceManager.GetString("Main.Advanced.chatbot_log_file", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. - /// - internal static string Main_Advanced_enable_emoji { - get { - return ResourceManager.GetString("Main.Advanced.enable_emoji", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set to false to opt-out of Sentry error logging.. - /// - internal static string Main_Advanced_enable_sentry { - get { - return ResourceManager.GetString("Main.Advanced.enable_sentry", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle entity handling.. - /// - internal static string Main_Advanced_entity_handling { - get { - return ResourceManager.GetString("Main.Advanced.entity_handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. - /// - internal static string Main_Advanced_exit_on_failure { - get { - return ResourceManager.GetString("Main.Advanced.exit_on_failure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ignore invalid player name. - /// - internal static string Main_Advanced_ignore_invalid_playername { - get { - return ResourceManager.GetString("Main.Advanced.ignore_invalid_playername", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. - /// - internal static string Main_Advanced_internal_cmd_char { - get { - return ResourceManager.GetString("Main.Advanced.internal_cmd_char", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle inventory handling.. - /// - internal static string Main_Advanced_inventory_handling { - get { - return ResourceManager.GetString("Main.Advanced.inventory_handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. - /// - internal static string Main_Advanced_language { - get { - return ResourceManager.GetString("Main.Advanced.language", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. - /// - internal static string Main_Advanced_LoadMccTrans { - get { - return ResourceManager.GetString("Main.Advanced.LoadMccTrans", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. - /// - internal static string Main_Advanced_mc_forge { - get { - return ResourceManager.GetString("Main.Advanced.mc_forge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. - /// - internal static string Main_Advanced_mc_version { - get { - return ResourceManager.GetString("Main.Advanced.mc_version", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. - /// - internal static string Main_Advanced_message_cooldown { - get { - return ResourceManager.GetString("Main.Advanced.message_cooldown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. - /// - internal static string Main_Advanced_minecraft_realms { - get { - return ResourceManager.GetString("Main.Advanced.minecraft_realms", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. - /// - internal static string Main_Advanced_MinTerminalHeight { - get { - return ResourceManager.GetString("Main.Advanced.MinTerminalHeight", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. - /// - internal static string Main_Advanced_MinTerminalWidth { - get { - return ResourceManager.GetString("Main.Advanced.MinTerminalWidth", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. - /// - internal static string Main_Advanced_move_head_while_walking { - get { - return ResourceManager.GetString("Main.Advanced.move_head_while_walking", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. - /// - internal static string Main_Advanced_movement_speed { - get { - return ResourceManager.GetString("Main.Advanced.movement_speed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. - /// - internal static string Main_Advanced_player_head_icon { - get { - return ResourceManager.GetString("Main.Advanced.player_head_icon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to For remote control of the bot.. - /// - internal static string Main_Advanced_private_msgs_cmd_name { - get { - return ResourceManager.GetString("Main.Advanced.private_msgs_cmd_name", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. - /// - internal static string Main_Advanced_profilekey_cache { - get { - return ResourceManager.GetString("Main.Advanced.profilekey_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. - /// - internal static string Main_Advanced_resolve_srv_records { - get { - return ResourceManager.GetString("Main.Advanced.resolve_srv_records", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. - /// - internal static string Main_Advanced_script_cache { - get { - return ResourceManager.GetString("Main.Advanced.script_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP - ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. - ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". - /// - internal static string Main_Advanced_server_list { - get { - return ResourceManager.GetString("Main.Advanced.server_list", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. - /// - internal static string Main_Advanced_session_cache { - get { - return ResourceManager.GetString("Main.Advanced.session_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. - /// - internal static string Main_Advanced_show_chat_links { - get { - return ResourceManager.GetString("Main.Advanced.show_chat_links", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. - /// - internal static string Main_Advanced_show_inventory_layout { - get { - return ResourceManager.GetString("Main.Advanced.show_inventory_layout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to System messages for server ops.. - /// - internal static string Main_Advanced_show_system_messages { - get { - return ResourceManager.GetString("Main.Advanced.show_system_messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. - /// - internal static string Main_Advanced_show_xpbar_messages { - get { - return ResourceManager.GetString("Main.Advanced.show_xpbar_messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers. Need to enable "TerrainAndMovements" first.. - /// - internal static string Main_Advanced_temporary_fix_badpacket { - get { - return ResourceManager.GetString("Main.Advanced.temporary_fix_badpacket", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. - /// - internal static string Main_Advanced_terrain_and_movements { - get { - return ResourceManager.GetString("Main.Advanced.terrain_and_movements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). - /// - internal static string Main_Advanced_timeout { - get { - return ResourceManager.GetString("Main.Advanced.timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prepend timestamps to chat messages.. - /// - internal static string Main_Advanced_timestamps { - get { - return ResourceManager.GetString("Main.Advanced.timestamps", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. - /// - internal static string Main_General_account { - get { - return ResourceManager.GetString("Main.General.account", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yggdrasil authlib server domain name and port.. - /// - internal static string Main_General_AuthlibServer { - get { - return ResourceManager.GetString("Main.General.AuthlibServer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically). - /// - internal static string Main_General_login { - get { - return ResourceManager.GetString("Main.General.login", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once.. - /// - internal static string Main_General_method { - get { - return ResourceManager.GetString("Main.General.method", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Account type: "mojang" OR "microsoft" OR "yggdrasil". Also affects interactive login in console.. - /// - internal static string Main_General_server_info { - get { - return ResourceManager.GetString("Main.General.server_info", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. - /// - internal static string MCSettings { - get { - return ResourceManager.GetString("MCSettings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allows disabling chat colors server-side.. - /// - internal static string MCSettings_ChatColors { - get { - return ResourceManager.GetString("MCSettings.ChatColors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... - /// - internal static string MCSettings_ChatMode { - get { - return ResourceManager.GetString("MCSettings.ChatMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. - /// - internal static string MCSettings_Difficulty { - get { - return ResourceManager.GetString("MCSettings.Difficulty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. - /// - internal static string MCSettings_Enabled { - get { - return ResourceManager.GetString("MCSettings.Enabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use any language implemented in Minecraft.. - /// - internal static string MCSettings_Locale { - get { - return ResourceManager.GetString("MCSettings.Locale", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. - /// - internal static string MCSettings_MainHand { - get { - return ResourceManager.GetString("MCSettings.MainHand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value range: [0 - 255].. - /// - internal static string MCSettings_RenderDistance { - get { - return ResourceManager.GetString("MCSettings.RenderDistance", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly - ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. - ///If the connection to the Minecraft game server is blocked by the firewall, set Enabled_Ingame=true to use a proxy to connect to the game server. - /// /!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. - /// - internal static string Proxy { - get { - return ResourceManager.GetString("Proxy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. - /// - internal static string Proxy_Enabled_Ingame { - get { - return ResourceManager.GetString("Proxy.Enabled_Ingame", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. - /// - internal static string Proxy_Enabled_Login { - get { - return ResourceManager.GetString("Proxy.Enabled_Login", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to download MCC updates via proxy.. - /// - internal static string Proxy_Enabled_Update { - get { - return ResourceManager.GetString("Proxy.Enabled_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only required for password-protected proxies.. - /// - internal static string Proxy_Password { - get { - return ResourceManager.GetString("Proxy.Password", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. - /// - internal static string Proxy_Proxy_Type { - get { - return ResourceManager.GetString("Proxy.Proxy_Type", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. - /// - internal static string Proxy_Server { - get { - return ResourceManager.GetString("Proxy.Server", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only required for password-protected proxies.. - /// - internal static string Proxy_Username { - get { - return ResourceManager.GetString("Proxy.Username", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). - /// - internal static string Signature { - get { - return ResourceManager.GetString("Signature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". - /// - internal static string Signature_LoginWithSecureProfile { - get { - return ResourceManager.GetString("Signature.LoginWithSecureProfile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. - /// - internal static string Signature_MarkIllegallySignedMsg { - get { - return ResourceManager.GetString("Signature.MarkIllegallySignedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. - /// - internal static string Signature_MarkLegallySignedMsg { - get { - return ResourceManager.GetString("Signature.MarkLegallySignedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. - /// - internal static string Signature_MarkModifiedMsg { - get { - return ResourceManager.GetString("Signature.MarkModifiedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). - /// - internal static string Signature_MarkSystemMessage { - get { - return ResourceManager.GetString("Signature.MarkSystemMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. - /// - internal static string Signature_ShowIllegalSignedChat { - get { - return ResourceManager.GetString("Signature.ShowIllegalSignedChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. - /// - internal static string Signature_ShowModifiedChat { - get { - return ResourceManager.GetString("Signature.ShowModifiedChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sign the chat send from MCC. - /// - internal static string Signature_SignChat { - get { - return ResourceManager.GetString("Signature.SignChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". - /// - internal static string Signature_SignMessageInCommand { - get { - return ResourceManager.GetString("Signature.SignMessageInCommand", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MinecraftClient { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ConfigComments { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ConfigComments() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.Resources.ConfigComments.ConfigComments", typeof(ConfigComments).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to can be used in some other fields as %yourvar% + ///%username% and %serverip% are reserved variables.. + /// + internal static string AppVars_Variables { + get { + return ResourceManager.GetString("AppVars.Variables", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to =============================== # + /// Minecraft Console Client Bots # + ///=============================== #. + /// + internal static string ChatBot { + get { + return ResourceManager.GetString("ChatBot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get alerted when specified words are detected in chat + ///Useful for moderating your server or detecting when someone is talking to you. + /// + internal static string ChatBot_Alerts { + get { + return ResourceManager.GetString("ChatBot.Alerts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. + /// + internal static string ChatBot_Alerts_Beep_Enabled { + get { + return ResourceManager.GetString("ChatBot.Alerts.Beep_Enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of words/strings to NOT alert you on.. + /// + internal static string ChatBot_Alerts_Excludes { + get { + return ResourceManager.GetString("ChatBot.Alerts.Excludes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name of a file where alers logs will be written.. + /// + internal static string ChatBot_Alerts_Log_File { + get { + return ResourceManager.GetString("ChatBot.Alerts.Log_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log alerts info a file.. + /// + internal static string ChatBot_Alerts_Log_To_File { + get { + return ResourceManager.GetString("ChatBot.Alerts.Log_To_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of words/strings to alert you on.. + /// + internal static string ChatBot_Alerts_Matches { + get { + return ResourceManager.GetString("ChatBot.Alerts.Matches", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. + /// + internal static string ChatBot_Alerts_Trigger_By_Rain { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Rain", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. + /// + internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Thunderstorm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. + /// + internal static string ChatBot_Alerts_Trigger_By_Words { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Words", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection + /// /!\ Make sure your server rules do not forbid anti-AFK mechanisms! + /// /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5). + /// + internal static string ChatBot_AntiAfk { + get { + return ResourceManager.GetString("ChatBot.AntiAfk", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command to send to the server.. + /// + internal static string ChatBot_AntiAfk_Command { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Command", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The time interval for execution. (in seconds). + /// + internal static string ChatBot_AntiAfk_Delay { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sneak when sending the command.. + /// + internal static string ChatBot_AntiAfk_Use_Sneak { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Use_Sneak", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. + /// + internal static string ChatBot_AntiAfk_Use_Terrain_Handling { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Use_Terrain_Handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). + /// + internal static string ChatBot_AntiAfk_Walk_Range { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Range", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. + /// + internal static string ChatBot_AntiAfk_Walk_Retries { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Retries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically attack hostile mobs around you + ///You need to enable Entity Handling to use this bot + /// /!\ Make sure server rules allow your planned use of AutoAttack + /// /!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES!. + /// + internal static string ChatBot_AutoAttack { + get { + return ResourceManager.GetString("ChatBot.AutoAttack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow attacking hostile mobs.. + /// + internal static string ChatBot_AutoAttack_Attack_Hostile { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Hostile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow attacking passive mobs.. + /// + internal static string ChatBot_AutoAttack_Attack_Passive { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Passive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Capped between 1 to 4. + /// + internal static string ChatBot_AutoAttack_Attack_Range { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Range", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. + /// + internal static string ChatBot_AutoAttack_Cooldown_Time { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Cooldown_Time", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. + /// + internal static string ChatBot_AutoAttack_Entites_List { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Entites_List", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. + /// + internal static string ChatBot_AutoAttack_Interaction { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Interaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. + /// + internal static string ChatBot_AutoAttack_List_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.List_Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. + /// + internal static string ChatBot_AutoAttack_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. + /// + internal static string ChatBot_AutoAttack_Priority { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Priority", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically craft items in your inventory + ///See https://mccteam.github.io/g/bots/#auto-craft for how to use + ///You need to enable Inventory Handling to use this bot + ///You should also enable Terrain and Movements if you need to use a crafting table. + /// + internal static string ChatBot_AutoCraft { + get { + return ResourceManager.GetString("ChatBot.AutoCraft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. + /// + internal static string ChatBot_AutoCraft_CraftingTable { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.CraftingTable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. + /// + internal static string ChatBot_AutoCraft_OnFailure { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.OnFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. + ///Recipes.Type: crafting table type: "player" or "table" + ///Recipes.Result: the resulting item + ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. + ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. + /// + internal static string ChatBot_AutoCraft_Recipes { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.Recipes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auto-digging blocks. + ///You need to enable Terrain Handling to use this bot + ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. + ///Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead. + ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. + /// + internal static string ChatBot_AutoDig { + get { + return ResourceManager.GetString("ChatBot.AutoDig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. + /// + internal static string ChatBot_AutoDig_Auto_Start_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Auto_Start_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically switch to the appropriate tool.. + /// + internal static string ChatBot_AutoDig_Auto_Tool_Switch { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Auto_Tool_Switch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Apply Efficiency enchantment speed when AutoDig computes mining time. Disable this for strict anti-cheat compatibility.. + /// + internal static string ChatBot_AutoDig_Apply_Efficiency_Enchantments { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Apply_Efficiency_Enchantments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Apply Haste and Conduit Power speed effects when AutoDig computes mining time. Disable this for strict anti-cheat compatibility.. + /// + internal static string ChatBot_AutoDig_Apply_Haste_Effects { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Apply_Haste_Effects", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. + /// + internal static string ChatBot_AutoDig_Dig_Timeout { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Dig_Timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. + /// + internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Drop_Low_Durability_Tools", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. + /// + internal static string ChatBot_AutoDig_Durability_Limit { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Durability_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. + /// + internal static string ChatBot_AutoDig_List_Type { + get { + return ResourceManager.GetString("ChatBot.AutoDig.List_Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. + /// + internal static string ChatBot_AutoDig_Location_Order { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Location_Order", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. + /// + internal static string ChatBot_AutoDig_Locations { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Locations", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to output logs when digging blocks.. + /// + internal static string ChatBot_AutoDig_Log_Block_Dig { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Log_Block_Dig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "lookat", "fixedpos" or "both". Digging the block being looked at, the block in a fixed position, or the block that needs to be all met.. + /// + internal static string ChatBot_AutoDig_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically drop items in inventory + ///You need to enable Inventory Handling to use this bot + ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. + /// + internal static string ChatBot_AutoDrop { + get { + return ResourceManager.GetString("ChatBot.AutoDrop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. + /// + internal static string ChatBot_AutoDrop_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoDrop.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically eat food when your Hunger value is low + ///You need to enable Inventory Handling to use this bot. + /// + internal static string ChatBot_AutoEat { + get { + return ResourceManager.GetString("ChatBot.AutoEat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically catch fish using a fishing rod + ///Guide: https://mccteam.github.io/g/bots/#auto-fishing + ///You can use "/fish" to control the bot manually. + /// /!\ Make sure server rules allow automated farming before using this bot. + /// + internal static string ChatBot_AutoFishing { + get { + return ResourceManager.GetString("ChatBot.AutoFishing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep it as false if you have not changed it before.. + /// + internal static string ChatBot_AutoFishing_Antidespawn { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Antidespawn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. + /// + internal static string ChatBot_AutoFishing_Auto_Rod_Switch { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Rod_Switch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. + /// + internal static string ChatBot_AutoFishing_Auto_Start { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How soon to re-cast after successful fishing.. + /// + internal static string ChatBot_AutoFishing_Cast_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Cast_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. + /// + internal static string ChatBot_AutoFishing_Durability_Limit { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Durability_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. + /// + internal static string ChatBot_AutoFishing_Enable_Move { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Enable_Move", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. + /// + internal static string ChatBot_AutoFishing_Fishing_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. + /// + internal static string ChatBot_AutoFishing_Fishing_Timeout { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. + /// + internal static string ChatBot_AutoFishing_Hook_Threshold { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Hook_Threshold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Used to adjust the above two thresholds, which when enabled will print the change in the position of the fishhook entity upon receipt of its movement packet.. + /// + internal static string ChatBot_AutoFishing_Log_Fish_Bobber { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Log_Fish_Bobber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. + /// + internal static string ChatBot_AutoFishing_Mainhand { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Mainhand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It will move in order "1->2->3->4->3->2->1->2->..." and can change position or facing or both each time. It is recommended to change the facing only.. + /// + internal static string ChatBot_AutoFishing_Movements { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Movements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. + /// + internal static string ChatBot_AutoFishing_Stationary_Threshold { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Stationary_Threshold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating + /// /!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. + /// + internal static string ChatBot_AutoRelog { + get { + return ResourceManager.GetString("ChatBot.AutoRelog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The delay time before joining the server. (in seconds). + /// + internal static string ChatBot_AutoRelog_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. + /// + internal static string ChatBot_AutoRelog_Ignore_Kick_Message { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Ignore_Kick_Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. + /// + internal static string ChatBot_AutoRelog_Kick_Messages { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Kick_Messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. + /// + internal static string ChatBot_AutoRelog_Retries { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Retries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Run commands or send messages automatically when a specified pattern is detected in chat + ///Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules + /// /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam. + /// + internal static string ChatBot_AutoRespond { + get { + return ResourceManager.GetString("ChatBot.AutoRespond", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). + /// + internal static string ChatBot_AutoRespond_Match_Colors { + get { + return ResourceManager.GetString("ChatBot.AutoRespond.Match_Colors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logs chat messages in a file on disk.. + /// + internal static string ChatBot_ChatLog { + get { + return ResourceManager.GetString("ChatBot.ChatLog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. + ///For Setup you can either use the documentation or read here (Documentation has images). + ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge + ///Setup: + ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . + /// /!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. + /// + internal static string ChatBot_DiscordBridge { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. + /// + internal static string ChatBot_DiscordBridge_ChannelId { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.ChannelId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message formats + ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! + ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. + ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. + /// + internal static string ChatBot_DiscordBridge_Formats { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.Formats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. + /// + internal static string ChatBot_DiscordBridge_GuildId { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.GuildId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. + /// + internal static string ChatBot_DiscordBridge_MessageSendTimeout { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.MessageSendTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. + /// + internal static string ChatBot_DiscordBridge_OwnersIds { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.OwnersIds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Discord Bot token.. + /// + internal static string ChatBot_DiscordBridge_Token { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.Token", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When enabled, messages from other Discord bots in the channel will be relayed to Minecraft chat.. + /// + internal static string ChatBot_DiscordBridge_AllowOtherBotMessages { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.AllowOtherBotMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically farms cropsfor you (plants, breaks and bonemeals them). + ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. + ///Usage: "/farmer start" command and "/farmer stop" command. + ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. + ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. + /// + internal static string ChatBot_Farmer { + get { + return ResourceManager.GetString("ChatBot.Farmer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). + /// + internal static string ChatBot_Farmer_Delay_Between_Tasks { + get { + return ResourceManager.GetString("ChatBot.Farmer.Delay_Between_Tasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled you to make the bot follow you + ///NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you + ///It's similar to making animals follow you when you're holding food in your hand. + ///This is due to a slow pathfinding algorithm, we're working on getting a better one + ///You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, + /// [rest of string was truncated]";. + /// + internal static string ChatBot_FollowPlayer { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not follow the player if he is in the range of 3 blocks (prevents the bot from pushing a player in an infinite loop). + /// + internal static string ChatBot_FollowPlayer_Stop_At_Distance { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer.Stop_At_Distance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). + /// + internal static string ChatBot_FollowPlayer_Update_Limit { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer.Update_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time. + ///You need to have ChatFormat working correctly and add yourself in botowners to start the game with /tell <bot username> start + /// /!\ This bot may get a bit spammy if many players are interacting with it. + /// + internal static string ChatBot_HangmanGame { + get { + return ResourceManager.GetString("ChatBot.HangmanGame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A Chat Bot that collects items on the ground. + /// + internal static string ChatBot_ItemsCollector { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If set to true, the bot will return to it's starting position after there are no items to collect. + /// + internal static string ChatBot_ItemsCollector_Always_Return_To_Start { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Always_Return_To_Start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If set to true, the bot will collect all items, regardless of their type. If you want to use the whitelisted item types, disable this by setting it to false. + /// + internal static string ChatBot_ItemsCollector_Collect_All_Item_Types { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Collect_All_Item_Types", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The radius in which bot will look for items to collect (Default: 30). + /// + internal static string ChatBot_ItemsCollector_Collection_Radius { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Collection_Radius", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delay in milliseconds between bot scanning items (Recommended: 300-500). + /// + internal static string ChatBot_ItemsCollector_Delay_Between_Tasks { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Delay_Between_Tasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In this list you can specify which items the bot will collect. To enable this, set the Collect_All_Item_Types to false. (NOTE: This does not prevent the bot from accidentally picking up other items, it only goes to positions where it finds the whitelisted items)\nYou can see the list of item types here: https://raw.githubusercontent.com/MCCTeam/Minecraft-Console-Client/master/MinecraftClient/Inventory/ItemType.cs. + /// + internal static string ChatBot_ItemsCollector_Items_Whitelist { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Items_Whitelist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If set to true, the bot will go after clustered items instead for the closest ones. + /// + internal static string ChatBot_ItemsCollector_Prioritize_Clusters { + get { + return ResourceManager.GetString("ChatBot.ItemsCollector.Prioritize_Clusters", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show a Discord Rich Presence status with your current Minecraft session info. + ///Setup: + ///1. Go to https://discord.com/developers/applications and log in with your Discord account. [rest of string was truncated]";. + /// + internal static string ChatBot_DiscordRpc { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Discord Application ID.. + /// + internal static string ChatBot_DiscordRpc_ApplicationId { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ApplicationId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The top line of the Rich Presence display. Supports placeholders.. + /// + internal static string ChatBot_DiscordRpc_PresenceDetails { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.PresenceDetails", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The second line of the Rich Presence display. Supports placeholders.. + /// + internal static string ChatBot_DiscordRpc_PresenceState { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.PresenceState", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The key of the large image asset uploaded to your Discord application.. + /// + internal static string ChatBot_DiscordRpc_LargeImageKey { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.LargeImageKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tooltip text for the large image. Supports placeholders.. + /// + internal static string ChatBot_DiscordRpc_LargeImageText { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.LargeImageText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The key of the small image asset uploaded to your Discord application (leave empty to hide).. + /// + internal static string ChatBot_DiscordRpc_SmallImageKey { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.SmallImageKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tooltip text for the small image. Supports placeholders.. + /// + internal static string ChatBot_DiscordRpc_SmallImageText { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.SmallImageText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the server address (host and port) in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowServerAddress { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowServerAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the player coordinates in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowCoordinates { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowCoordinates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show health and food level in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowHealth { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowHealth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the current dimension in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowDimension { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowDimension", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the current gamemode in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowGamemode { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowGamemode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show elapsed session time in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowElapsedTime { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowElapsedTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the online player count as a party size in the Discord presence.. + /// + internal static string ChatBot_DiscordRpc_ShowPlayerCount { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.ShowPlayerCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How often (in seconds) to refresh the Discord presence. Minimum: 1. + /// + internal static string ChatBot_DiscordRpc_UpdateIntervalSeconds { + get { + return ResourceManager.GetString("ChatBot.DiscordRpc.UpdateIntervalSeconds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Relay messages between players and servers, like a mail plugin + ///This bot can store messages when the recipients are offline, and send them when they join the server + /// /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins. + /// + internal static string ChatBot_Mailer { + get { + return ResourceManager.GetString("ChatBot.Mailer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) + ///This is useful for solving captchas which use maps + ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. + ///NOTE: + ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. + /// /!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. + /// + internal static string ChatBot_Map { + get { + return ResourceManager.GetString("ChatBot.Map", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. + /// + internal static string ChatBot_Map_Auto_Render_On_Update { + get { + return ResourceManager.GetString("ChatBot.Map.Auto_Render_On_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. + /// + internal static string ChatBot_Map_Delete_All_On_Unload { + get { + return ResourceManager.GetString("ChatBot.Map.Delete_All_On_Unload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. + /// + internal static string ChatBot_Map_Notify_On_First_Update { + get { + return ResourceManager.GetString("ChatBot.Map.Notify_On_First_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. + /// + internal static string ChatBot_Map_Rasize_Rendered_Image { + get { + return ResourceManager.GetString("ChatBot.Map.Rasize_Rendered_Image", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to render the map in the console.. + /// + internal static string ChatBot_Map_Render_In_Console { + get { + return ResourceManager.GetString("ChatBot.Map.Render_In_Console", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. + /// + internal static string ChatBot_Map_Resize_To { + get { + return ResourceManager.GetString("ChatBot.Map.Resize_To", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. + /// + internal static string ChatBot_Map_Save_To_File { + get { + return ResourceManager.GetString("ChatBot.Map.Save_To_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) + ///You need to enable Save_To_File in order for this to work. + ///We also recommend turning on resizing.. + /// + internal static string ChatBot_Map_Send_Rendered_To_Bridges { + get { + return ResourceManager.GetString("ChatBot.Map.Send_Rendered_To_Bridges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log the list of players periodically into a textual file.. + /// + internal static string ChatBot_PlayerListLogger { + get { + return ResourceManager.GetString("ChatBot.PlayerListLogger", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (In seconds). + /// + internal static string ChatBot_PlayerListLogger_Delay { + get { + return ResourceManager.GetString("ChatBot.PlayerListLogger.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send MCC console commands to your bot through server PMs (/tell) + ///You need to have ChatFormat working correctly and add yourself in botowners to use the bot + /// /!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins. + /// + internal static string ChatBot_RemoteControl { + get { + return ResourceManager.GetString("ChatBot.RemoteControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/) + ///Please note that due to technical limitations, the client player (you) will not be shown in the replay file + /// /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT!. + /// + internal static string ChatBot_ReplayCapture { + get { + return ResourceManager.GetString("ChatBot.ReplayCapture", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. + /// + internal static string ChatBot_ReplayCapture_Backup_Interval { + get { + return ResourceManager.GetString("ChatBot.ReplayCapture.Backup_Interval", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval + ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. + /// + internal static string ChatBot_ScriptScheduler { + get { + return ResourceManager.GetString("ChatBot.ScriptScheduler", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. + /// /!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. + ///----------------------------------------------------------- + ///Setup: + ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather + ///Click on "Start" button and re [rest of string was truncated]";. + /// + internal static string ChatBot_TelegramBridge { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. + /// + internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Authorized_Chat_Ids", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. + /// + internal static string ChatBot_TelegramBridge_ChannelId { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.ChannelId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message formats + ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! + ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. + ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. + /// + internal static string ChatBot_TelegramBridge_Formats { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Formats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. + /// + internal static string ChatBot_TelegramBridge_MessageSendTimeout { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.MessageSendTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Telegram Bot token.. + /// + internal static string ChatBot_TelegramBridge_Token { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Token", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remotely control the client using Web Sockets.\n# This is useful if you want to implement an application that can remotely and asynchronously execute procedures in MCC.\n# Example implementation written in JavaScript: https://github.com/milutinke/MCC.js.git\n# The protocol specification will be available in the documentation soon.. + /// + internal static string ChatBot_WebSocketBot { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow IP aliases, such as "localhost" or if using containers then the container name can be used.... + /// + internal static string ChatBot_WebSocketBot_AllowIpAlias { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.AllowIpAlias", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions.. + /// + internal static string ChatBot_WebSocketBot_DebugMode { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.DebugMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The IP address that Websocket server will be bound to.. + /// + internal static string ChatBot_WebSocketBot_Ip { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.Ip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A password that will be used to authenticate on thw Websocket server (It is recommended to change the default password and to set a strong one).. + /// + internal static string ChatBot_WebSocketBot_Password { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Port that Websocket server will be bounded to.. + /// + internal static string ChatBot_WebSocketBot_Port { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.Port", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MCC does it best to detect chat messages, but some server have unusual chat formats + ///When this happens, you'll need to configure chat format below, see https://mccteam.github.io/g/conf/#chat-format-section. + /// + internal static string ChatFormat { + get { + return ResourceManager.GetString("ChatFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. + /// + internal static string ChatFormat_Builtins { + get { + return ResourceManager.GetString("ChatFormat.Builtins", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. + /// + internal static string ChatFormat_UserDefined { + get { + return ResourceManager.GetString("ChatFormat.UserDefined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Console-related settings.. + /// + internal static string Console { + get { + return ResourceManager.GetString("Console", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The settings for command completion suggestions. + ///Custom colors are only available when using "vt100_24bit" color mode.. + /// + internal static string Console_CommandSuggestion { + get { + return ResourceManager.GetString("Console.CommandSuggestion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display command suggestions in the console.. + /// + internal static string Console_CommandSuggestion_Enable { + get { + return ResourceManager.GetString("Console.CommandSuggestion.Enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable this option if the arrows in the command suggestions are not displayed properly in your terminal.. + /// + internal static string Console_CommandSuggestion_Use_Basic_Arrow { + get { + return ResourceManager.GetString("Console.CommandSuggestion.Use_Basic_Arrow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Console mode: "classic" for the standard terminal, "tui" for a pseudo-graphical full-screen interface.. + /// + internal static string Console_General_ConsoleMode { + get { + return ResourceManager.GetString("Console.General.ConsoleMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "disable", "legacy_4bit", "vt100_4bit", "vt100_8bit" or "vt100_24bit". If a garbled code like "←[0m" appears on the terminal, you can try switching to "legacy_4bit" mode, or just disable it.. + /// + internal static string Console_General_ConsoleColorMode { + get { + return ResourceManager.GetString("Console.General.ConsoleColorMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display the MCC startup banner with version info and icon.. + /// + internal static string Console_General_Display_Icon_Banner { + get { + return ResourceManager.GetString("Console.General.Display_Icon_Banner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can use "Ctrl+P" to print out the current input and cursor position.. + /// + internal static string Console_General_Display_Input { + get { + return ResourceManager.GetString("Console.General.Display_Input", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display received chat messages in the console. This does not affect chat bots or chat log files.. + /// + internal static string Console_General_Display_Chat { + get { + return ResourceManager.GetString("Console.General.Display_Chat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximum number of input history records to keep.. + /// + internal static string Console_General_History_Input_Records { + get { + return ResourceManager.GetString("Console.General.History_Input_Records", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximum log lines kept in TUI mode scrollback. Set to 0 for automatic (3000 on x86/x64, 500 on ARM).. + /// + internal static string Console_General_TUI_Log_Scrollback { + get { + return ResourceManager.GetString("Console.General.TUI_Log_Scrollback", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Startup Config File + ///Please do not record extraneous data in this file as it will be overwritten by MCC. + /// + ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html + ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. + /// + internal static string Head { + get { + return ResourceManager.GetString("Head", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This setting affects only the messages in the console.. + /// + internal static string Logging { + get { + return ResourceManager.GetString("Logging", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Regex for filtering chat message.. + /// + internal static string Logging_ChatFilter { + get { + return ResourceManager.GetString("Logging.ChatFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show server chat messages.. + /// + internal static string Logging_ChatMessages { + get { + return ResourceManager.GetString("Logging.ChatMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Regex for filtering debug message.. + /// + internal static string Logging_DebugFilter { + get { + return ResourceManager.GetString("Logging.DebugFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. + /// + internal static string Logging_DebugMessages { + get { + return ResourceManager.GetString("Logging.DebugMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show low-level packet debug logs.. + /// + internal static string Logging_PacketDebugMessages { + get { + return ResourceManager.GetString("Logging.PacketDebugMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Packet types to exclude from packet debug logs, e.g. ["KeepAlive", "Ping"].. + /// + internal static string Logging_PacketDebugExclusions { + get { + return ResourceManager.GetString("Logging.PacketDebugExclusions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show error messages.. + /// + internal static string Logging_ErrorMessages { + get { + return ResourceManager.GetString("Logging.ErrorMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. + /// + internal static string Logging_FilterMode { + get { + return ResourceManager.GetString("Logging.FilterMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). + /// + internal static string Logging_InfoMessages { + get { + return ResourceManager.GetString("Logging.InfoMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log file name.. + /// + internal static string Logging_LogFile { + get { + return ResourceManager.GetString("Logging.LogFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Write log messages to file.. + /// + internal static string Logging_LogToFile { + get { + return ResourceManager.GetString("Logging.LogToFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prepend timestamp to messages in log file.. + /// + internal static string Logging_PrependTimestamp { + get { + return ResourceManager.GetString("Logging.PrependTimestamp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). + /// + internal static string Logging_SaveColorCodes { + get { + return ResourceManager.GetString("Logging.SaveColorCodes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show warning messages.. + /// + internal static string Logging_WarningMessages { + get { + return ResourceManager.GetString("Logging.WarningMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. + /// + internal static string Main_Advanced { + get { + return ResourceManager.GetString("Main.Advanced", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials + ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". + /// + internal static string Main_Advanced_account_list { + get { + return ResourceManager.GetString("Main.Advanced.account_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. + /// + internal static string Main_Advanced_auto_respawn { + get { + return ResourceManager.GetString("Main.Advanced.auto_respawn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. + /// + internal static string Main_Advanced_bot_owners { + get { + return ResourceManager.GetString("Main.Advanced.bot_owners", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. + /// + internal static string Main_Advanced_brand_info { + get { + return ResourceManager.GetString("Main.Advanced.brand_info", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leave empty for no logfile.. + /// + internal static string Main_Advanced_chatbot_log_file { + get { + return ResourceManager.GetString("Main.Advanced.chatbot_log_file", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. + /// + internal static string Main_Advanced_enable_emoji { + get { + return ResourceManager.GetString("Main.Advanced.enable_emoji", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set to false to opt-out of Sentry error logging.. + /// + internal static string Main_Advanced_enable_sentry { + get { + return ResourceManager.GetString("Main.Advanced.enable_sentry", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle entity handling.. + /// + internal static string Main_Advanced_entity_handling { + get { + return ResourceManager.GetString("Main.Advanced.entity_handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. + /// + internal static string Main_Advanced_exit_on_failure { + get { + return ResourceManager.GetString("Main.Advanced.exit_on_failure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignore invalid player name. + /// + internal static string Main_Advanced_ignore_invalid_playername { + get { + return ResourceManager.GetString("Main.Advanced.ignore_invalid_playername", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. + /// + internal static string Main_Advanced_internal_cmd_char { + get { + return ResourceManager.GetString("Main.Advanced.internal_cmd_char", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle inventory handling.. + /// + internal static string Main_Advanced_inventory_handling { + get { + return ResourceManager.GetString("Main.Advanced.inventory_handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. + /// + internal static string Main_Advanced_language { + get { + return ResourceManager.GetString("Main.Advanced.language", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. + /// + internal static string Main_Advanced_LoadMccTrans { + get { + return ResourceManager.GetString("Main.Advanced.LoadMccTrans", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. + /// + internal static string Main_Advanced_mc_forge { + get { + return ResourceManager.GetString("Main.Advanced.mc_forge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. + /// + internal static string Main_Advanced_mc_version { + get { + return ResourceManager.GetString("Main.Advanced.mc_version", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. + /// + internal static string Main_Advanced_message_cooldown { + get { + return ResourceManager.GetString("Main.Advanced.message_cooldown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Override the maximum chat message length. Set to 0 to use the default (100 for 1.10 and below, 256 for 1.11+). WARNING: Setting this incorrectly may cause you to be kicked from the server.. + /// + internal static string Main_Advanced_max_chat_message_length { + get { + return ResourceManager.GetString("Main.Advanced.max_chat_message_length", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. + /// + internal static string Main_Advanced_minecraft_realms { + get { + return ResourceManager.GetString("Main.Advanced.minecraft_realms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. + /// + internal static string Main_Advanced_MinTerminalHeight { + get { + return ResourceManager.GetString("Main.Advanced.MinTerminalHeight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. + /// + internal static string Main_Advanced_MinTerminalWidth { + get { + return ResourceManager.GetString("Main.Advanced.MinTerminalWidth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. + /// + internal static string Main_Advanced_move_head_while_walking { + get { + return ResourceManager.GetString("Main.Advanced.move_head_while_walking", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. + /// + internal static string Main_Advanced_movement_speed { + get { + return ResourceManager.GetString("Main.Advanced.movement_speed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. + /// + internal static string Main_Advanced_player_head_icon { + get { + return ResourceManager.GetString("Main.Advanced.player_head_icon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to For remote control of the bot.. + /// + internal static string Main_Advanced_private_msgs_cmd_name { + get { + return ResourceManager.GetString("Main.Advanced.private_msgs_cmd_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. + /// + internal static string Main_Advanced_profilekey_cache { + get { + return ResourceManager.GetString("Main.Advanced.profilekey_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. + /// + internal static string Main_Advanced_resolve_srv_records { + get { + return ResourceManager.GetString("Main.Advanced.resolve_srv_records", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. + /// + internal static string Main_Advanced_script_cache { + get { + return ResourceManager.GetString("Main.Advanced.script_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP + ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. + ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". + /// + internal static string Main_Advanced_server_list { + get { + return ResourceManager.GetString("Main.Advanced.server_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. + /// + internal static string Main_Advanced_session_cache { + get { + return ResourceManager.GetString("Main.Advanced.session_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. + /// + internal static string Main_Advanced_show_chat_links { + get { + return ResourceManager.GetString("Main.Advanced.show_chat_links", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. + /// + internal static string Main_Advanced_show_inventory_layout { + get { + return ResourceManager.GetString("Main.Advanced.show_inventory_layout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show a GitHub star reminder on startup. Set to false to hide it.. + /// + internal static string Main_Advanced_show_github_star_reminder { + get { + return ResourceManager.GetString("Main.Advanced.show_github_star_reminder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show full effect names and levels in the TUI status bar instead of compact effect icons only.. + /// + internal static string Main_Advanced_show_effect_names_in_tui { + get { + return ResourceManager.GetString("Main.Advanced.show_effect_names_in_tui", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to System messages for server ops.. + /// + internal static string Main_Advanced_show_system_messages { + get { + return ResourceManager.GetString("Main.Advanced.show_system_messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. + /// + internal static string Main_Advanced_show_xpbar_messages { + get { + return ResourceManager.GetString("Main.Advanced.show_xpbar_messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers. Need to enable "TerrainAndMovements" first.. + /// + internal static string Main_Advanced_temporary_fix_badpacket { + get { + return ResourceManager.GetString("Main.Advanced.temporary_fix_badpacket", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. + /// + internal static string Main_Advanced_terrain_and_movements { + get { + return ResourceManager.GetString("Main.Advanced.terrain_and_movements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). + /// + internal static string Main_Advanced_timeout { + get { + return ResourceManager.GetString("Main.Advanced.timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prepend timestamps to chat messages.. + /// + internal static string Main_Advanced_timestamps { + get { + return ResourceManager.GetString("Main.Advanced.timestamps", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. + /// + internal static string Main_General_account { + get { + return ResourceManager.GetString("Main.General.account", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yggdrasil authlib server domain name and port.. + /// + internal static string Main_General_AuthlibServer { + get { + return ResourceManager.GetString("Main.General.AuthlibServer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yggdrasil authlib multi-user selection.. + /// + internal static string Main_General_AuthlibUser { + get { + return ResourceManager.GetString("Main.General.AuthlibUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically). + /// + internal static string Main_General_login { + get { + return ResourceManager.GetString("Main.General.login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" (device code, supports 2FA) OR "browser" (manual browser login).. + /// + internal static string Main_General_method { + get { + return ResourceManager.GetString("Main.General.method", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account type: "mojang" OR "microsoft" OR "yggdrasil". Also affects interactive login in console.. + /// + internal static string Main_General_server_info { + get { + return ResourceManager.GetString("Main.General.server_info", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. + /// + internal static string MCSettings { + get { + return ResourceManager.GetString("MCSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows disabling chat colors server-side.. + /// + internal static string MCSettings_ChatColors { + get { + return ResourceManager.GetString("MCSettings.ChatColors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... + /// + internal static string MCSettings_ChatMode { + get { + return ResourceManager.GetString("MCSettings.ChatMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. + /// + internal static string MCSettings_Difficulty { + get { + return ResourceManager.GetString("MCSettings.Difficulty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. + /// + internal static string MCSettings_Enabled { + get { + return ResourceManager.GetString("MCSettings.Enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use any language implemented in Minecraft.. + /// + internal static string MCSettings_Locale { + get { + return ResourceManager.GetString("MCSettings.Locale", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. + /// + internal static string MCSettings_MainHand { + get { + return ResourceManager.GetString("MCSettings.MainHand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value range: [0 - 255].. + /// + internal static string MCSettings_RenderDistance { + get { + return ResourceManager.GetString("MCSettings.RenderDistance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly + ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. + ///If the connection to the Minecraft game server is blocked by the firewall, set Enabled_Ingame=true to use a proxy to connect to the game server. + /// /!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. + /// + internal static string Proxy { + get { + return ResourceManager.GetString("Proxy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. + /// + internal static string Proxy_Enabled_Ingame { + get { + return ResourceManager.GetString("Proxy.Enabled_Ingame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. + /// + internal static string Proxy_Enabled_Login { + get { + return ResourceManager.GetString("Proxy.Enabled_Login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to download MCC updates via proxy.. + /// + internal static string Proxy_Enabled_Update { + get { + return ResourceManager.GetString("Proxy.Enabled_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only required for password-protected proxies.. + /// + internal static string Proxy_Password { + get { + return ResourceManager.GetString("Proxy.Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. + /// + internal static string Proxy_Proxy_Type { + get { + return ResourceManager.GetString("Proxy.Proxy_Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. + /// + internal static string Proxy_Server { + get { + return ResourceManager.GetString("Proxy.Server", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only required for password-protected proxies.. + /// + internal static string Proxy_Username { + get { + return ResourceManager.GetString("Proxy.Username", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). + /// + internal static string Signature { + get { + return ResourceManager.GetString("Signature", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". + /// + internal static string Signature_LoginWithSecureProfile { + get { + return ResourceManager.GetString("Signature.LoginWithSecureProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. + /// + internal static string Signature_MarkIllegallySignedMsg { + get { + return ResourceManager.GetString("Signature.MarkIllegallySignedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. + /// + internal static string Signature_MarkLegallySignedMsg { + get { + return ResourceManager.GetString("Signature.MarkLegallySignedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. + /// + internal static string Signature_MarkModifiedMsg { + get { + return ResourceManager.GetString("Signature.MarkModifiedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). + /// + internal static string Signature_MarkSystemMessage { + get { + return ResourceManager.GetString("Signature.MarkSystemMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. + /// + internal static string Signature_ShowIllegalSignedChat { + get { + return ResourceManager.GetString("Signature.ShowIllegalSignedChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. + /// + internal static string Signature_ShowModifiedChat { + get { + return ResourceManager.GetString("Signature.ShowModifiedChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sign the chat send from MCC. + /// + internal static string Signature_SignChat { + get { + return ResourceManager.GetString("Signature.SignChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". + /// + internal static string Signature_SignMessageInCommand { + get { + return ResourceManager.GetString("Signature.SignMessageInCommand", resourceCulture); + } + } + } +} diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index ca440314..d1ebbcb2 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -1,4 +1,4 @@ - + " + ex.InnerException.GetFullMessage(); } diff --git a/MinecraftClient/TabList/TabListFormatter.cs b/MinecraftClient/TabList/TabListFormatter.cs new file mode 100644 index 00000000..633cc46f --- /dev/null +++ b/MinecraftClient/TabList/TabListFormatter.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MinecraftClient.Mapping; +using MinecraftClient.Scripting; + +namespace MinecraftClient +{ + internal sealed record TabListSnapshot(string Header, string Footer, IReadOnlyList Entries); + + internal sealed record TabListEntry( + Guid Uuid, + string Name, + string DisplayName, + string TeamName, + string TeamDisplayName, + int Gamemode, + int Ping, + int TabListOrder, + bool Listed); + + internal static class TabListFormatter + { + private const int MaxRowsPerColumn = 20; + private const int PingColumnWidth = 11; + private const int ColumnGapWidth = 4; + + public static string FormatTeamMemberName(string playerName, PlayerTeam? team) + { + ArgumentException.ThrowIfNullOrWhiteSpace(playerName); + + if (team is null) + return playerName; + + var sb = new StringBuilder(); + sb.Append(team.Prefix); + + string colorCode = TeamColorToTag(team.Color); + if (!string.IsNullOrEmpty(colorCode)) + sb.Append(colorCode); + + sb.Append(playerName); + sb.Append(team.Suffix); + return sb.ToString(); + } + + public static string Render(TabListSnapshot snapshot, bool includeOverlayHint = false) + { + ArgumentNullException.ThrowIfNull(snapshot); + bool showTeams = Settings.Config.Console.TabList.ShowTeams; + + var lines = new List + { + $"§e{string.Format(Translations.cmd_tab_title, snapshot.Entries.Count)}§r" + }; + + AppendSection(lines, snapshot.Header); + + var listedEntries = snapshot.Entries + .Where(static entry => entry.Listed && !string.IsNullOrWhiteSpace(entry.Name)) + .OrderBy(static entry => entry.TabListOrder) + .ThenBy(static entry => entry.Gamemode == 3 ? 1 : 0) + .ThenBy(static entry => entry.TeamName, StringComparer.OrdinalIgnoreCase) + .ThenBy(static entry => entry.Name, StringComparer.OrdinalIgnoreCase) + .ToList(); + + if (listedEntries.Count == 0) + { + lines.Add($"§7{Translations.cmd_tab_no_players}§r"); + } + else + { + lines.AddRange(BuildTableLines(listedEntries, showTeams)); + } + + AppendSection(lines, snapshot.Footer); + + if (includeOverlayHint) + { + lines.Add(string.Empty); + lines.Add($"§8{Translations.tui_tab_hint}§r"); + } + + return string.Join('\n', lines); + } + + private static void AppendSection(List lines, string text) + { + if (string.IsNullOrWhiteSpace(text)) + return; + + foreach (string line in text.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n').Split('\n')) + { + if (!string.IsNullOrWhiteSpace(line)) + lines.Add(line); + } + } + + private static List BuildTableLines(IReadOnlyList entries, bool showTeams) + { + int columns = 1; + int rows = entries.Count; + while (rows > MaxRowsPerColumn) + { + columns++; + rows = (entries.Count + columns - 1) / columns; + } + + int teamColumnWidth = showTeams + ? Math.Max( + GetVisibleLength(Translations.cmd_tab_column_team), + entries.Max(static entry => GetVisibleLength(GetTeamLabel(entry)))) + : 0; + + string headerRow = BuildRow( + $"§8{Translations.cmd_tab_column_ping}§r", + showTeams ? $"§8{Translations.cmd_tab_column_team}§r" : null, + $"§8{Translations.cmd_tab_column_player}§r", + teamColumnWidth); + + List renderedRows = entries + .Select(entry => BuildRow( + GetPingCell(entry.Ping), + showTeams ? GetTeamLabel(entry) : null, + GetPlayerLabel(entry), + teamColumnWidth)) + .ToList(); + + int[] columnWidths = new int[columns]; + for (int column = 0; column < columns; column++) + { + int width = GetVisibleLength(headerRow); + for (int row = 0; row < rows; row++) + { + int index = row + (column * rows); + if (index >= renderedRows.Count) + break; + + width = Math.Max(width, GetVisibleLength(renderedRows[index])); + } + columnWidths[column] = width; + } + + var lines = new List { CombineColumns(headerRow, rows, columns, columnWidths) }; + for (int row = 0; row < rows; row++) + lines.Add(CombineColumns(renderedRows, row, rows, columns, columnWidths)); + + return lines; + } + + private static string CombineColumns(string headerRow, int rows, int columns, IReadOnlyList columnWidths) + { + var perColumnValues = new string[columns]; + for (int column = 0; column < columns; column++) + perColumnValues[column] = headerRow; + + return CombineColumns(perColumnValues, columnWidths); + } + + private static string CombineColumns(IReadOnlyList renderedRows, int row, int rows, int columns, IReadOnlyList columnWidths) + { + var perColumnValues = new string[columns]; + for (int column = 0; column < columns; column++) + { + int index = row + (column * rows); + perColumnValues[column] = index < renderedRows.Count ? renderedRows[index] : string.Empty; + } + + return CombineColumns(perColumnValues, columnWidths); + } + + private static string CombineColumns(IReadOnlyList parts, IReadOnlyList widths) + { + var sb = new StringBuilder(); + for (int index = 0; index < parts.Count; index++) + { + if (index > 0) + sb.Append(' ', ColumnGapWidth); + + sb.Append(PadFormattedRight(parts[index], widths[index])); + } + return sb.ToString().TrimEnd(); + } + + private static string BuildRow(string pingCell, string? teamCell, string playerCell, int teamColumnWidth) + { + var sb = new StringBuilder(); + sb.Append(PadFormattedRight(pingCell, PingColumnWidth)); + sb.Append(" "); + if (!string.IsNullOrEmpty(teamCell)) + { + sb.Append(PadFormattedRight(teamCell, teamColumnWidth)); + sb.Append(" "); + } + sb.Append(playerCell); + return sb.ToString(); + } + + private static string GetPingCell(int ping) + { + string barColor; + int filledBars; + + if (ping < 0) + { + barColor = "§8"; + filledBars = 0; + } + else if (ping < 150) + { + barColor = "§a"; + filledBars = 5; + } + else if (ping < 300) + { + barColor = "§e"; + filledBars = 4; + } + else if (ping < 600) + { + barColor = "§6"; + filledBars = 3; + } + else if (ping < 1000) + { + barColor = "§c"; + filledBars = 2; + } + else + { + barColor = "§4"; + filledBars = 1; + } + + string numericPing = ping >= 0 ? $"{Math.Min(ping, 9999),4}ms" : " ???ms"; + return $"{barColor}{new string('|', filledBars)}§8{new string('.', 5 - filledBars)}§r {numericPing}"; + } + + private static string GetTeamLabel(TabListEntry entry) + { + if (string.IsNullOrWhiteSpace(entry.TeamName)) + return "§8-§r"; + + if (!string.IsNullOrWhiteSpace(entry.TeamDisplayName)) + return entry.TeamDisplayName; + + if (LooksLikeOpaqueTeamName(entry.TeamName)) + return "§8-§r"; + + return entry.TeamName; + } + + private static string GetPlayerLabel(TabListEntry entry) + { + string label = string.IsNullOrWhiteSpace(entry.DisplayName) + ? entry.Name + : entry.DisplayName; + + if (entry.Gamemode == 3) + return $"§7§o{label}§r"; + + return label; + } + + private static string PadFormattedRight(string text, int totalWidth) + { + int visibleLength = GetVisibleLength(text); + if (visibleLength >= totalWidth) + return text; + + return text + new string(' ', totalWidth - visibleLength); + } + + private static int GetVisibleLength(string text) => ChatBot.GetVerbatim(text).Length; + + private static bool LooksLikeOpaqueTeamName(string text) + { + if (Guid.TryParse(text, out _)) + return true; + + int hyphenCount = text.Count(static ch => ch == '-'); + if (text.Length >= 24 && hyphenCount >= 3) + return true; + + return false; + } + + private static string TeamColorToTag(int color) => color switch + { + 0 => "§0", + 1 => "§1", + 2 => "§2", + 3 => "§3", + 4 => "§4", + 5 => "§5", + 6 => "§6", + 7 => "§7", + 8 => "§8", + 9 => "§9", + 10 => "§a", + 11 => "§b", + 12 => "§c", + 13 => "§d", + 14 => "§e", + 15 => "§f", + _ => string.Empty + }; + } +} diff --git a/MinecraftClient/TaskWithResult.cs b/MinecraftClient/TaskWithResult.cs index 90e21e8d..53aec3aa 100644 --- a/MinecraftClient/TaskWithResult.cs +++ b/MinecraftClient/TaskWithResult.cs @@ -14,7 +14,7 @@ namespace MinecraftClient private T? result = default; private Exception? exception = null; private bool taskRun = false; - private readonly object taskRunLock = new(); + private readonly Lock taskRunLock = new(); /// /// Create a new asynchronous task with return value @@ -113,7 +113,7 @@ namespace MinecraftClient } // Receive exception from task - if (exception != null) + if (exception is not null) throw exception; return result!; diff --git a/MinecraftClient/Tui/BookTuiHost.cs b/MinecraftClient/Tui/BookTuiHost.cs new file mode 100644 index 00000000..5d20b13f --- /dev/null +++ b/MinecraftClient/Tui/BookTuiHost.cs @@ -0,0 +1,383 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui; + +public static class BookTuiHost +{ + private static volatile bool isRunning; + + public static bool TryOpen(McClient handler, BookHand hand, bool editable) + { + if (ConsoleIO.Backend is not TuiConsoleBackend) + return false; + + Open(handler, hand, editable); + return true; + } + + public static void OpenFromServer(McClient handler, BookHand hand) + { + if (ConsoleIO.Backend is TuiConsoleBackend) + Open(handler, hand, editable: BookContentHelper.IsWritableBook(handler.GetHeldBook(hand))); + } + + private static void Open(McClient handler, BookHand hand, bool editable) + { + Dispatcher.UIThread.Post(() => + { + if (isRunning) + return; + + MainTuiView? view = TuiConsoleBackend.Instance?.GetView(); + if (view is null) + return; + + if (!handler.TryGetHeldBookContent(out BookContent content, hand)) + return; + + isRunning = true; + var bookView = new BookView(handler, content, editable && !content.IsSigned); + view.ShowOverlay(bookView, () => isRunning = false); + }); + } +} + +internal sealed class BookView : UserControl +{ + private readonly McClient handler; + private readonly bool editable; + private readonly List pages; + private readonly TextBlock header; + private readonly TextBlock status; + private readonly TextBlock shortcutTip; + private readonly TextBox pageText; + private readonly TextBox titleText; + private readonly Button previousButton; + private readonly Button nextButton; + private readonly Button insertButton; + private readonly Button deleteButton; + private readonly Button saveButton; + private readonly Button signButton; + private bool bookSigned; + private int pageIndex; + + public BookView(McClient handler, BookContent content, bool editable) + { + this.handler = handler; + this.editable = editable; + bookSigned = content.IsSigned; + pages = content.Pages.ToList(); + if (pages.Count == 0) + pages.Add(string.Empty); + + Focusable = true; + Background = Brushes.Black; + + header = new TextBlock + { + Foreground = Brushes.Yellow, + Margin = new Thickness(1, 0), + TextWrapping = TextWrapping.Wrap + }; + + status = new TextBlock + { + Foreground = Brushes.Gray, + Margin = new Thickness(1, 0), + TextWrapping = TextWrapping.Wrap + }; + + shortcutTip = new TextBlock + { + Foreground = Brushes.DarkGray, + Margin = new Thickness(1, 0), + Text = Translations.tui_book_page_shortcut_tip, + TextWrapping = TextWrapping.Wrap + }; + + pageText = new TextBox + { + AcceptsReturn = true, + TextWrapping = TextWrapping.Wrap, + IsReadOnly = !CanEdit, + Foreground = Brushes.White, + Background = Brushes.Black, + BorderBrush = Brushes.Gray, + MinHeight = 12, + Margin = new Thickness(1) + }; + pageText.TextChanged += (_, _) => + { + if (CanEdit && pageIndex >= 0 && pageIndex < pages.Count) + pages[pageIndex] = pageText.Text ?? string.Empty; + }; + + titleText = new TextBox + { + Watermark = Translations.tui_book_title_watermark, + IsVisible = CanEdit, + Foreground = Brushes.White, + Background = Brushes.Black, + BorderBrush = Brushes.Gray, + Margin = new Thickness(1) + }; + + previousButton = Button(Translations.tui_book_prev, (_, _) => TryMovePage(-1)); + nextButton = Button(Translations.tui_book_next, (_, _) => TryMovePage(1)); + insertButton = Button(Translations.tui_book_insert, (_, _) => InsertPage(), editable); + deleteButton = Button(Translations.tui_book_delete, (_, _) => DeletePage(), editable); + saveButton = Button(Translations.tui_book_save, (_, _) => Save(), editable); + signButton = Button(Translations.tui_book_sign, (_, _) => Sign(), editable); + + var controls = new StackPanel + { + Orientation = Orientation.Horizontal, + Spacing = 1, + Margin = new Thickness(1), + Children = + { + previousButton, + nextButton, + insertButton, + deleteButton, + saveButton, + signButton, + Button(Translations.tui_book_close, (_, _) => Close()) + } + }; + + var panel = new DockPanel + { + Background = Brushes.Black, + Children = + { + DockTo(header, Dock.Top), + DockTo(status, Dock.Bottom), + DockTo(shortcutTip, Dock.Bottom), + DockTo(controls, Dock.Bottom), + DockTo(titleText, Dock.Bottom), + pageText + } + }; + + Content = panel; + AttachedToVisualTree += (_, _) => + { + AddHandler(KeyDownEvent, OnTunnelKeyDown, RoutingStrategies.Tunnel, handledEventsToo: true); + FocusPageText(); + }; + DetachedFromVisualTree += (_, _) => RemoveHandler(KeyDownEvent, OnTunnelKeyDown); + Refresh(); + } + + private bool CanEdit => editable && !bookSigned; + + private void OnTunnelKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.PageUp) + { + TryMovePage(-1); + e.Handled = true; + return; + } + + if (e.Key == Key.PageDown) + { + TryMovePage(1); + e.Handled = true; + } + } + + private static Control DockTo(Control control, Dock dock) + { + DockPanel.SetDock(control, dock); + return control; + } + + private static Button Button(string text, EventHandler handler, bool enabled = true) + { + var button = new Button + { + Content = text, + IsEnabled = enabled, + Padding = new Thickness(1, 0), + Margin = new Thickness(0) + }; + button.Click += handler; + return button; + } + + private bool TryMovePage(int delta) + { + int targetPageIndex = Math.Clamp(pageIndex + delta, 0, pages.Count - 1); + if (targetPageIndex == pageIndex) + return false; + + pageIndex = targetPageIndex; + Refresh(); + return true; + } + + private void InsertPage() + { + if (!CanEdit) + return; + + pages.Insert(pageIndex + 1, string.Empty); + pageIndex++; + Refresh(); + } + + private void DeletePage() + { + if (!CanEdit) + return; + + if (pages.Count == 1) + pages[0] = string.Empty; + else + { + pages.RemoveAt(pageIndex); + pageIndex = Math.Clamp(pageIndex, 0, pages.Count - 1); + } + Refresh(); + } + + private void Save() + { + if (bookSigned || IsHeldBookSigned()) + { + bookSigned = true; + status.Text = Translations.cmd_book_cannot_edit_signed; + RefreshEditability(); + return; + } + + if (!Validate(out string error)) + { + status.Text = error; + return; + } + + status.Text = handler.SendBookEdit(pages) + ? Translations.tui_book_saved + : Translations.tui_book_save_failed; + } + + private void Sign() + { + if (bookSigned || IsHeldBookSigned()) + { + bookSigned = true; + status.Text = Translations.cmd_book_already_signed; + RefreshEditability(); + return; + } + + string title = (titleText.Text ?? string.Empty).Trim(); + if (!Validate(out string error, title)) + { + status.Text = error; + return; + } + + if (handler.SendBookEdit(pages, title)) + { + bookSigned = true; + status.Text = Translations.tui_book_signed; + RefreshEditability(); + return; + } + + status.Text = IsHeldBookSigned() + ? Translations.cmd_book_already_signed + : Translations.tui_book_save_failed; + } + + private bool Validate(out string error, string? title = null) + { + BookLimits limits = BookLimits.ForProtocol(handler.GetProtocolVersion()); + error = string.Empty; + + if (pages.Count > limits.MaxPages) + { + error = string.Format(Translations.cmd_book_too_many_pages, pages.Count, limits.MaxPages); + return false; + } + + for (int i = 0; i < pages.Count; i++) + { + if (pages[i].Length > limits.MaxPageLength) + { + error = string.Format(Translations.cmd_book_page_too_long, i + 1, pages[i].Length, limits.MaxPageLength); + return false; + } + } + + if (title is not null && (title.Length == 0 || title.Length > limits.MaxTitleLength)) + { + error = string.Format(Translations.cmd_book_title_invalid, limits.MaxTitleLength); + return false; + } + + return true; + } + + private void Close() + { + TuiConsoleBackend.Instance?.GetView()?.HideOverlay(); + } + + private void Refresh() + { + string currentPageText = pages[pageIndex]; + if (!string.Equals(pageText.Text, currentPageText, StringComparison.Ordinal)) + pageText.Text = currentPageText; + + header.Text = string.Format(Translations.tui_book_page_header, pageIndex + 1, pages.Count); + status.Text = CanEdit ? Translations.tui_book_editing : Translations.tui_book_reading; + RefreshEditability(); + FocusPageText(); + } + + private void RefreshEditability() + { + previousButton.IsEnabled = pageIndex > 0; + nextButton.IsEnabled = pageIndex < pages.Count - 1; + insertButton.IsEnabled = CanEdit; + deleteButton.IsEnabled = CanEdit; + saveButton.IsEnabled = CanEdit; + signButton.IsEnabled = CanEdit; + pageText.IsReadOnly = !CanEdit; + titleText.IsEnabled = CanEdit; + titleText.IsVisible = CanEdit; + shortcutTip.Text = CanEdit + ? Translations.tui_book_edit_shortcut_tip + : Translations.tui_book_page_shortcut_tip; + } + + private void FocusPageText() + { + Dispatcher.UIThread.Post(() => + { + pageText.Focus(); + if (CanEdit) + pageText.CaretIndex = pageText.Text?.Length ?? 0; + }, DispatcherPriority.Input); + } + + private bool IsHeldBookSigned() + { + return BookContentHelper.TryRead(handler.GetHeldBook(BookHand.Main), out BookContent content) && content.IsSigned; + } +} diff --git a/MinecraftClient/Tui/BrewingStandView.cs b/MinecraftClient/Tui/BrewingStandView.cs new file mode 100644 index 00000000..6a00071b --- /dev/null +++ b/MinecraftClient/Tui/BrewingStandView.cs @@ -0,0 +1,152 @@ +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class BrewingStandView : ContainerViewBase + { + private readonly BrewingViewModel _brewVm; + + public BrewingStandView(McClient handler, int windowId) + : base(new BrewingViewModel(handler, windowId)) + { + _brewVm = (BrewingViewModel)_vm; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 3 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var panel = new StackPanel + { + Spacing = 0, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + var topRow = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Spacing = 0, + }; + + var fuelCol = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + fuelCol.Children.Add(new TextBlock + { + Text = Translations.tui_brewing_fuel, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + fuelCol.Children.Add(CreateSlotCell(_brewVm.FuelSlot, 0, 0)); + topRow.Children.Add(fuelCol); + + topRow.Children.Add(new Border { Width = 2 }); + + var ingredientCol = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + ingredientCol.Children.Add(new TextBlock + { + Text = Translations.tui_brewing_ingredient, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + ingredientCol.Children.Add(CreateSlotCell(_brewVm.IngredientSlot, 0, 1)); + topRow.Children.Add(ingredientCol); + + panel.Children.Add(topRow); + + panel.Children.Add(new TextBlock + { + Text = "\u25bc", + Foreground = new SolidColorBrush(Color.FromRgb(140, 140, 140)), + HorizontalAlignment = HorizontalAlignment.Center, + }); + + var bottleRow = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Spacing = 0, + }; + for (int i = 0; i < 3; i++) + { + var bottlePanel = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + bottlePanel.Children.Add(new TextBlock + { + Text = string.Format(Translations.tui_brewing_bottle, i + 1), + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + bottlePanel.Children.Add(CreateSlotCell(_brewVm.BottleSlots[i], 1, i)); + bottleRow.Children.Add(bottlePanel); + } + + panel.Children.Add(bottleRow); + + return panel; + } + } + + public class BrewingViewModel : ContainerViewModel + { + public ObservableCollection BottleSlots { get; } = new(); + public SlotViewModel IngredientSlot { get; private set; } = null!; + public SlotViewModel FuelSlot { get; private set; } = null!; + + public BrewingViewModel(McClient handler, int windowId) + : base(handler, windowId, ContainerType.BrewingStand) + { + IngredientSlot = SlotMap[3]; + FuelSlot = SlotMap[4]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + for (int i = 0; i <= 2; i++) + { + var slot = new SlotViewModel(i); + BottleSlots.Add(slot); + SlotMap[i] = slot; + } + + SlotMap[3] = new SlotViewModel(3); + SlotMap[4] = new SlotViewModel(4); + + for (int i = 5; i <= 31; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 32; i <= 40; i++) + { + int hotbarIdx = i - 32; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + } +} diff --git a/MinecraftClient/Tui/ContainerViewBase.cs b/MinecraftClient/Tui/ContainerViewBase.cs new file mode 100644 index 00000000..92787f46 --- /dev/null +++ b/MinecraftClient/Tui/ContainerViewBase.cs @@ -0,0 +1,723 @@ +using System; +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public abstract class ContainerViewBase : UserControl + { + protected static readonly IBrush BrSlotEmptyA = new SolidColorBrush(Color.FromRgb(40, 40, 40)); + protected static readonly IBrush BrSlotEmptyB = new SolidColorBrush(Color.FromRgb(55, 55, 55)); + protected static readonly IBrush BrSlotFillA = new SolidColorBrush(Color.FromRgb(60, 60, 75)); + protected static readonly IBrush BrSlotFillB = new SolidColorBrush(Color.FromRgb(75, 75, 90)); + protected static readonly IBrush BrSlotHover = new SolidColorBrush(Color.FromRgb(100, 100, 140)); + protected static readonly IBrush BrName = Brushes.White; + protected static readonly IBrush BrCount = Brushes.Yellow; + protected static readonly IBrush BrDim = new SolidColorBrush(Color.FromRgb(80, 80, 80)); + protected static readonly IBrush BrEquipLbl = Brushes.DarkCyan; + protected static readonly IBrush BrInfoHighlight = new SolidColorBrush(Color.FromRgb(40, 40, 60)); + protected static readonly IBrush BrHeldItemBg = new SolidColorBrush(Color.FromRgb(60, 50, 80)); + protected static readonly IBrush BrHeldItemBorder = Brushes.Yellow; + + protected int _slotW; + protected int _slotH; + protected int _nameMaxLen; + protected int _nameLines; + protected int _termW; + + protected readonly ContainerViewModel _vm; + protected TextBlock _titleText = null!; + protected Border _infoDetailBorder = null!; + protected TextBlock _infoDetailText = null!; + protected TextBlock _cursorItemText = null!; + protected TextBlock _helpText = null!; + + protected TextBlock[] _hotbarIndicators = new TextBlock[9]; + protected int _currentHotbarSlot = -1; + + protected Border? _lastHoveredSlotBorder; + + protected Canvas _overlayCanvas = null!; + protected Border _heldItemFloater = null!; + protected TextBlock _heldItemFloaterName = null!; + protected TextBlock _heldItemFloaterCount = null!; + + protected ScrollViewer _chatScrollViewer = null!; + protected ObservableCollection? _chatLines; + protected int _lastTermW; + protected int _lastTermH; + protected bool _chatScrollToBottom = true; + + protected ContainerViewBase(ContainerViewModel vm) + { + _vm = vm; + _currentHotbarSlot = vm.Handler.GetCurrentSlot(); + + _chatLines = TuiConsoleBackend.Instance?.GetView()?.GetRecentLogLines(50) + ?? new ObservableCollection(); + } + + protected void Initialize() + { + RebuildUi(); + } + + protected abstract int GetTotalSlotRows(); + + protected abstract Control BuildContainerSpecificArea(); + + protected virtual void OnContainerDataChanged() { } + + protected virtual void RebuildUi() + { + int termH; + try + { + _termW = System.Console.WindowWidth; + termH = System.Console.WindowHeight; + } + catch + { + _termW = 120; + termH = 40; + } + + _lastTermW = _termW; + _lastTermH = termH; + + int availW = _termW - 26; + _slotW = Math.Clamp(availW / 9, 8, 18); + _nameMaxLen = _slotW; + + int totalRows = GetTotalSlotRows(); + int overhead = 4; + int chatMinH = 1; + _slotH = Math.Clamp((termH - overhead - chatMinH) / totalRows, 2, 5); + _nameLines = _slotH; + + _vm.SetSlotDisplayParams(_nameMaxLen, _nameLines); + + _lastHoveredSlotBorder = null; + + _titleText = new TextBlock + { + FontWeight = FontWeight.Bold, + Foreground = Brushes.Cyan, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + _infoDetailText = new TextBlock + { + TextWrapping = TextWrapping.Wrap, + Foreground = Brushes.White, + }; + + _infoDetailBorder = new Border + { + Background = Brushes.Transparent, + Padding = new Thickness(0), + Child = _infoDetailText, + }; + + _cursorItemText = new TextBlock + { + Foreground = Brushes.Yellow, + FontWeight = FontWeight.Bold, + TextWrapping = TextWrapping.Wrap, + }; + + _helpText = new TextBlock + { + TextWrapping = TextWrapping.Wrap, + Foreground = new SolidColorBrush(Color.FromRgb(140, 140, 140)), + Text = Translations.tui_inventory_controls_help, + }; + + _heldItemFloaterName = new TextBlock + { + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + TextWrapping = TextWrapping.Wrap, + }; + _heldItemFloaterCount = new TextBlock + { + Foreground = BrCount, + FontWeight = FontWeight.Bold, + }; + _heldItemFloater = new Border + { + Background = BrHeldItemBg, + BorderBrush = BrHeldItemBorder, + BorderThickness = new Thickness(1), + Padding = new Thickness(1, 0), + IsVisible = false, + MaxWidth = 24, + Child = new StackPanel + { + Children = { _heldItemFloaterName, _heldItemFloaterCount }, + }, + }; + + _overlayCanvas = new Canvas { IsHitTestVisible = false }; + _overlayCanvas.Children.Add(_heldItemFloater); + + var chatLines = _chatLines!; + chatLines.CollectionChanged += (_, _) => + { + Avalonia.Threading.Dispatcher.UIThread.Post(() => + { + var sv = _chatScrollViewer; + if (sv.Extent.Height > sv.Viewport.Height) + sv.Offset = new Vector(0, sv.Extent.Height - sv.Viewport.Height); + }, Avalonia.Threading.DispatcherPriority.Background); + }; + var chatItemsControl = new ItemsControl + { + ItemsSource = chatLines, + Focusable = false, + ItemTemplate = new FuncDataTemplate((s, _) => + McColorParser.CreateColoredTextBlock(s ?? "", TextWrapping.Wrap)), + }; + _chatScrollViewer = new ScrollViewer + { + Content = chatItemsControl, + Background = Brushes.Black, + HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, + VerticalScrollBarVisibility = ScrollBarVisibility.Hidden, + Padding = new Thickness(0), + }; + + _hotbarIndicators = new TextBlock[9]; + + Content = BuildRootLayout(); + UpdateTitle(); + UpdateInfoPanel(); + + _chatScrollToBottom = true; + _chatScrollViewer.ScrollChanged += OnChatScrollChanged; + } + + private void OnChatScrollChanged(object? sender, ScrollChangedEventArgs e) + { + if (!_chatScrollToBottom) return; + var sv = _chatScrollViewer; + if (sv.Extent.Height > sv.Viewport.Height) + { + sv.Offset = new Vector(0, sv.Extent.Height - sv.Viewport.Height); + _chatScrollToBottom = false; + } + } + + protected virtual Control BuildRootLayout() + { + var inventoryArea = BuildMainArea(); + DockPanel.SetDock(_titleText, Dock.Top); + DockPanel.SetDock(inventoryArea, Dock.Top); + + var mainContent = new DockPanel + { + Children = { _titleText, inventoryArea, _chatScrollViewer } + }; + + return new Panel + { + Background = Brushes.Black, + Children = { mainContent, _overlayCanvas } + }; + } + + protected virtual Control BuildMainArea() + { + var infoPanel = BuildInfoPanel(); + DockPanel.SetDock(infoPanel, Dock.Right); + + return new DockPanel + { + Children = { infoPanel, BuildInventoryPanel() } + }; + } + + protected virtual Control BuildInventoryPanel() + { + var root = new StackPanel + { + Spacing = 0, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + root.Children.Add(BuildContainerSpecificArea()); + root.Children.Add(BuildSeparator()); + root.Children.Add(BuildSlotGrid(_vm.MainInventorySlots, 9)); + root.Children.Add(BuildHotbarSection()); + + return new Border + { + BorderThickness = new Thickness(1), + BorderBrush = Brushes.Gray, + Child = root, + }; + } + + protected Control BuildSeparator() + { + return new Border + { + Height = 1, + Background = Brushes.Transparent, + Margin = new Thickness(0, 0, 0, 0), + }; + } + + protected Control BuildInfoPanel() + { + return new Border + { + BorderThickness = new Thickness(1), + BorderBrush = Brushes.Gray, + Padding = new Thickness(1), + Width = 24, + Child = new StackPanel + { + Children = + { + new TextBlock { Text = Translations.tui_inventory_item_info, FontWeight = FontWeight.Bold, Foreground = Brushes.Cyan }, + _infoDetailBorder, + new TextBlock { Text = Translations.tui_inventory_held_item, FontWeight = FontWeight.Bold, Foreground = Brushes.Yellow, Margin = new Thickness(0, 1, 0, 0) }, + _cursorItemText, + new TextBlock { Text = Translations.tui_inventory_controls, FontWeight = FontWeight.Bold, Foreground = Brushes.Green, Margin = new Thickness(0, 1, 0, 0) }, + _helpText, + } + } + }; + } + + protected Control BuildHotbarSection() + { + var panel = new StackPanel { Spacing = 0 }; + + var numberRow = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + }; + for (int i = 0; i < 9; i++) + { + bool active = i == _currentHotbarSlot; + string label = active ? $"{i + 1} \u25bc" : $" {i + 1} "; + + var tb = new TextBlock + { + Text = label, + Width = _slotW, + TextAlignment = TextAlignment.Center, + Foreground = active ? Brushes.LightGreen : Brushes.DarkCyan, + FontWeight = FontWeight.Bold, + }; + _hotbarIndicators[i] = tb; + numberRow.Children.Add(tb); + } + panel.Children.Add(numberRow); + panel.Children.Add(BuildSlotGrid(_vm.HotbarSlots, 9)); + return panel; + } + + protected Control BuildSlotGrid(ObservableCollection slots, int columns) + { + var grid = new Grid(); + int rows = (slots.Count + columns - 1) / columns; + + for (int r = 0; r < rows; r++) + grid.RowDefinitions.Add(new RowDefinition(GridLength.Auto)); + for (int c = 0; c < columns; c++) + grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto)); + + for (int i = 0; i < slots.Count; i++) + { + int row = i / columns; + int col = i % columns; + var cell = CreateSlotCell(slots[i], row, col); + Grid.SetRow(cell, row); + Grid.SetColumn(cell, col); + grid.Children.Add(cell); + } + + return grid; + } + + protected static IBrush GetSlotBg(bool isEmpty, int row, int col) + { + bool isA = (row + col) % 2 == 0; + return isEmpty + ? (isA ? BrSlotEmptyA : BrSlotEmptyB) + : (isA ? BrSlotFillA : BrSlotFillB); + } + + protected Border CreateSlotCell(SlotViewModel slot, int row = 0, int col = 0) + { + var nameTb = new TextBlock + { + TextWrapping = TextWrapping.Wrap, + Padding = new Thickness(0), + Margin = new Thickness(0), + VerticalAlignment = VerticalAlignment.Top, + }; + + var countTb = new TextBlock + { + Foreground = BrCount, + FontWeight = FontWeight.Bold, + Padding = new Thickness(0), + Margin = new Thickness(0), + HorizontalAlignment = HorizontalAlignment.Right, + VerticalAlignment = VerticalAlignment.Bottom, + }; + + ApplySlotVisual(slot, nameTb, countTb); + + int r = row, c = col; + var border = new Border + { + Width = _slotW, + Height = _slotH, + Background = GetSlotBg(slot.IsEmpty, r, c), + Child = new Panel + { + Children = { nameTb, countTb }, + }, + Tag = (slot, r, c), + }; + + border.PointerPressed += OnSlotPointerPressed; + border.PointerEntered += OnSlotPointerEnter; + border.PointerExited += OnSlotPointerExit; + border.PointerMoved += OnSlotPointerMoved; + + slot.PropertyChanged += (_, _) => + { + ApplySlotVisual(slot, nameTb, countTb); + border.Background = GetSlotBg(slot.IsEmpty, r, c); + }; + + return border; + } + + protected static void ApplySlotVisual(SlotViewModel slot, TextBlock nameTb, TextBlock countTb) + { + if (slot.IsEmpty) + { + nameTb.Text = ""; + nameTb.Foreground = BrDim; + countTb.Text = ""; + } + else + { + nameTb.Text = slot.ItemDisplayText; + nameTb.Foreground = BrName; + countTb.Text = slot.CountDisplay; + } + } + + protected TextBlock MakeLabel(string text) + { + return new TextBlock + { + Text = text, + Foreground = BrEquipLbl, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(1, 0, 0, 0), + FontWeight = FontWeight.Bold, + }; + } + + #region Pointer / Keyboard interaction + + private void OnSlotPointerPressed(object? sender, PointerPressedEventArgs e) + { + if (sender is not Border border || border.Tag is not (SlotViewModel slot, int, int)) + return; + + SetHover(border, slot); + + var point = e.GetCurrentPoint(border); + bool isShift = (e.KeyModifiers & KeyModifiers.Shift) != 0; + + WindowActionType action; + if (point.Properties.IsRightButtonPressed) + action = isShift ? WindowActionType.ShiftRightClick : WindowActionType.RightClick; + else + action = isShift ? WindowActionType.ShiftClick : WindowActionType.LeftClick; + + _vm.PerformAction(slot.SlotId, action); + UpdateInfoPanel(); + UpdateHeldItemFloater(e); + OnContainerDataChanged(); + e.Handled = true; + } + + private void OnSlotPointerEnter(object? sender, PointerEventArgs e) + { + if (sender is Border b && b.Tag is (SlotViewModel slot, int, int)) + { + SetHover(b, slot); + UpdateHeldItemFloater(e); + } + } + + private void OnSlotPointerMoved(object? sender, PointerEventArgs e) + { + if (sender is Border b && b.Tag is (SlotViewModel slot, int, int)) + { + SetHover(b, slot); + UpdateHeldItemFloater(e); + } + } + + private void OnSlotPointerExit(object? sender, PointerEventArgs e) + { + if (sender is Border b && b.Tag is (SlotViewModel slot, int row, int col)) + b.Background = GetSlotBg(slot.IsEmpty, row, col); + } + + protected void SetHover(Border border, SlotViewModel slot) + { + if (_lastHoveredSlotBorder != null && _lastHoveredSlotBorder != border) + { + if (_lastHoveredSlotBorder.Tag is (SlotViewModel oldSlot, int or, int oc)) + _lastHoveredSlotBorder.Background = GetSlotBg(oldSlot.IsEmpty, or, oc); + } + + _lastHoveredSlotBorder = border; + border.Background = BrSlotHover; + _vm.HoveredSlot = slot; + UpdateInfoPanel(); + } + + protected void UpdateHeldItemFloater(PointerEventArgs e) + { + if (!_vm.HasCursorItem) + { + _heldItemFloater.IsVisible = false; + return; + } + + _heldItemFloaterName.Text = _vm.CursorItemInfo; + _heldItemFloaterCount.Text = ""; + + try + { + var pos = e.GetPosition(_overlayCanvas); + double left = pos.X + 2; + double remainingW = _termW - left - 2; + int maxW = Math.Max(8, (int)remainingW); + _heldItemFloater.MaxWidth = maxW; + Canvas.SetLeft(_heldItemFloater, left); + Canvas.SetTop(_heldItemFloater, pos.Y); + } + catch + { + _heldItemFloater.MaxWidth = 24; + Canvas.SetLeft(_heldItemFloater, 0); + Canvas.SetTop(_heldItemFloater, 0); + } + + _heldItemFloater.IsVisible = true; + } + + protected void UpdateInfoPanel() + { + _infoDetailText.Text = _vm.HoveredSlotDetailText; + + bool hasHoveredItem = _vm.HoveredSlot != null && !_vm.HoveredSlot.IsEmpty; + _infoDetailBorder.Background = hasHoveredItem ? BrInfoHighlight : Brushes.Transparent; + + if (_vm.HasCursorItem) + { + _cursorItemText.Text = _vm.CursorItemInfo; + _cursorItemText.Foreground = Brushes.Yellow; + } + else + { + _cursorItemText.Text = Translations.tui_inventory_cursor_empty; + _cursorItemText.Foreground = BrDim; + _heldItemFloater.IsVisible = false; + } + } + + protected void UpdateTitle() + { + _titleText.Text = _vm.Title; + } + + protected void CloseInventory() + { + if (_vm.WindowId != 0) + _vm.Handler.CloseInventory(_vm.WindowId); + + if (ConsoleIO.Backend is TuiConsoleBackend tuiBackend) + tuiBackend.GetView()?.HideOverlay(); + else + (Application.Current?.ApplicationLifetime as IControlledApplicationLifetime)?.Shutdown(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + switch (e.Key) + { + case Key.Escape: + case Key.E: + CloseInventory(); + e.Handled = true; + break; + + case Key.C: + if ((e.KeyModifiers & KeyModifiers.Shift) != 0 && + _vm.HoveredSlot != null && !_vm.HoveredSlot.IsEmpty) + { + _vm.PerformAction(_vm.HoveredSlot.SlotId, WindowActionType.ShiftClick); + UpdateInfoPanel(); + } + e.Handled = true; + break; + + case Key.Q: + if (_vm.HoveredSlot != null && !_vm.HoveredSlot.IsEmpty) + { + var action = (e.KeyModifiers & KeyModifiers.Control) != 0 + ? WindowActionType.DropItemStack + : WindowActionType.DropItem; + _vm.PerformAction(_vm.HoveredSlot.SlotId, action); + UpdateInfoPanel(); + } + e.Handled = true; + break; + + case Key.R: + _vm.RefreshFromContainer(); + _currentHotbarSlot = _vm.Handler.GetCurrentSlot(); + UpdateHotbarIndicators(); + UpdateInfoPanel(); + OnContainerDataChanged(); + e.Handled = true; + break; + } + } + + protected void UpdateHotbarIndicators() + { + for (int i = 0; i < 9; i++) + { + bool active = i == _currentHotbarSlot; + _hotbarIndicators[i].Text = active ? $"{i + 1} \u25bc" : $" {i + 1} "; + _hotbarIndicators[i].Foreground = active ? Brushes.LightGreen : Brushes.DarkCyan; + } + } + + #endregion + + #region Lifecycle + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + Focusable = true; + Focus(); + AddHandler(KeyDownEvent, OnTunnelKeyDown, Avalonia.Interactivity.RoutingStrategies.Tunnel); + SizeChanged += OnViewSizeChanged; + } + + private void OnTunnelKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + CloseInventory(); + e.Handled = true; + } + } + + private void OnViewSizeChanged(object? sender, SizeChangedEventArgs e) + { + int newW, newH; + try + { + newW = System.Console.WindowWidth; + newH = System.Console.WindowHeight; + } + catch { return; } + + if (newW == _lastTermW && newH == _lastTermH) return; + + _vm.RefreshFromContainer(); + _currentHotbarSlot = _vm.Handler.GetCurrentSlot(); + RebuildUi(); + Focus(); + } + + protected override void OnGotFocus(GotFocusEventArgs e) + { + base.OnGotFocus(e); + Focusable = true; + } + + #endregion + + public static bool HasTuiSupport(ContainerType type) + { + return type switch + { + ContainerType.PlayerInventory => true, + ContainerType.Generic_9x1 => true, + ContainerType.Generic_9x2 => true, + ContainerType.Generic_9x3 => true, + ContainerType.Generic_9x4 => true, + ContainerType.Generic_9x5 => true, + ContainerType.Generic_9x6 => true, + ContainerType.Generic_3x3 => true, + ContainerType.Crafter => true, + ContainerType.ShulkerBox => true, + ContainerType.Crafting => true, + ContainerType.Furnace => true, + ContainerType.BlastFurnace => true, + ContainerType.Smoker => true, + ContainerType.Enchantment => true, + ContainerType.BrewingStand => true, + ContainerType.Hopper => true, + ContainerType.Grindstone => true, + _ => false, + }; + } + + public static ContainerViewBase CreateView(ContainerType type, McClient handler, int windowId) + { + return type switch + { + ContainerType.PlayerInventory => new PlayerInventoryView(handler, windowId), + ContainerType.Generic_9x3 or ContainerType.ShulkerBox => new GridContainerView(handler, windowId, type, 3, 9), + ContainerType.Generic_9x6 => new GridContainerView(handler, windowId, type, 6, 9), + ContainerType.Generic_3x3 or ContainerType.Crafter + => new GridContainerView(handler, windowId, type, 3, 3), + ContainerType.Generic_9x1 => new GridContainerView(handler, windowId, type, 1, 9), + ContainerType.Generic_9x2 => new GridContainerView(handler, windowId, type, 2, 9), + ContainerType.Generic_9x4 => new GridContainerView(handler, windowId, type, 4, 9), + ContainerType.Generic_9x5 => new GridContainerView(handler, windowId, type, 5, 9), + ContainerType.Crafting => new CraftingView(handler, windowId), + ContainerType.Furnace or ContainerType.BlastFurnace or ContainerType.Smoker + => new FurnaceView(handler, windowId, type), + ContainerType.Enchantment => new EnchantingTableView(handler, windowId), + ContainerType.BrewingStand => new BrewingStandView(handler, windowId), + ContainerType.Hopper => new HopperView(handler, windowId), + ContainerType.Grindstone => new GrindstoneView(handler, windowId), + _ => throw new ArgumentException($"No TUI view for {type}"), + }; + } + } +} diff --git a/MinecraftClient/Tui/ContainerViewModel.cs b/MinecraftClient/Tui/ContainerViewModel.cs new file mode 100644 index 00000000..1effcdae --- /dev/null +++ b/MinecraftClient/Tui/ContainerViewModel.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using MinecraftClient.Inventory; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +namespace MinecraftClient.Tui +{ + public class ContainerViewModel : INotifyPropertyChanged + { + private SlotViewModel? _hoveredSlot; + private string _title = ""; + private string _statusText = ""; + private string _cursorItemInfo = ""; + private bool _hasCursorItem; + + public McClient Handler { get; } + public int WindowId { get; } + public ContainerType ContainerType { get; } + + public ObservableCollection ContainerSlots { get; } = new(); + public ObservableCollection MainInventorySlots { get; } = new(); + public ObservableCollection HotbarSlots { get; } = new(); + + public string Title + { + get => _title; + set { _title = value; OnPropertyChanged(); } + } + + public string StatusText + { + get => _statusText; + set { _statusText = value; OnPropertyChanged(); } + } + + public string CursorItemInfo + { + get => _cursorItemInfo; + set { _cursorItemInfo = value; OnPropertyChanged(); } + } + + public bool HasCursorItem + { + get => _hasCursorItem; + set { _hasCursorItem = value; OnPropertyChanged(); } + } + + public SlotViewModel? HoveredSlot + { + get => _hoveredSlot; + set + { + if (_hoveredSlot != null) + _hoveredSlot.IsHovered = false; + _hoveredSlot = value; + if (_hoveredSlot != null) + _hoveredSlot.IsHovered = true; + OnPropertyChanged(); + OnPropertyChanged(nameof(HoveredSlotDetailText)); + } + } + + public string HoveredSlotDetailText + { + get + { + if (_hoveredSlot == null) + return Translations.tui_inventory_hover_hint; + + if (_hoveredSlot.IsEmpty) + return $"Slot #{_hoveredSlot.SlotId}\n{Translations.tui_inventory_slot_empty}"; + + var sb = new StringBuilder(); + sb.AppendLine(_hoveredSlot.ItemTypeName); + sb.AppendLine(string.Format(Translations.tui_inventory_slot_detail, _hoveredSlot.SlotId, _hoveredSlot.ItemCount)); + + var item = _hoveredSlot.RawItem; + if (item != null) + AppendItemExtras(sb, item); + + return sb.ToString().TrimEnd(); + } + } + + protected Dictionary SlotMap { get; } = new(); + + public ContainerViewModel(McClient handler, int windowId, ContainerType containerType) + { + Handler = handler; + WindowId = windowId; + ContainerType = containerType; + + InitializeSlots(); + RefreshFromContainer(); + } + + public void SetSlotDisplayParams(int maxWidth, int maxLines) + { + foreach (var kvp in SlotMap) + { + kvp.Value.NameMaxWidth = maxWidth; + kvp.Value.NameMaxLines = maxLines; + } + RefreshFromContainer(); + } + + protected virtual void InitializeSlots() + { + SlotMap.Clear(); + + int slotCount = ContainerType.SlotCount(); + if (slotCount == 0) return; + + int playerInvStart = slotCount - 36; + + for (int i = 0; i < playerInvStart; i++) + { + var slot = new SlotViewModel(i); + ContainerSlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = playerInvStart; i < playerInvStart + 27; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = playerInvStart + 27; i < slotCount; i++) + { + int hotbarIdx = i - (playerInvStart + 27); + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + + public virtual void RefreshFromContainer() + { + Inventory.Container? container = Handler.GetInventory(WindowId); + if (container == null) + { + StatusText = Translations.tui_inventory_container_not_found; + return; + } + + Title = string.Format(Translations.tui_inventory_title, WindowId, container.Title); + + foreach (var kvp in SlotMap) + { + Item? item = container.Items.TryGetValue(kvp.Key, out var it) ? it : null; + kvp.Value.Update(item); + } + + UpdateCursorItem(container); + int itemCount = 0; + foreach (var kvp in container.Items) + { + if (kvp.Key >= 0 && !kvp.Value.IsEmpty) + itemCount++; + } + StatusText = string.Format(Translations.tui_inventory_item_count, itemCount); + + OnPropertyChanged(nameof(HoveredSlotDetailText)); + } + + protected void UpdateCursorItem(Inventory.Container _) + { + var playerInv = Handler.GetInventory(0); + if (playerInv != null && playerInv.Items.TryGetValue(-1, out var cursorItem) && !cursorItem.IsEmpty) + { + CursorItemInfo = FormatItemDetail(cursorItem); + HasCursorItem = true; + } + else + { + CursorItemInfo = ""; + HasCursorItem = false; + } + } + + protected static string FormatItemDetail(Item item) + { + var sb = new StringBuilder(); + sb.AppendLine($"x{item.Count} {item.GetTypeString()}"); + AppendItemExtras(sb, item); + if (sb.Length > 0 && sb[sb.Length - 1] == '\n') + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + + private static void AppendItemExtras(StringBuilder sb, Item item) + { + int damage = item.Damage; + if (damage != 0) + { + int maxDamage = item.Components?.OfType().FirstOrDefault()?.MaxDamage ?? 0; + if (maxDamage > 0) + sb.AppendLine($"{Translations.tui_inventory_durability}: {maxDamage - damage}/{maxDamage}"); + else + sb.AppendLine($"{Translations.cmd_inventory_damage}: {damage}"); + } + + try + { + var enchList = item.EnchantmentList; + if (enchList is not null) + { + bool isFirstEnchantment = true; + foreach (var ench in enchList) + { + string name = EnchantmentMapping.GetEnchantmentName(ench.Type); + string level = EnchantmentMapping.ConvertLevelToRomanNumbers(ench.Level); + if (isFirstEnchantment) + { + isFirstEnchantment = false; + sb.Append($"{name} {level}"); + } + else + { + sb.Append($" | {name} {level}"); + } + } + } + else if (item.NBT is not null && + (item.NBT.TryGetValue("Enchantments", out object? enchantments) || + item.NBT.TryGetValue("StoredEnchantments", out enchantments))) + { + bool isFirstEnchantment = true; + foreach (Dictionary enchantment in (object[])enchantments) + { + short level = (short)enchantment["lvl"]; + string id = ((string)enchantment["id"]).Replace(':', '.'); + string name = Protocol.Message.ChatParser.TranslateString("enchantment." + id) ?? id; + string levelStr = Protocol.Message.ChatParser.TranslateString("enchantment.level." + level) ?? level.ToString(); + if (isFirstEnchantment) + { + isFirstEnchantment = false; + sb.Append($"{name} {levelStr}"); + } + else + { + sb.Append($" | {name} {levelStr}"); + } + } + } + } + catch { } + } + + public bool PerformAction(int slotId, WindowActionType action) + { + bool result = Handler.DoWindowAction(WindowId, slotId, action); + RefreshFromContainer(); + return result; + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string? name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + } +} diff --git a/MinecraftClient/Tui/CraftingView.cs b/MinecraftClient/Tui/CraftingView.cs new file mode 100644 index 00000000..dbbfc508 --- /dev/null +++ b/MinecraftClient/Tui/CraftingView.cs @@ -0,0 +1,112 @@ +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class CraftingView : ContainerViewBase + { + private readonly CraftingViewModel _craftVm; + + public CraftingView(McClient handler, int windowId) + : base(new CraftingViewModel(handler, windowId)) + { + _craftVm = (CraftingViewModel)_vm; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 3 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var row = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + var gridPanel = new StackPanel { Spacing = 0 }; + gridPanel.Children.Add(new TextBlock + { + Text = Translations.tui_crafting_grid, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + gridPanel.Children.Add(BuildSlotGrid(_craftVm.CraftingGridSlots, 3)); + row.Children.Add(gridPanel); + + row.Children.Add(new TextBlock + { + Text = " \u2192 ", + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + VerticalAlignment = VerticalAlignment.Center, + }); + + var outPanel = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + outPanel.Children.Add(new TextBlock + { + Text = Translations.tui_inventory_output, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + outPanel.Children.Add(CreateSlotCell(_craftVm.OutputSlot, 0, 0)); + row.Children.Add(outPanel); + + return row; + } + } + + public class CraftingViewModel : ContainerViewModel + { + public ObservableCollection CraftingGridSlots { get; } = new(); + public SlotViewModel OutputSlot { get; private set; } = null!; + + public CraftingViewModel(McClient handler, int windowId) + : base(handler, windowId, ContainerType.Crafting) + { + OutputSlot = SlotMap[0]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + var output = new SlotViewModel(0); + SlotMap[0] = output; + + for (int i = 1; i <= 9; i++) + { + var slot = new SlotViewModel(i); + CraftingGridSlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 10; i <= 36; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 37; i <= 45; i++) + { + int hotbarIdx = i - 37; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + } +} diff --git a/MinecraftClient/Tui/DialogTuiHost.cs b/MinecraftClient/Tui/DialogTuiHost.cs new file mode 100644 index 00000000..f94370e6 --- /dev/null +++ b/MinecraftClient/Tui/DialogTuiHost.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; +using MinecraftClient.Dialogs; + +namespace MinecraftClient.Tui; + +internal interface IOverlayCloseHandler +{ + bool TryCloseByUser(); +} + +public static class DialogTuiHost +{ + public static bool TryOpen(McClient handler, DialogInstance instance, bool force) + { + if (ConsoleIO.Backend is not TuiConsoleBackend) + return false; + + Dispatcher.UIThread.Post(() => + { + var view = TuiConsoleBackend.Instance?.GetView(); + if (view is null) + return; + + if (view.HasOverlay && view.OverlayContent is not DialogView && !force) + { + handler.Log.Info(Translations.dialog_tui_pending); + return; + } + + view.ShowOverlay(new DialogView(handler, instance)); + }); + + return true; + } + + public static void CloseCurrent() + { + if (ConsoleIO.Backend is not TuiConsoleBackend) + return; + + Dispatcher.UIThread.Post(() => + { + var view = TuiConsoleBackend.Instance?.GetView(); + if (view?.OverlayContent is DialogView) + view.HideOverlay(); + }); + } +} + +internal sealed class DialogView : Border, IOverlayCloseHandler +{ + private static readonly Color AccentColor = Color.FromRgb(80, 180, 255); + private static readonly Color BorderColor = Color.FromRgb(70, 70, 70); + private static readonly Color SectionBg = Color.FromRgb(20, 20, 20); + private static readonly Color InputBg = Color.FromRgb(35, 35, 35); + + private readonly McClient _handler; + private readonly DialogInstance _instance; + private readonly TextBlock _status; + private readonly Dictionary _inputControls = new(StringComparer.Ordinal); + private readonly StackPanel _inputsPanel; + private readonly WrapPanel _buttonsPanel; + + public DialogView(McClient handler, DialogInstance instance) + { + _handler = handler; + _instance = instance; + + BorderBrush = new SolidColorBrush(BorderColor); + BorderThickness = new Thickness(1); + Background = new SolidColorBrush(Color.FromRgb(12, 12, 12)); + Padding = new Thickness(2); + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + Focusable = true; + + _status = new TextBlock + { + Foreground = Brushes.Gray, + TextWrapping = TextWrapping.Wrap, + Margin = new Thickness(0, 1, 0, 0) + }; + + _inputsPanel = new StackPanel { Spacing = 0, Margin = new Thickness(0) }; + _buttonsPanel = new WrapPanel { Orientation = Orientation.Horizontal }; + + Child = BuildContent(); + + AttachedToVisualTree += (_, _) => + { + AddHandler(KeyDownEvent, OnTunnelKeyDown, RoutingStrategies.Tunnel, handledEventsToo: true); + FocusFirstInput(); + Focus(); + }; + DetachedFromVisualTree += (_, _) => RemoveHandler(KeyDownEvent, OnTunnelKeyDown); + } + + public bool TryCloseByUser() + { + if (!_instance.Definition.CanCloseWithEscape && _instance.Definition.CancelAction is null) + { + SetStatus(Translations.dialog_cannot_cancel); + return false; + } + + var result = _handler.Dialogs.Cancel(); + SetStatus(result.Message); + if (result.Success) + CloseIfInactive(); + + return false; + } + + private Control BuildContent() + { + var root = new DockPanel { Margin = new Thickness(0) }; + + var scroll = new ScrollViewer + { + HorizontalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Disabled, + VerticalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto + }; + + var main = new StackPanel { Spacing = 0, Margin = new Thickness(0) }; + + // Title + main.Children.Add(McColorParser.CreateColoredTextBlock(_instance.Definition.DisplayTitle())); + + // Separator + main.Children.Add(new Border + { + Height = 1, + Background = new SolidColorBrush(BorderColor), + Margin = new Thickness(0, 1, 0, 1) + }); + + // Body + foreach (var body in _instance.Definition.Body) + { + if (string.IsNullOrWhiteSpace(body.Text)) + continue; + main.Children.Add(McColorParser.CreateColoredTextBlock(body.Text)); + } + + // Build action buttons + EnsureActionButtons(); + + // Inputs + foreach (var input in _instance.Definition.Inputs) + main.Children.Add(BuildInput(input)); + + // Action buttons + if (_buttonsPanel.Children.Count > 0) + main.Children.Add(_buttonsPanel); + + // Cancel hint + if (_instance.Definition.CancelAction is not null || _instance.Definition.CanCloseWithEscape) + { + main.Children.Add(new TextBlock + { + Text = Translations.dialog_render_cancel_hint, + Foreground = new SolidColorBrush(Color.FromRgb(120, 120, 120)), + TextWrapping = TextWrapping.Wrap + }); + } + + main.Children.Add(new TextBlock + { + Text = Translations.dialog_render_help_hint, + Foreground = new SolidColorBrush(Color.FromRgb(120, 120, 120)), + TextWrapping = TextWrapping.Wrap + }); + + main.Children.Add(_status); + scroll.Content = main; + root.Children.Add(scroll); + + return root; + } + + private void EnsureActionButtons() + { + if (_buttonsPanel.Children.Count > 0) + return; + + foreach (var action in _instance.Definition.Actions) + { + var btn = new Button + { + Content = McColorParser.CreateColoredTextBlock(action.Label), + Padding = new Thickness(1), + BorderThickness = new Thickness(1), + BorderBrush = new SolidColorBrush(Color.FromRgb(60, 60, 60)), + Background = new SolidColorBrush(Color.FromRgb(40, 40, 40)), + Margin = new Thickness(0, 0, 1, 1) + }; + btn.Click += (_, _) => Click(action.Index); + _buttonsPanel.Children.Add(btn); + } + + if (_instance.Definition.CancelAction is not null || _instance.Definition.CanCloseWithEscape) + { + var cancel = new Button + { + Content = McColorParser.CreateColoredTextBlock(Translations.tui_dialog_cancel), + Padding = new Thickness(1), + BorderThickness = new Thickness(1), + BorderBrush = new SolidColorBrush(Color.FromRgb(80, 40, 40)), + Background = new SolidColorBrush(Color.FromRgb(50, 25, 25)) + }; + cancel.Click += (_, _) => TryCloseByUser(); + _buttonsPanel.Children.Add(cancel); + } + } + + private Control BuildInput(DialogInput input) + { + _instance.Values.TryGetValue(input.Key, out var value); + value ??= input.InitialValue; + + var panel = new StackPanel { Spacing = 0, Margin = new Thickness(0) }; + + if (input.LabelVisible && !string.IsNullOrWhiteSpace(input.Label)) + panel.Children.Add(McColorParser.CreateColoredTextBlock(input.Label)); + + Control inner = input.Kind switch + { + DialogInputKind.Boolean => BuildBooleanInput(value, input), + DialogInputKind.SingleOption => BuildOptionInput(input, value), + DialogInputKind.NumberRange => BuildNumberInput(input, value), + _ => BuildTextInput(input, value) + }; + + _inputControls[input.Key] = inner; + + if (input.Kind == DialogInputKind.Boolean) + { + panel.Children.Add(inner); + } + else + { + panel.Children.Add(new Border + { + Background = new SolidColorBrush(InputBg), + BorderBrush = new SolidColorBrush(Color.FromRgb(55, 55, 55)), + BorderThickness = new Thickness(1), + Padding = new Thickness(1), + Child = inner + }); + } + + return panel; + } + + private Control BuildTextInput(DialogInput input, string value) + { + var tb = new TextBox + { + Text = value, + AcceptsReturn = input.Multiline, + TextWrapping = input.Multiline ? TextWrapping.Wrap : TextWrapping.NoWrap, + MaxLength = input.MaxLength, + Foreground = Brushes.White, + Background = new SolidColorBrush(InputBg), + BorderThickness = new Thickness(0), + Padding = new Thickness(0) + }; + + return tb; + } + + private Control BuildBooleanInput(string value, DialogInput input) + { + return new CheckBox + { + IsChecked = value.Equals("true", StringComparison.OrdinalIgnoreCase), + Foreground = Brushes.White, + Padding = new Thickness(0) + }; + } + + private Control BuildOptionInput(DialogInput input, string value) + { + var combo = new ComboBox + { + ItemsSource = input.Options ?? [], + Foreground = Brushes.White, + Background = new SolidColorBrush(InputBg), + BorderThickness = new Thickness(0), + Padding = new Thickness(1, 0) + }; + + combo.SelectedItem = input.Options?.FirstOrDefault(option => option.Id.Equals(value, StringComparison.Ordinal)) + ?? input.Options?.FirstOrDefault(); + + return combo; + } + + private Control BuildNumberInput(DialogInput input, string value) + { + double numValue = double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed) + ? parsed + : input.InitialNumber ?? input.Start; + + double min = Math.Min(input.Start, input.End); + double max = Math.Max(input.Start, input.End); + + var panel = new DockPanel { LastChildFill = true }; + + var slider = new Slider + { + Minimum = min, + Maximum = max, + Value = numValue, + TickFrequency = input.Step ?? 1, + IsSnapToTickEnabled = input.Step is not null, + Foreground = new SolidColorBrush(AccentColor) + }; + + var label = new TextBlock + { + Text = numValue.ToString(CultureInfo.InvariantCulture), + Foreground = Brushes.White, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(2, 0, 0, 0), + MinWidth = 16 + }; + + slider.PropertyChanged += (_, e) => + { + if (e.Property == Slider.ValueProperty) + label.Text = ((float)slider.Value).ToString(CultureInfo.InvariantCulture); + }; + + DockPanel.SetDock(label, Dock.Right); + panel.Children.Add(slider); + panel.Children.Add(label); + + return panel; + } + + private void Click(int index) + { + if (!StoreInputs()) + return; + + var result = _handler.Dialogs.Click(index); + SetStatus(result.Message); + if (result.Success) + CloseIfInactive(); + } + + private bool StoreInputs() + { + foreach (var input in _instance.Definition.Inputs) + { + if (!_inputControls.TryGetValue(input.Key, out var control)) + continue; + + var value = control switch + { + TextBox textBox => textBox.Text ?? string.Empty, + CheckBox checkBox => checkBox.IsChecked == true ? "true" : "false", + ComboBox comboBox when comboBox.SelectedItem is DialogOption option => option.Id, + Slider slider => NumberToString((float)slider.Value), + _ => input.InitialValue + }; + + var result = _handler.Dialogs.SetInput(input.Key, value); + if (!result.Success) + { + SetStatus(result.Message); + return false; + } + } + + return true; + } + + private void FocusFirstInput() + { + var first = _inputControls.Values.FirstOrDefault(); + if (first is TextBox tb) + { + tb.Focus(); + tb.SelectAll(); + } + else + { + first?.Focus(); + } + } + + private void CloseIfInactive() + { + var current = _handler.Dialogs.Current; + if (current is null) + { + DialogTuiHost.CloseCurrent(); + return; + } + + if (current.Revision != _instance.Revision) + DialogTuiHost.TryOpen(_handler, current, force: true); + } + + private void SetStatus(string text) + { + _status.Text = text; + } + + private void OnTunnelKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + TryCloseByUser(); + e.Handled = true; + return; + } + + if (e.Key == Key.Enter) + { + var focused = TopLevel.GetTopLevel(this)?.FocusManager?.GetFocusedElement(); + if (focused is TextBox && _instance.Definition.Actions.Count > 0) + { + Click(_instance.Definition.Actions[0].Index); + e.Handled = true; + } + } + } + + private static string NumberToString(float value) + { + var integer = (int)value; + return integer == value + ? integer.ToString(CultureInfo.InvariantCulture) + : value.ToString(CultureInfo.InvariantCulture); + } +} diff --git a/MinecraftClient/Tui/EnchantingTableView.cs b/MinecraftClient/Tui/EnchantingTableView.cs new file mode 100644 index 00000000..c1386d7a --- /dev/null +++ b/MinecraftClient/Tui/EnchantingTableView.cs @@ -0,0 +1,198 @@ +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class EnchantingTableView : ContainerViewBase + { + private readonly EnchantingViewModel _enchantVm; + private readonly TextBlock[] _enchantNameLabels = new TextBlock[3]; + private readonly TextBlock[] _enchantCostLabels = new TextBlock[3]; + + public EnchantingTableView(McClient handler, int windowId) + : base(new EnchantingViewModel(handler, windowId)) + { + _enchantVm = (EnchantingViewModel)_vm; + Initialize(); + } + + private void RefreshEnchantOptions() + { + var container = _vm.Handler.GetInventory(_vm.WindowId); + if (container == null) return; + + int protocolVersion = _vm.Handler.GetProtocolVersion(); + + for (int i = 0; i < 3; i++) + { + if (_enchantNameLabels[i] == null) continue; + + short levelReq = container.Properties.TryGetValue(i, out var lr) ? lr : (short)0; + short enchantId = container.Properties.TryGetValue(i + 4, out var eid) ? eid : (short)-1; + short enchantLevel = container.Properties.TryGetValue(i + 7, out var el) ? el : (short)0; + + if (levelReq > 0 && enchantId >= 0) + { + try + { + var enchant = EnchantmentMapping.GetEnchantmentById(protocolVersion, enchantId); + string name = EnchantmentMapping.GetEnchantmentName(enchant); + string roman = EnchantmentMapping.ConvertLevelToRomanNumbers(enchantLevel); + _enchantNameLabels[i].Text = $"{name} {roman}"; + _enchantCostLabels[i].Text = $" ({levelReq})"; + } + catch + { + _enchantNameLabels[i].Text = string.Format(Translations.tui_enchanting_option_slot, i + 1); + _enchantCostLabels[i].Text = levelReq > 0 ? $" ({levelReq})" : ""; + } + } + else + { + _enchantNameLabels[i].Text = string.Format(Translations.tui_enchanting_option_slot, i + 1); + _enchantCostLabels[i].Text = ""; + } + } + } + + protected override void OnContainerDataChanged() + { + RefreshEnchantOptions(); + } + + protected override int GetTotalSlotRows() + { + return 3 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var panel = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Spacing = 0, + }; + + var slotsCol = new StackPanel + { + Spacing = 0, + VerticalAlignment = VerticalAlignment.Center, + }; + + slotsCol.Children.Add(new TextBlock + { + Text = Translations.tui_enchanting_item, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + slotsCol.Children.Add(CreateSlotCell(_enchantVm.ItemSlot, 0, 0)); + + slotsCol.Children.Add(new TextBlock + { + Text = Translations.tui_enchanting_lapis, + Foreground = new SolidColorBrush(Color.FromRgb(60, 80, 200)), + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + slotsCol.Children.Add(CreateSlotCell(_enchantVm.LapisSlot, 1, 0)); + + panel.Children.Add(slotsCol); + + var optionsCol = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(2, 0, 0, 0), + }; + + optionsCol.Children.Add(new TextBlock + { + Text = Translations.tui_enchanting_options, + Foreground = Brushes.Magenta, + FontWeight = FontWeight.Bold, + }); + + int optionWidth = System.Math.Max(_slotW * 4, 30); + + for (int i = 0; i < 3; i++) + { + var nameLabel = new TextBlock + { + Text = string.Format(Translations.tui_enchanting_option_slot, i + 1), + Foreground = new SolidColorBrush(Color.FromRgb(100, 200, 70)), + TextWrapping = TextWrapping.NoWrap, + }; + _enchantNameLabels[i] = nameLabel; + + var costLabel = new TextBlock + { + Foreground = new SolidColorBrush(Color.FromRgb(100, 200, 70)), + FontWeight = FontWeight.Bold, + VerticalAlignment = VerticalAlignment.Center, + }; + _enchantCostLabels[i] = costLabel; + + var content = new DockPanel(); + DockPanel.SetDock(costLabel, Dock.Right); + content.Children.Add(costLabel); + content.Children.Add(nameLabel); + + optionsCol.Children.Add(new Border + { + Background = new SolidColorBrush(Color.FromRgb(55, 50, 40)), + MinWidth = optionWidth, + MinHeight = _slotH, + Padding = new Thickness(1, 0), + Child = content, + }); + } + + RefreshEnchantOptions(); + + panel.Children.Add(optionsCol); + + return panel; + } + } + + public class EnchantingViewModel : ContainerViewModel + { + public SlotViewModel ItemSlot { get; private set; } = null!; + public SlotViewModel LapisSlot { get; private set; } = null!; + + public EnchantingViewModel(McClient handler, int windowId) + : base(handler, windowId, ContainerType.Enchantment) + { + ItemSlot = SlotMap[0]; + LapisSlot = SlotMap[1]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + SlotMap[0] = new SlotViewModel(0); + SlotMap[1] = new SlotViewModel(1); + + for (int i = 2; i <= 28; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 29; i <= 37; i++) + { + int hotbarIdx = i - 29; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + } +} diff --git a/MinecraftClient/Tui/FurnaceView.cs b/MinecraftClient/Tui/FurnaceView.cs new file mode 100644 index 00000000..cde8054c --- /dev/null +++ b/MinecraftClient/Tui/FurnaceView.cs @@ -0,0 +1,133 @@ +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class FurnaceView : ContainerViewBase + { + private readonly FurnaceViewModel _furnaceVm; + + public FurnaceView(McClient handler, int windowId, ContainerType type) + : base(new FurnaceViewModel(handler, windowId, type)) + { + _furnaceVm = (FurnaceViewModel)_vm; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 3 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var panel = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Spacing = 0, + }; + + var leftCol = new StackPanel + { + Spacing = 0, + VerticalAlignment = VerticalAlignment.Center, + }; + + leftCol.Children.Add(new TextBlock + { + Text = Translations.tui_furnace_input, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + leftCol.Children.Add(CreateSlotCell(_furnaceVm.InputSlot, 0, 0)); + + leftCol.Children.Add(new TextBlock + { + Text = "\u2592\u2592\u2592", + Foreground = new SolidColorBrush(Color.FromRgb(180, 100, 40)), + HorizontalAlignment = HorizontalAlignment.Center, + }); + + leftCol.Children.Add(new TextBlock + { + Text = Translations.tui_furnace_fuel, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + leftCol.Children.Add(CreateSlotCell(_furnaceVm.FuelSlot, 1, 0)); + + panel.Children.Add(leftCol); + + panel.Children.Add(new TextBlock + { + Text = " \u2192 ", + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + VerticalAlignment = VerticalAlignment.Center, + }); + + var rightCol = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + rightCol.Children.Add(new TextBlock + { + Text = Translations.tui_furnace_output, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + rightCol.Children.Add(CreateSlotCell(_furnaceVm.OutputSlot, 0, 1)); + + panel.Children.Add(rightCol); + + return panel; + } + } + + public class FurnaceViewModel : ContainerViewModel + { + public SlotViewModel InputSlot { get; private set; } = null!; + public SlotViewModel FuelSlot { get; private set; } = null!; + public SlotViewModel OutputSlot { get; private set; } = null!; + + public FurnaceViewModel(McClient handler, int windowId, ContainerType type) + : base(handler, windowId, type) + { + InputSlot = SlotMap[0]; + FuelSlot = SlotMap[1]; + OutputSlot = SlotMap[2]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + SlotMap[0] = new SlotViewModel(0); + SlotMap[1] = new SlotViewModel(1); + SlotMap[2] = new SlotViewModel(2); + + for (int i = 3; i <= 29; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 30; i <= 38; i++) + { + int hotbarIdx = i - 30; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + } +} diff --git a/MinecraftClient/Tui/GridContainerView.cs b/MinecraftClient/Tui/GridContainerView.cs new file mode 100644 index 00000000..c2da293b --- /dev/null +++ b/MinecraftClient/Tui/GridContainerView.cs @@ -0,0 +1,31 @@ +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class GridContainerView : ContainerViewBase + { + private readonly int _gridRows; + private readonly int _gridCols; + + public GridContainerView(McClient handler, int windowId, ContainerType type, int rows, int cols) + : base(new ContainerViewModel(handler, windowId, type)) + { + _gridRows = rows; + _gridCols = cols; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return _gridRows + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + return BuildSlotGrid(_vm.ContainerSlots, _gridCols); + } + } +} diff --git a/MinecraftClient/Tui/GrindstoneView.cs b/MinecraftClient/Tui/GrindstoneView.cs new file mode 100644 index 00000000..04a283a6 --- /dev/null +++ b/MinecraftClient/Tui/GrindstoneView.cs @@ -0,0 +1,126 @@ +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class GrindstoneView : ContainerViewBase + { + private readonly GrindstoneViewModel _grindVm; + + public GrindstoneView(McClient handler, int windowId) + : base(new GrindstoneViewModel(handler, windowId)) + { + _grindVm = (GrindstoneViewModel)_vm; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 2 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var row = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Spacing = 0, + }; + + var inputCol = new StackPanel + { + Spacing = 0, + VerticalAlignment = VerticalAlignment.Center, + }; + + inputCol.Children.Add(new TextBlock + { + Text = Translations.tui_grindstone_input1, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + inputCol.Children.Add(CreateSlotCell(_grindVm.Input1Slot, 0, 0)); + + inputCol.Children.Add(new TextBlock + { + Text = Translations.tui_grindstone_input2, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + inputCol.Children.Add(CreateSlotCell(_grindVm.Input2Slot, 1, 0)); + + row.Children.Add(inputCol); + + row.Children.Add(new TextBlock + { + Text = "=>", + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + VerticalAlignment = VerticalAlignment.Center, + Padding = new Thickness(1, 0), + }); + + var outCol = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + outCol.Children.Add(new TextBlock + { + Text = Translations.tui_inventory_output, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + outCol.Children.Add(CreateSlotCell(_grindVm.OutputSlot, 0, 1)); + row.Children.Add(outCol); + + return row; + } + } + + public class GrindstoneViewModel : ContainerViewModel + { + public SlotViewModel Input1Slot { get; private set; } = null!; + public SlotViewModel Input2Slot { get; private set; } = null!; + public SlotViewModel OutputSlot { get; private set; } = null!; + + public GrindstoneViewModel(McClient handler, int windowId) + : base(handler, windowId, ContainerType.Grindstone) + { + Input1Slot = SlotMap[0]; + Input2Slot = SlotMap[1]; + OutputSlot = SlotMap[2]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + SlotMap[0] = new SlotViewModel(0); + SlotMap[1] = new SlotViewModel(1); + SlotMap[2] = new SlotViewModel(2); + + for (int i = 3; i <= 29; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 30; i <= 38; i++) + { + int hotbarIdx = i - 30; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + } + } +} diff --git a/MinecraftClient/Tui/HopperView.cs b/MinecraftClient/Tui/HopperView.cs new file mode 100644 index 00000000..bb69b2ae --- /dev/null +++ b/MinecraftClient/Tui/HopperView.cs @@ -0,0 +1,30 @@ +using Avalonia.Controls; +using Avalonia.Layout; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class HopperView : ContainerViewBase + { + public HopperView(McClient handler, int windowId) + : base(new ContainerViewModel(handler, windowId, ContainerType.Hopper)) + { + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 1 + 3 + 1; + } + + protected override Control BuildContainerSpecificArea() + { + var grid = BuildSlotGrid(_vm.ContainerSlots, 5); + return new StackPanel + { + HorizontalAlignment = HorizontalAlignment.Center, + Children = { grid }, + }; + } + } +} diff --git a/MinecraftClient/Tui/IconGridBuilder.cs b/MinecraftClient/Tui/IconGridBuilder.cs new file mode 100644 index 00000000..3d13a8e2 --- /dev/null +++ b/MinecraftClient/Tui/IconGridBuilder.cs @@ -0,0 +1,125 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + internal static class IconGridBuilder + { + internal static Grid BuildFromRgba(byte[] rgba, int srcWidth, int srcHeight, int displaySize) + { + int cellCols = displaySize; + int cellRows = displaySize / 2; + + var grid = new Grid(); + for (int c = 0; c < cellCols; c++) + grid.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Auto)); + for (int r = 0; r < cellRows; r++) + grid.RowDefinitions.Add(new RowDefinition(1, GridUnitType.Auto)); + + for (int row = 0; row < cellRows; row++) + { + for (int col = 0; col < cellCols; col++) + { + int topPixelY = row * 2; + int bottomPixelY = row * 2 + 1; + + var topColor = SamplePixel(rgba, srcWidth, srcHeight, col, topPixelY, cellCols, displaySize); + var bottomColor = SamplePixel(rgba, srcWidth, srcHeight, col, bottomPixelY, cellCols, displaySize); + + var cell = new TextBlock + { + Text = "\u2580", + Foreground = new SolidColorBrush(topColor), + Background = new SolidColorBrush(bottomColor), + Padding = new Thickness(0), + Margin = new Thickness(0), + }; + + Grid.SetRow(cell, row); + Grid.SetColumn(cell, col); + grid.Children.Add(cell); + } + } + + return grid; + } + + internal static Grid BuildFromBase64(string base64Data, int displaySize) + { + byte[] imageBytes; + try + { + imageBytes = Convert.FromBase64String(base64Data); + } + catch + { + return new Grid(); + } + + return BuildFromImageBytes(imageBytes, displaySize) ?? new Grid(); + } + + internal static Grid? BuildFromImageBytes(byte[] imageBytes, int displaySize) + { + int srcWidth, srcHeight; + byte[] rgba; + try + { + (srcWidth, srcHeight, rgba) = DecodeImageToRgba(imageBytes); + } + catch + { + return null; + } + + return BuildFromRgba(rgba, srcWidth, srcHeight, displaySize); + } + + internal static (int Width, int Height, byte[] Rgba) DecodeImageToRgba(byte[] imageData) + { + using var image = new ImageMagick.MagickImage(imageData); + int w = (int)image.Width; + int h = (int)image.Height; + + using var pixels = image.GetPixelsUnsafe(); + var rgba = new byte[w * h * 4]; + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + var pixel = pixels.GetPixel(x, y)!; + int idx = (y * w + x) * 4; + var color = pixel.ToColor()!; + rgba[idx] = (byte)(color.R >> 8); + rgba[idx + 1] = (byte)(color.G >> 8); + rgba[idx + 2] = (byte)(color.B >> 8); + rgba[idx + 3] = (byte)(color.A >> 8); + } + } + + return (w, h, rgba); + } + + private static Color SamplePixel(byte[] rgba, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) + { + int srcX = dstX * srcW / dstW; + int srcY = dstY * srcH / dstH; + srcX = Math.Clamp(srcX, 0, srcW - 1); + srcY = Math.Clamp(srcY, 0, srcH - 1); + + int idx = (srcY * srcW + srcX) * 4; + if (idx + 3 >= rgba.Length) + return Color.FromRgb(0, 0, 0); + + byte r = rgba[idx]; + byte g = rgba[idx + 1]; + byte b = rgba[idx + 2]; + byte a = rgba[idx + 3]; + + return a < 128 ? Color.FromRgb(0, 0, 0) : Color.FromRgb(r, g, b); + } + } +} diff --git a/MinecraftClient/Tui/InventoryApp.cs b/MinecraftClient/Tui/InventoryApp.cs new file mode 100644 index 00000000..ea866300 --- /dev/null +++ b/MinecraftClient/Tui/InventoryApp.cs @@ -0,0 +1,36 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Consolonia.Themes; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class InventoryApp : Application + { + public override void Initialize() + { + Styles.Add(new ModernTheme()); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var handler = InventoryTuiHost.ActiveHandler!; + var windowId = InventoryTuiHost.ActiveWindowId; + var container = handler.GetInventory(windowId); + var containerType = container?.Type ?? ContainerType.PlayerInventory; + var view = ContainerViewBase.CreateView(containerType, handler, windowId); + + desktop.MainWindow = new Window + { + Content = view, + Title = "MCC Inventory" + }; + } + + base.OnFrameworkInitializationCompleted(); + } + } +} diff --git a/MinecraftClient/Tui/InventoryMainView.cs b/MinecraftClient/Tui/InventoryMainView.cs new file mode 100644 index 00000000..d141870d --- /dev/null +++ b/MinecraftClient/Tui/InventoryMainView.cs @@ -0,0 +1,129 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + public class PlayerInventoryView : ContainerViewBase + { + private readonly PlayerInventoryViewModel _playerVm; + private int _topGap; + + public PlayerInventoryView(McClient handler, int windowId) + : base(new PlayerInventoryViewModel(handler, windowId)) + { + _playerVm = (PlayerInventoryViewModel)_vm; + Initialize(); + } + + protected override int GetTotalSlotRows() + { + return 6; + } + + protected override void RebuildUi() + { + int availW = 0; + try { availW = System.Console.WindowWidth - 26; } catch { availW = 94; } + int slotW = System.Math.Clamp(availW / 9, 8, 18); + int topUsedW = slotW * 4 + 8 + slotW * 2 + 4 + slotW; + _topGap = System.Math.Max(2, (slotW * 9 - topUsedW) / 2); + + base.RebuildUi(); + } + + protected override Control BuildContainerSpecificArea() + { + var row = new StackPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + }; + + var offPanel = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(0, 0, 1, 0), + }; + offPanel.Children.Add(new TextBlock + { + Text = Translations.tui_inventory_offhand, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + offPanel.Children.Add(CreateSlotCell(_playerVm.OffhandSlot, 0, 0)); + row.Children.Add(offPanel); + + var equipGrid = new Grid + { + RowDefinitions = new RowDefinitions("Auto,Auto"), + ColumnDefinitions = new ColumnDefinitions("Auto,Auto,Auto,Auto"), + }; + + void AddEquipSlot(int r, int gc, string label, int eqIdx) + { + var lbl = MakeLabel(label); + Grid.SetRow(lbl, r); Grid.SetColumn(lbl, gc); + equipGrid.Children.Add(lbl); + var btn = CreateSlotCell(_playerVm.EquipmentSlots[eqIdx], r, gc / 2); + Grid.SetRow(btn, r); Grid.SetColumn(btn, gc + 1); + equipGrid.Children.Add(btn); + } + + AddEquipSlot(0, 0, Translations.tui_inventory_equip_head, 0); + AddEquipSlot(0, 2, Translations.tui_inventory_equip_body, 1); + AddEquipSlot(1, 0, Translations.tui_inventory_equip_legs, 2); + AddEquipSlot(1, 2, Translations.tui_inventory_equip_feet, 3); + + row.Children.Add(equipGrid); + row.Children.Add(new Border { Width = _topGap }); + + var craftGrid = new Grid + { + RowDefinitions = new RowDefinitions("Auto,Auto"), + ColumnDefinitions = new ColumnDefinitions("Auto,Auto,Auto,Auto"), + }; + + for (int ci = 0; ci < 4; ci++) + { + int cr = ci / 2, cc = ci % 2; + var cs = CreateSlotCell(_playerVm.CraftingInputSlots[ci], cr, cc); + Grid.SetRow(cs, cr); + Grid.SetColumn(cs, cc); + craftGrid.Children.Add(cs); + } + + var arrowTb = new TextBlock + { + Text = "=>", + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + VerticalAlignment = VerticalAlignment.Top, + Padding = new Thickness(1, 0), + }; + Grid.SetRow(arrowTb, 1); Grid.SetColumn(arrowTb, 2); + craftGrid.Children.Add(arrowTb); + + var craftOutPanel = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + }; + craftOutPanel.Children.Add(new TextBlock + { + Text = Translations.tui_inventory_output, + Foreground = BrEquipLbl, + FontWeight = FontWeight.Bold, + HorizontalAlignment = HorizontalAlignment.Center, + }); + craftOutPanel.Children.Add(CreateSlotCell(_playerVm.CraftingOutputSlot, 0, 1)); + Grid.SetRow(craftOutPanel, 0); Grid.SetColumn(craftOutPanel, 3); + Grid.SetRowSpan(craftOutPanel, 2); + craftGrid.Children.Add(craftOutPanel); + + row.Children.Add(craftGrid); + return row; + } + } +} diff --git a/MinecraftClient/Tui/InventoryTuiHost.cs b/MinecraftClient/Tui/InventoryTuiHost.cs new file mode 100644 index 00000000..f740ffd4 --- /dev/null +++ b/MinecraftClient/Tui/InventoryTuiHost.cs @@ -0,0 +1,209 @@ +using System; +using System.Threading; +using Avalonia; +using Avalonia.Threading; +using Consolonia; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + /// + /// Manages the lifecycle of the inventory TUI. + /// In TUI mode: opens as a Consolonia dialog window. + /// In classic mode: launches standalone Consolonia on a dedicated thread. + /// + public static class InventoryTuiHost + { + private static volatile bool _isRunning; + private static bool _classicEverLaunched; + + public static McClient? ActiveHandler { get; private set; } + public static int ActiveWindowId { get; private set; } + + public static bool IsRunning => _isRunning; + + /// + /// Called by McClient.OnInventoryClose when the server closes a container. + /// If the closed window matches the active TUI window, auto-close the TUI. + /// + public static void NotifyInventoryClosed(int windowId) + { + if (!_isRunning || windowId != ActiveWindowId) + return; + + if (ConsoleIO.Backend is TuiConsoleBackend) + { + Dispatcher.UIThread.Post(() => + { + var view = TuiConsoleBackend.Instance?.GetView(); + view?.HideOverlay(); + }); + } + else + { + (Avalonia.Application.Current?.ApplicationLifetime + as Avalonia.Controls.ApplicationLifetimes.IControlledApplicationLifetime)?.Shutdown(); + } + } + + /// + /// Whether the TUI can be launched (classic mode has a one-shot limit). + /// + public static bool CanLaunch + { + get + { + if (_isRunning) return false; + if (ConsoleIO.Backend is TuiConsoleBackend) return true; + return !_classicEverLaunched; + } + } + + /// + /// Called before standalone TUI takes over the terminal (classic mode only). + /// + public static Action? OnSuspendConsole { get; set; } + + /// + /// Called after standalone TUI releases the terminal (classic mode only). + /// + public static Action? OnResumeConsole { get; set; } + + public static bool Launch(McClient handler, int windowId) + { + if (_isRunning) + return false; + + Container? container = handler.GetInventory(windowId); + if (container == null) + return false; + + _isRunning = true; + ActiveHandler = handler; + ActiveWindowId = windowId; + + if (ConsoleIO.Backend is TuiConsoleBackend) + { + LaunchAsDialog(); + } + else + { + if (_classicEverLaunched) + { + _isRunning = false; + ActiveHandler = null; + return false; + } + var tuiThread = new Thread(RunClassicTui) { Name = "InventoryTUI", IsBackground = false }; + tuiThread.Start(); + } + + return true; + } + + /// + /// Open inventory as an overlay panel within the main TUI view. + /// + private static void LaunchAsDialog() + { + Dispatcher.UIThread.Post(() => + { + try + { + var view = TuiConsoleBackend.Instance?.GetView(); + if (view != null) + { + var container = ActiveHandler!.GetInventory(ActiveWindowId); + var content = ContainerViewBase.CreateView( + container?.Type ?? ContainerType.PlayerInventory, + ActiveHandler, ActiveWindowId); + view.ShowOverlay(content, () => + { + ActiveHandler = null; + _isRunning = false; + }); + } + else + { + ActiveHandler = null; + _isRunning = false; + } + } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"§c[InventoryTUI] Error: {ex.Message}"); + ConsoleIO.WriteLineFormatted($"§c[InventoryTUI] Stack: {ex.StackTrace}"); + if (ex.InnerException != null) + ConsoleIO.WriteLineFormatted($"§c[InventoryTUI] Inner: {ex.InnerException.Message}"); + ActiveHandler = null; + _isRunning = false; + } + }); + } + + /// + /// Classic mode: run standalone Consolonia on a dedicated thread. + /// + private static void RunClassicTui() + { + try + { + OnSuspendConsole?.Invoke(); + _classicEverLaunched = true; + + AppBuilder builder = AppBuilder.Configure() + .UseConsolonia() + .UseAutoDetectedConsole() + .LogToException(); + + builder.StartWithConsoleLifetime(Array.Empty()); + } + catch (Exception ex) + { + System.Console.Error.WriteLine($"[InventoryTUI] Error: {ex.Message}"); + System.Console.Error.WriteLine($"[InventoryTUI] Stack: {ex.StackTrace}"); + if (ex.InnerException != null) + System.Console.Error.WriteLine($"[InventoryTUI] Inner: {ex.InnerException}"); + } + finally + { + RestoreTerminalState(); + OnResumeConsole?.Invoke(); + ActiveHandler = null; + _isRunning = false; + } + } + + private static void RestoreTerminalState() + { + try + { + System.Console.Write("\x1b[?1049l"); + System.Console.Write("\x1b[?25h"); + System.Console.Write("\x1b[?1000l"); + System.Console.Write("\x1b[?1002l"); + System.Console.Write("\x1b[?1003l"); + System.Console.Write("\x1b[?1006l"); + System.Console.Write("\x1b[?2004l"); + System.Console.Write("\x1b[0m"); + System.Console.Write("\x1b(B"); + System.Console.Out.Flush(); + + try + { + using var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = "stty", + Arguments = "sane", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }); + proc?.WaitForExit(2000); + } + catch { } + } + catch { } + } + } +} diff --git a/MinecraftClient/Tui/InventoryViewModel.cs b/MinecraftClient/Tui/InventoryViewModel.cs new file mode 100644 index 00000000..3b29928f --- /dev/null +++ b/MinecraftClient/Tui/InventoryViewModel.cs @@ -0,0 +1,60 @@ +using System.Collections.ObjectModel; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class PlayerInventoryViewModel : ContainerViewModel + { + public ObservableCollection EquipmentSlots { get; } = new(); + public ObservableCollection CraftingInputSlots { get; } = new(); + public SlotViewModel CraftingOutputSlot { get; } + public SlotViewModel OffhandSlot { get; } + + public PlayerInventoryViewModel(McClient handler, int windowId) + : base(handler, windowId, ContainerType.PlayerInventory) + { + CraftingOutputSlot = SlotMap[0]; + OffhandSlot = SlotMap[45]; + } + + protected override void InitializeSlots() + { + SlotMap.Clear(); + + var craftOut = new SlotViewModel(0); + SlotMap[0] = craftOut; + + for (int i = 1; i <= 4; i++) + { + var slot = new SlotViewModel(i); + CraftingInputSlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 5; i <= 8; i++) + { + var slot = new SlotViewModel(i); + EquipmentSlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 9; i <= 35; i++) + { + var slot = new SlotViewModel(i); + MainInventorySlots.Add(slot); + SlotMap[i] = slot; + } + + for (int i = 36; i <= 44; i++) + { + int hotbarIdx = i - 36; + var slot = new SlotViewModel(i, isHotbar: true, hotbarIndex: hotbarIdx); + HotbarSlots.Add(slot); + SlotMap[i] = slot; + } + + var offhand = new SlotViewModel(45); + SlotMap[45] = offhand; + } + } +} diff --git a/MinecraftClient/Tui/MainTuiView.cs b/MinecraftClient/Tui/MainTuiView.cs new file mode 100644 index 00000000..78ec2592 --- /dev/null +++ b/MinecraftClient/Tui/MainTuiView.cs @@ -0,0 +1,1250 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class MainTuiView : UserControl + { + private static readonly int MaxLogLines = ResolveMaxLogLines(); + private const int CtrlCDoublePressMsec = 1500; + + private static int ResolveMaxLogLines() + { + int configured = Settings.Config.Console.General.TUI_Log_Scrollback; + if (configured > 0) + return configured; + + return 3000; + } + + private readonly ObservableCollection _logLines = new(); + private readonly ObservableCollection _logControls = new(); + private readonly ItemsControl _logItemsControl; + private readonly ScrollViewer _logScrollViewer; + private readonly TextBox _commandInput; + private bool _autoScroll = true; + private bool _programmaticScroll; + private readonly ObservableCollection _commandHistory = new(); + private int _historyIndex = -1; + + private readonly Panel _rootPanel; + private readonly DockPanel _mainContent; + private Control? _overlayContent; + private Action? _overlayCloseCallback; + + private readonly TextBlock _statusBar; + private readonly Border _notificationBorder; + private readonly TextBlock _notificationText; + private long _lastCtrlCTicks; + private long _lastLogClickTicks; + private const int DoubleClickMsec = 500; + + private readonly Border _minimapBorder; + private readonly MinimapControl _minimapControl; + private volatile bool _minimapVisible; + + private TuiTooltipService? _tooltipService; + + private readonly Border _suggestionBorder; + private readonly StackPanel _suggestionPanel; + private CommandSuggestion[] _suggestions = Array.Empty(); + private (int Start, int End) _suggestionRange; + private int _selectedSuggestionIndex = -1; + private int _suggestionViewTop; + private bool _acceptingSuggestion; + private bool _tabCycling; + + private int MaxVisibleSuggestions => + Math.Max(1, Settings.Config.Console.CommandSuggestion.Max_Displayed_Suggestions); + + public TuiTooltipService? TooltipService => _tooltipService; + + public MainTuiView() + { + Background = Brushes.Black; + + _statusBar = new TextBlock + { + Foreground = Brushes.Gray, + Background = Brushes.Black, + Padding = new Thickness(0), + Margin = new Thickness(0), + IsVisible = false, + }; + + _logItemsControl = new ItemsControl + { + ItemsSource = _logControls, + Focusable = false, + ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()), + }; + + _logScrollViewer = new ScrollViewer + { + Content = _logItemsControl, + Background = Brushes.Black, + HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + Padding = new Thickness(0), + Focusable = false, + }; + + _logScrollViewer.ScrollChanged += OnLogScrollChanged; + _logScrollViewer.PointerPressed += OnLogAreaPointerPressed; + + _commandInput = new TextBox + { + Watermark = "", + Foreground = Brushes.White, + Background = Brushes.Black, + BorderThickness = new Thickness(0), + Padding = new Thickness(0), + Margin = new Thickness(0), + MinHeight = 1, + }; + + _commandInput.AddHandler(KeyDownEvent, OnCommandKeyDown, Avalonia.Interactivity.RoutingStrategies.Tunnel); + _commandInput.AddHandler(TextInputEvent, OnCommandTextInput, Avalonia.Interactivity.RoutingStrategies.Tunnel); + _commandInput.TextChanged += OnCommandTextChanged; + + var promptLabel = new TextBlock + { + Text = "> ", + Foreground = Brushes.Cyan, + VerticalAlignment = VerticalAlignment.Center, + FontWeight = FontWeight.Bold, + }; + + var inputRow = new DockPanel + { + Background = Brushes.Black, + Children = + { + SetDock(promptLabel, Dock.Left), + _commandInput + } + }; + + _notificationText = new TextBlock + { + Foreground = Brushes.Yellow, + Padding = new Thickness(1, 0), + }; + _notificationBorder = new Border + { + Background = new SolidColorBrush(Color.FromRgb(60, 50, 20)), + BorderBrush = Brushes.Yellow, + BorderThickness = new Thickness(1), + Child = _notificationText, + IsVisible = false, + HorizontalAlignment = HorizontalAlignment.Right, + VerticalAlignment = VerticalAlignment.Top, + }; + + _suggestionPanel = new StackPanel + { + Orientation = Avalonia.Layout.Orientation.Vertical, + }; + _suggestionPanel.PointerWheelChanged += OnSuggestionWheelChanged; + _suggestionBorder = new Border + { + Background = new SolidColorBrush(Color.FromRgb(30, 30, 30)), + BorderBrush = new SolidColorBrush(Color.FromRgb(80, 80, 80)), + BorderThickness = new Thickness(1), + Child = _suggestionPanel, + IsVisible = false, + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Bottom, + Margin = new Thickness(0, 0, 0, 1), + }; + + var mmCfg = Settings.Config.Console.Minimap; + mmCfg.OnSettingUpdate(); + _minimapControl = new MinimapControl(mmCfg.Width, mmCfg.Height); + _minimapControl.BlocksPerPixel = mmCfg.Zoom; + _minimapControl.RefreshIntervalMs = mmCfg.RefreshInterval; + _minimapControl.NameConfig.Players = mmCfg.ShowPlayerNames; + _minimapControl.NameConfig.Hostile = mmCfg.ShowHostileNames; + _minimapControl.NameConfig.Neutral = mmCfg.ShowNeutralNames; + _minimapControl.NameConfig.Passive = mmCfg.ShowPassiveNames; + _minimapControl.CaveMode = mmCfg.CaveMode; + + var (hAlign, vAlign, margin) = GetMinimapAlignment(mmCfg.Position); + _minimapBorder = new Border + { + Background = new SolidColorBrush(Color.FromArgb(220, 15, 15, 15)), + BorderBrush = new SolidColorBrush(Color.FromRgb(80, 80, 80)), + BorderThickness = new Thickness(1), + Child = _minimapControl, + IsVisible = false, + HorizontalAlignment = hAlign, + VerticalAlignment = vAlign, + Margin = margin, + }; + + _mainContent = new DockPanel + { + Background = Brushes.Black, + Children = + { + SetDock(_statusBar, Dock.Top), + SetDock(inputRow, Dock.Bottom), + _logScrollViewer + } + }; + + _rootPanel = new Panel + { + Background = Brushes.Black, + Children = { _mainContent, _minimapBorder, _notificationBorder, _suggestionBorder } + }; + + _tooltipService = new TuiTooltipService(_rootPanel); + _minimapControl.TooltipService = _tooltipService; + _minimapControl.Position = mmCfg.Position; + + Content = _rootPanel; + + if (mmCfg.Enabled) + { + _minimapVisible = true; + _minimapBorder.IsVisible = true; + _minimapControl.Start(); + } + + StartStatusBarTimer(); + } + + private static Control SetDock(Control control, Dock dock) + { + DockPanel.SetDock(control, dock); + return control; + } + + #region Log output + + public void AppendLogLine(string text) + { + _logLines.Add(text); + + var tb = new TextBlock + { + Text = text, + Foreground = Brushes.White, + Padding = new Thickness(0), + Margin = new Thickness(0), + TextWrapping = TextWrapping.Wrap, + }; + _logControls.Add(tb); + + TrimLog(); + + if (_autoScroll) + ScheduleScrollToEnd(); + } + + public void AppendFormattedLogLine(string text) + { + _logLines.Add(text); + + var tb = McColorParser.CreateColoredTextBlock(text, TextWrapping.Wrap); + _logControls.Add(tb); + + TrimLog(); + + if (_autoScroll) + ScheduleScrollToEnd(); + } + + public void ClearLog() + { + _logLines.Clear(); + _logControls.Clear(); + ScheduleScrollToEnd(); + } + + private void TrimLog() + { + while (_logLines.Count > MaxLogLines) + { + _logLines.RemoveAt(0); + _logControls.RemoveAt(0); + } + } + + private void ScheduleScrollToEnd() + { + Dispatcher.UIThread.Post(() => + { + _programmaticScroll = true; + var sv = _logScrollViewer; + sv.Offset = new Vector(0, sv.Extent.Height); + _programmaticScroll = false; + }, DispatcherPriority.Background); + } + + public string LatestLogLine => _logLines.Count > 0 ? _logLines[^1] : ""; + + public ObservableCollection GetRecentLogLines(int _) => _logLines; + + private void OnLogAreaPointerPressed(object? sender, PointerPressedEventArgs e) + { + var props = e.GetCurrentPoint(null).Properties; + if (!props.IsLeftButtonPressed) + { + Dispatcher.UIThread.Post(() => _commandInput.Focus()); + return; + } + + bool shift = (e.KeyModifiers & KeyModifiers.Shift) != 0; + if (shift) + return; + + long now = Environment.TickCount64; + long elapsed = now - _lastLogClickTicks; + _lastLogClickTicks = now; + + if (elapsed < DoubleClickMsec) + { + ShowNotification(Translations.tui_select_copy_hint, 3000); + _lastLogClickTicks = 0; + } + + Dispatcher.UIThread.Post(() => _commandInput.Focus()); + } + + #endregion + + #region Input + + public void ClearInput() + { + _commandInput.Text = string.Empty; + } + + private void OnCommandKeyDown(object? sender, KeyEventArgs e) + { + if (_tabCycling && e.Key is not Key.Tab) + _tabCycling = false; + + bool ctrl = (e.KeyModifiers & KeyModifiers.Control) != 0; + + if (e.Key == Key.C && ctrl) + { + HandleCtrlC(); + e.Handled = true; + return; + } + + if ((e.Key == Key.Back || e.Key == Key.W) && ctrl) + { + DeleteWordBackward(); + e.Handled = true; + return; + } + + if (e.Key == Key.Left && ctrl) + { + MoveCaretWordLeft(); + e.Handled = true; + return; + } + + if (e.Key == Key.Right && ctrl) + { + MoveCaretWordRight(); + e.Handled = true; + return; + } + + if (e.Key == Key.A && ctrl) + { + _commandInput.CaretIndex = 0; + e.Handled = true; + return; + } + + if (e.Key == Key.E && ctrl) + { + _commandInput.CaretIndex = _commandInput.Text?.Length ?? 0; + e.Handled = true; + return; + } + + if (e.Key == Key.U && ctrl) + { + _commandInput.Text = string.Empty; + e.Handled = true; + return; + } + + if (e.Key == Key.Escape && SuggestionsVisible) + { + ClearSuggestions(); + e.Handled = true; + return; + } + + if (e.Key == Key.Tab && SuggestionsVisible) + { + if (_tabCycling) + { + MoveSuggestionSelection(1); + ApplySuggestionInPlace(_selectedSuggestionIndex); + } + else + { + ApplySuggestionInPlace(_selectedSuggestionIndex); + _tabCycling = true; + } + e.Handled = true; + return; + } + + if (e.Key == Key.Tab) + { + e.Handled = true; + return; + } + + switch (e.Key) + { + case Key.Enter: + SubmitCommand(); + e.Handled = true; + break; + + case Key.Up: + if (SuggestionsVisible) + MoveSuggestionSelection(-1); + else + NavigateHistory(-1); + e.Handled = true; + break; + + case Key.Down: + if (SuggestionsVisible) + MoveSuggestionSelection(1); + else + NavigateHistory(1); + e.Handled = true; + break; + + case Key.PageUp: + ScrollLog(-10); + e.Handled = true; + break; + + case Key.PageDown: + ScrollLog(10); + e.Handled = true; + break; + } + } + + private void DeleteWordBackward() + { + string text = _commandInput.Text ?? ""; + int caret = _commandInput.CaretIndex; + if (caret == 0 || text.Length == 0) return; + + int pos = caret - 1; + while (pos > 0 && text[pos - 1] == ' ') pos--; + while (pos > 0 && text[pos - 1] != ' ') pos--; + + _commandInput.Text = text[..pos] + text[caret..]; + _commandInput.CaretIndex = pos; + } + + private void MoveCaretWordLeft() + { + string text = _commandInput.Text ?? ""; + int pos = _commandInput.CaretIndex; + if (pos == 0) return; + + pos--; + while (pos > 0 && text[pos - 1] == ' ') pos--; + while (pos > 0 && text[pos - 1] != ' ') pos--; + + _commandInput.CaretIndex = pos; + } + + private void MoveCaretWordRight() + { + string text = _commandInput.Text ?? ""; + int pos = _commandInput.CaretIndex; + if (pos >= text.Length) return; + + while (pos < text.Length && text[pos] != ' ') pos++; + while (pos < text.Length && text[pos] == ' ') pos++; + + _commandInput.CaretIndex = pos; + } + + private void OnCommandTextInput(object? sender, TextInputEventArgs e) + { + string? incoming = e.Text; + if (string.IsNullOrEmpty(incoming) || (!incoming.Contains('\n') && !incoming.Contains('\r'))) + return; + + e.Handled = true; + + string[] lines = incoming.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); + + string prefix = _commandInput.Text ?? ""; + for (int i = 0; i < lines.Length; i++) + { + string line = lines[i]; + bool isLast = i == lines.Length - 1; + + if (line.Length > 0 || prefix.Length > 0) + { + _acceptingSuggestion = true; + try { _commandInput.Text = prefix + line; } + finally { _acceptingSuggestion = false; } + } + + if (!isLast) + { + SubmitCommand(); + prefix = ""; + } + } + } + + private void OnCommandTextChanged(object? sender, TextChangedEventArgs e) + { + string text = _commandInput.Text ?? string.Empty; + + if (_acceptingSuggestion || _tabCycling) + return; + + if (string.IsNullOrEmpty(text)) + { + ClearSuggestions(); + return; + } + + var backend = TuiConsoleBackend.Instance; + if (backend == null) return; + int cursor = _commandInput.CaretIndex; + backend.OnInputChanged(text, cursor); + } + + private void SubmitCommand() + { + string command = _commandInput.Text?.Trim() ?? string.Empty; + if (string.IsNullOrEmpty(command)) + return; + + ClearSuggestions(); + _tabCycling = false; + + _commandHistory.Add(command); + _historyIndex = _commandHistory.Count; + + _acceptingSuggestion = true; + try { _commandInput.Text = string.Empty; } + finally { _acceptingSuggestion = false; } + + _autoScroll = true; + + AppendLogLine($"> {command}"); + + TuiConsoleBackend.Instance?.OnCommandSubmitted(command); + } + + private void NavigateHistory(int direction) + { + if (_commandHistory.Count == 0) + return; + + _historyIndex += direction; + if (_historyIndex < 0) _historyIndex = 0; + if (_historyIndex >= _commandHistory.Count) + { + _historyIndex = _commandHistory.Count; + _commandInput.Text = string.Empty; + return; + } + + string historyText = _commandHistory[_historyIndex]; + SetCommandText(historyText); + } + + private void SetCommandText(string text) + { + _commandInput.TextChanged -= OnCommandTextChanged; + try + { + _commandInput.Text = text; + _commandInput.CaretIndex = text.Length; + } + finally + { + _commandInput.TextChanged += OnCommandTextChanged; + } + + Dispatcher.UIThread.Post(() => + { + var endKeyEvent = new KeyEventArgs + { + RoutedEvent = KeyDownEvent, + Key = Key.End, + Source = _commandInput, + }; + _commandInput.RaiseEvent(endKeyEvent); + }, DispatcherPriority.Input); + } + + #endregion + + #region Suggestions + + private const int PromptWidth = 2; // "> " + private const int BorderAndPadding = 2; // 1 border + 1 padding on each side + + internal void UpdateSuggestions(CommandSuggestion[] suggestions, (int Start, int End) range) + { + if (suggestions.Length == 0) + { + ClearSuggestions(); + return; + } + + _suggestions = suggestions; + _suggestionRange = range; + _selectedSuggestionIndex = 0; + _suggestionViewTop = 0; + + int leftOffset = PromptWidth + range.Start - BorderAndPadding; + double screenWidth = Bounds.Width; + if (screenWidth < 1) + screenWidth = 80; + + if (leftOffset < 0) + leftOffset = 0; + + _suggestionBorder.Margin = new Thickness(leftOffset, 0, 0, 1); + _suggestionBorder.MaxWidth = Math.Max(10, screenWidth - leftOffset); + + RebuildSuggestionItems(); + _suggestionBorder.IsVisible = true; + } + + internal void ClearSuggestions() + { + if (!_suggestionBorder.IsVisible && _suggestions.Length == 0) + return; + + _suggestions = Array.Empty(); + _selectedSuggestionIndex = -1; + _suggestionBorder.IsVisible = false; + _suggestionPanel.Children.Clear(); + } + + private bool SuggestionsVisible => _suggestionBorder.IsVisible && _suggestions.Length > 0; + + private void RebuildSuggestionItems() + { + _suggestionPanel.Children.Clear(); + + int visibleCount = Math.Min(_suggestions.Length, MaxVisibleSuggestions); + int viewBottom = _suggestionViewTop + visibleCount; + + for (int i = _suggestionViewTop; i < viewBottom && i < _suggestions.Length; i++) + { + var sug = _suggestions[i]; + int index = i; + + string label = sug.Text; + if (!string.IsNullOrEmpty(sug.Tooltip)) + label += " " + sug.Tooltip; + + var tb = new TextBlock + { + Text = label, + Padding = new Thickness(1, 0), + Foreground = Brushes.White, + TextTrimming = TextTrimming.CharacterEllipsis, + Background = i == _selectedSuggestionIndex + ? new SolidColorBrush(Color.FromRgb(0, 90, 160)) + : Brushes.Transparent, + }; + + var row = new Border + { + Child = tb, + Background = Brushes.Transparent, + }; + + row.PointerPressed += (_, _) => + { + _selectedSuggestionIndex = index; + ApplySuggestionInPlace(index); + _tabCycling = true; + }; + row.PointerEntered += (_, _) => + { + if (_selectedSuggestionIndex != index) + { + _selectedSuggestionIndex = index; + UpdateSuggestionHighlight(); + } + }; + + _suggestionPanel.Children.Add(row); + } + + if (_suggestions.Length > MaxVisibleSuggestions) + { + string scrollHint = $"[{_suggestionViewTop + 1}-{viewBottom}/{_suggestions.Length}]"; + var hintTb = new TextBlock + { + Text = scrollHint, + Foreground = new SolidColorBrush(Color.FromRgb(120, 120, 120)), + Padding = new Thickness(1, 0), + TextAlignment = TextAlignment.Right, + HorizontalAlignment = HorizontalAlignment.Stretch, + }; + _suggestionPanel.Children.Add(hintTb); + } + } + + private void UpdateSuggestionHighlight() + { + int visibleCount = Math.Min(_suggestions.Length, MaxVisibleSuggestions); + for (int i = 0; i < visibleCount && i < _suggestionPanel.Children.Count; i++) + { + if (_suggestionPanel.Children[i] is Border border && border.Child is TextBlock tb) + { + int dataIndex = _suggestionViewTop + i; + tb.Background = dataIndex == _selectedSuggestionIndex + ? new SolidColorBrush(Color.FromRgb(0, 90, 160)) + : Brushes.Transparent; + } + } + } + + private void MoveSuggestionSelection(int direction) + { + if (_suggestions.Length == 0) return; + + _selectedSuggestionIndex += direction; + if (_selectedSuggestionIndex < 0) + _selectedSuggestionIndex = _suggestions.Length - 1; + else if (_selectedSuggestionIndex >= _suggestions.Length) + _selectedSuggestionIndex = 0; + + int visibleCount = Math.Min(_suggestions.Length, MaxVisibleSuggestions); + if (_selectedSuggestionIndex < _suggestionViewTop) + { + _suggestionViewTop = _selectedSuggestionIndex; + RebuildSuggestionItems(); + } + else if (_selectedSuggestionIndex >= _suggestionViewTop + visibleCount) + { + _suggestionViewTop = _selectedSuggestionIndex - visibleCount + 1; + RebuildSuggestionItems(); + } + else + { + UpdateSuggestionHighlight(); + } + } + + private void OnSuggestionWheelChanged(object? sender, PointerWheelEventArgs e) + { + if (!SuggestionsVisible) return; + + int direction = e.Delta.Y > 0 ? -1 : 1; + ScrollSuggestionViewport(direction); + e.Handled = true; + } + + private void ScrollSuggestionViewport(int direction) + { + if (_suggestions.Length <= MaxVisibleSuggestions) return; + + int newTop = _suggestionViewTop + direction; + int maxTop = _suggestions.Length - MaxVisibleSuggestions; + newTop = Math.Clamp(newTop, 0, maxTop); + + if (newTop == _suggestionViewTop) return; + _suggestionViewTop = newTop; + + int viewBottom = _suggestionViewTop + MaxVisibleSuggestions; + if (_selectedSuggestionIndex < _suggestionViewTop) + _selectedSuggestionIndex = _suggestionViewTop; + else if (_selectedSuggestionIndex >= viewBottom) + _selectedSuggestionIndex = viewBottom - 1; + + RebuildSuggestionItems(); + } + + private void ApplySuggestionText(int index) + { + if (index < 0 || index >= _suggestions.Length) return; + + string text = _commandInput.Text ?? ""; + string selected = _suggestions[index].Text; + + int start = Math.Min(_suggestionRange.Start, text.Length); + int end = Math.Min(_suggestionRange.End, text.Length); + + string before = text[..start]; + string after = text[end..]; + string newText = before + selected + after; + + _commandInput.Text = newText; + _commandInput.CaretIndex = before.Length + selected.Length; + + _suggestionRange = (start, start + selected.Length); + } + + private void ApplySuggestionInPlace(int index) + { + if (index < 0 || index >= _suggestions.Length) return; + + _acceptingSuggestion = true; + try + { + ApplySuggestionText(index); + } + finally + { + _acceptingSuggestion = false; + } + UpdateSuggestionHighlight(); + } + + #endregion + + #region Ctrl+C + + internal void HandleCtrlC() + { + long now = Environment.TickCount64; + long elapsed = now - _lastCtrlCTicks; + + if (_lastCtrlCTicks > 0 && elapsed < CtrlCDoublePressMsec) + { + HideNotification(); + TuiConsoleBackend.Instance?.Shutdown(); + return; + } + + _lastCtrlCTicks = now; + + string inputText = _commandInput.Text?.Trim() ?? ""; + if (inputText.Length > 0) + { + _commandInput.Text = string.Empty; + ShowNotification(Translations.tui_ctrlc_input_cleared, CtrlCDoublePressMsec); + } + else + { + ShowNotification(Translations.tui_ctrlc_quit_hint, CtrlCDoublePressMsec); + } + } + + private void ShowNotification(string message, int autoHideMs) + { + _notificationText.Text = message; + _notificationBorder.IsVisible = true; + + var timer = new Avalonia.Threading.DispatcherTimer + { + Interval = TimeSpan.FromMilliseconds(autoHideMs), + }; + timer.Tick += (_, _) => + { + timer.Stop(); + HideNotification(); + }; + timer.Start(); + } + + private void HideNotification() + { + _notificationBorder.IsVisible = false; + } + + #endregion + + #region Scrolling + + private void ScrollLog(int delta) + { + var sv = _logScrollViewer; + var newY = sv.Offset.Y + delta; + newY = Math.Max(0, Math.Min(newY, sv.Extent.Height - sv.Viewport.Height)); + sv.Offset = new Vector(0, newY); + + _autoScroll = newY >= sv.Extent.Height - sv.Viewport.Height - 2; + } + + private void OnLogScrollChanged(object? sender, ScrollChangedEventArgs e) + { + if (_programmaticScroll) return; + + var sv = _logScrollViewer; + _autoScroll = sv.Offset.Y >= sv.Extent.Height - sv.Viewport.Height - 2; + } + + #endregion + + #region Status Bar (Health / Food) + + private void StartStatusBarTimer() + { + var timer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1), + }; + timer.Tick += (_, _) => UpdateStatusBar(); + timer.Start(); + } + + private void UpdateStatusBar() + { + if (McClient.Instance is not McClient client) + { + _statusBar.IsVisible = false; + return; + } + + int gamemode = client.GetGamemode(); + if (gamemode != 0 && gamemode != 2) + { + _statusBar.IsVisible = false; + return; + } + + float health = client.GetHealth(); + int food = client.GetSaturation(); + + int heartsFilled = (int)Math.Ceiling(health / 20f * 10); + heartsFilled = Math.Clamp(heartsFilled, 0, 10); + int foodFilled = (int)Math.Ceiling(food / 20f * 10); + foodFilled = Math.Clamp(foodFilled, 0, 10); + + _statusBar.Inlines?.Clear(); + _statusBar.Inlines ??= new Avalonia.Controls.Documents.InlineCollection(); + + var healthText = BuildBarText(heartsFilled, 10, "\u2764\ufe0f", " \u2661 "); + var foodText = BuildBarText(foodFilled, 10, "\ud83c\udf56", " \u25cb "); + + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run(healthText) + { + Foreground = new SolidColorBrush(Color.FromRgb(255, 85, 85)), + }); + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run($" {health:F1} ") + { + Foreground = new SolidColorBrush(Color.FromRgb(255, 150, 150)), + }); + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run(foodText) + { + Foreground = new SolidColorBrush(Color.FromRgb(200, 160, 80)), + }); + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run($" {food}") + { + Foreground = new SolidColorBrush(Color.FromRgb(220, 190, 100)), + }); + + // Add effects display + var effects = client.GetPlayerEffects().Values + .Where(effectData => !effectData.IsExpired) + .OrderBy(effectData => effectData.Effect) + .ToArray(); + if (effects.Length > 0) + { + bool showEffectNamesInTui = Settings.Config.Main.Advanced.ShowEffectNamesInTUI; + + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run(" | ") + { + Foreground = Brushes.Gray, + }); + + bool first = true; + foreach (var effectData in effects) + { + if (!first) + { + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run(", ") + { + Foreground = Brushes.Gray, + }); + } + first = false; + + var color = GetEffectIconAndColor(effectData.Effect).Color; + var displayText = showEffectNamesInTui + ? effectData.GetDisplayName() + : GetCompactEffectLabel(effectData); + displayText = $"{displayText} ({effectData.GetRemainingDurationText()})"; + + _statusBar.Inlines.Add(new Avalonia.Controls.Documents.Run(displayText) + { + Foreground = color, + }); + } + } + + _statusBar.IsVisible = true; + } + + private static string BuildBarText(int filled, int total, string filledChar, string emptyChar) + { + var sb = new System.Text.StringBuilder(); + for (int i = 0; i < filled; i++) + { + if (i > 0) sb.Append(' '); + sb.Append(filledChar); + } + for (int i = filled; i < total; i++) + { + sb.Append(emptyChar); + } + return sb.ToString(); + } + + private static string GetCompactEffectLabel(EffectData effectData) + { + var icon = GetEffectIconAndColor(effectData.Effect).Icon; + return effectData.Amplifier > 0 + ? $"{icon}{effectData.Amplifier + 1}" + : icon; + } + + private static (string Icon, IBrush Color) GetEffectIconAndColor(Effects effect) + { + return effect switch + { + Effects.Speed => ("⚡", new SolidColorBrush(Color.FromRgb(135, 206, 235))), + Effects.Slowness => ("🐢", new SolidColorBrush(Color.FromRgb(139, 139, 139))), + Effects.Haste => ("⛏", new SolidColorBrush(Color.FromRgb(255, 215, 0))), + Effects.MiningFatigue => ("🔨", new SolidColorBrush(Color.FromRgb(64, 64, 64))), + Effects.Strength => ("⚔", new SolidColorBrush(Color.FromRgb(255, 99, 71))), + Effects.InstantHealth => ("❤", new SolidColorBrush(Color.FromRgb(255, 182, 193))), + Effects.InstantDamage => ("💀", new SolidColorBrush(Color.FromRgb(139, 0, 0))), + Effects.JumpBoost => ("🦘", new SolidColorBrush(Color.FromRgb(50, 205, 50))), + Effects.Nausea => ("💫", new SolidColorBrush(Color.FromRgb(85, 107, 47))), + Effects.Regeneration => ("✨", new SolidColorBrush(Color.FromRgb(255, 105, 180))), + Effects.Resistance => ("🛡", new SolidColorBrush(Color.FromRgb(112, 128, 144))), + Effects.FireResistance => ("🔥", new SolidColorBrush(Color.FromRgb(255, 140, 0))), + Effects.WaterBreathing => ("🐟", new SolidColorBrush(Color.FromRgb(0, 191, 255))), + Effects.Invisibility => ("👻", new SolidColorBrush(Color.FromRgb(200, 200, 200))), + Effects.Blindness => ("🕶", new SolidColorBrush(Color.FromRgb(50, 50, 50))), + Effects.NightVision => ("👁", new SolidColorBrush(Color.FromRgb(0, 255, 127))), + Effects.Hunger => ("🍔", new SolidColorBrush(Color.FromRgb(139, 69, 19))), + Effects.Weakness => ("💪", new SolidColorBrush(Color.FromRgb(128, 128, 128))), + Effects.Poison => ("☠", new SolidColorBrush(Color.FromRgb(75, 0, 130))), + Effects.Wither => ("🥀", new SolidColorBrush(Color.FromRgb(0, 0, 0))), + Effects.HealthBoost => ("💖", new SolidColorBrush(Color.FromRgb(255, 20, 147))), + Effects.Absorption => ("💛", new SolidColorBrush(Color.FromRgb(255, 215, 0))), + Effects.Saturation => ("🍖", new SolidColorBrush(Color.FromRgb(255, 165, 0))), + Effects.Glowing => ("💡", new SolidColorBrush(Color.FromRgb(255, 255, 150))), + Effects.Levitation => ("🎈", new SolidColorBrush(Color.FromRgb(147, 112, 219))), + Effects.Luck => ("🍀", new SolidColorBrush(Color.FromRgb(50, 205, 50))), + Effects.BadLuck => ("🐈‍⬛", new SolidColorBrush(Color.FromRgb(128, 0, 0))), + Effects.SlowFalling => ("🪶", new SolidColorBrush(Color.FromRgb(255, 182, 193))), + Effects.ConduitPower => ("🐡", new SolidColorBrush(Color.FromRgb(0, 255, 255))), + Effects.DolphinsGrace => ("🐬", new SolidColorBrush(Color.FromRgb(135, 206, 235))), + Effects.BadOmen => ("🏴", new SolidColorBrush(Color.FromRgb(0, 100, 0))), + Effects.HerooftheVillage => ("🎉", new SolidColorBrush(Color.FromRgb(255, 215, 0))), + _ => ("✦", new SolidColorBrush(Color.FromRgb(200, 200, 200))), + }; + } + + #endregion + + #region Minimap + + public void ShowMinimap() + { + if (_minimapVisible) return; + _minimapVisible = true; + _minimapBorder.IsVisible = true; + _minimapControl.Start(); + Settings.Config.Console.Minimap.Enabled = true; + } + + public void HideMinimap() + { + if (!_minimapVisible) return; + _minimapVisible = false; + _minimapControl.Stop(); + _minimapBorder.IsVisible = false; + Settings.Config.Console.Minimap.Enabled = false; + } + + public void ToggleMinimap() + { + if (_minimapVisible) + HideMinimap(); + else + ShowMinimap(); + } + + public bool IsMinimapVisible => _minimapVisible; + + public void SetMinimapZoom(int level) + { + _minimapControl.BlocksPerPixel = level; + Settings.Config.Console.Minimap.Zoom = level; + } + + public int GetMinimapZoom() => _minimapControl.BlocksPerPixel; + + public NameDisplayConfig GetMinimapNameConfig() => _minimapControl.NameConfig; + + public void SyncMinimapNameConfig() + { + var nc = _minimapControl.NameConfig; + var cfg = Settings.Config.Console.Minimap; + cfg.ShowPlayerNames = nc.Players; + cfg.ShowHostileNames = nc.Hostile; + cfg.ShowNeutralNames = nc.Neutral; + cfg.ShowPassiveNames = nc.Passive; + } + + public void ResizeMinimap(int width, int height) + { + _minimapControl.Resize(width, height); + Settings.Config.Console.Minimap.Width = width; + Settings.Config.Console.Minimap.Height = height; + } + + public void SetMinimapPosition(MinimapPosition pos) + { + var (hAlign, vAlign, margin) = GetMinimapAlignment(pos); + _minimapBorder.HorizontalAlignment = hAlign; + _minimapBorder.VerticalAlignment = vAlign; + _minimapBorder.Margin = margin; + _minimapControl.Position = pos; + Settings.Config.Console.Minimap.Position = pos; + } + + public MinimapPosition GetMinimapPosition() => Settings.Config.Console.Minimap.Position; + + public void SetMinimapCaveMode(CaveModeOption mode) + { + _minimapControl.CaveMode = mode; + Settings.Config.Console.Minimap.CaveMode = mode; + } + + public CaveModeOption GetMinimapCaveMode() => _minimapControl.CaveMode; + + private static (HorizontalAlignment h, VerticalAlignment v, Thickness margin) GetMinimapAlignment(MinimapPosition pos) => pos switch + { + MinimapPosition.top_left => (HorizontalAlignment.Left, VerticalAlignment.Top, new Thickness(1, 1, 0, 0)), + MinimapPosition.top_right => (HorizontalAlignment.Right, VerticalAlignment.Top, new Thickness(0, 1, 1, 0)), + MinimapPosition.center => (HorizontalAlignment.Center, VerticalAlignment.Center, new Thickness(0)), + MinimapPosition.bottom_left => (HorizontalAlignment.Left, VerticalAlignment.Bottom, new Thickness(1, 0, 0, 2)), + MinimapPosition.bottom_right => (HorizontalAlignment.Right, VerticalAlignment.Bottom, new Thickness(0, 0, 1, 2)), + _ => (HorizontalAlignment.Right, VerticalAlignment.Top, new Thickness(0, 1, 1, 0)), + }; + + public void ApplyMinimapConfig() + { + var cfg = Settings.Config.Console.Minimap; + if (cfg.Enabled && !_minimapVisible) + ShowMinimap(); + else if (!cfg.Enabled && _minimapVisible) + HideMinimap(); + } + + #endregion + + #region Overlay + + public void ShowOverlay(Control content, Action? onClose = null) + { + if (_overlayContent != null) + HideOverlay(); + + _overlayContent = content; + _overlayCloseCallback = onClose; + _mainContent.IsVisible = false; + + _rootPanel.Children.Add(_overlayContent); + } + + public void HideOverlay() + { + if (_overlayContent == null) return; + + _rootPanel.Children.Remove(_overlayContent); + + _overlayContent = null; + _mainContent.IsVisible = true; + + var cb = _overlayCloseCallback; + _overlayCloseCallback = null; + cb?.Invoke(); + + _commandInput.Focus(); + } + + public bool HasOverlay => _overlayContent != null; + + public Control? OverlayContent => _overlayContent; + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Escape && _overlayContent != null) + { + if (_overlayContent is IOverlayCloseHandler closeHandler) + { + if (closeHandler.TryCloseByUser()) + HideOverlay(); + + e.Handled = true; + return; + } + + HideOverlay(); + e.Handled = true; + return; + } + base.OnKeyDown(e); + } + + #endregion + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + Dispatcher.UIThread.Post(() => + { + _commandInput.Focus(); + }, DispatcherPriority.Loaded); + } + + #region Custom Control Append + + public void AppendControlToLog(Control control) + { + _logLines.Add(string.Empty); + _logControls.Add(control); + TrimLog(); + if (_autoScroll) + ScheduleScrollToEnd(); + } + + #endregion + } +} diff --git a/MinecraftClient/Tui/MapOverlay.cs b/MinecraftClient/Tui/MapOverlay.cs new file mode 100644 index 00000000..a52096f6 --- /dev/null +++ b/MinecraftClient/Tui/MapOverlay.cs @@ -0,0 +1,499 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using MinecraftClient.ChatBots; + +namespace MinecraftClient.Tui +{ + /// + /// Fullscreen overlay that renders a Minecraft map with interactive zoom and pan. + /// Uses Unicode half-block characters where each terminal cell displays two vertical + /// pixels (foreground = top, background = bottom). Supports mouse wheel zoom with + /// center-anchoring, mouse drag panning, and keyboard navigation. + /// At 100% one terminal column = one map pixel wide; at 200% two columns = one pixel. + /// + internal sealed class MapOverlay : Panel + { + private const double MaxScale = 2.0; + private const double ZoomStep = 0.125; + private const double KeyPanStep = 4.0; + private const double OffsetEpsilon = 0.5; + + private readonly TextBlock _mapBlock; + private readonly TextBlock _headerBlock; + private readonly TextBlock _controlsBlock; + private readonly TextBlock _cornerTL; + private readonly TextBlock _cornerTR; + private readonly TextBlock _cornerBL; + private readonly TextBlock _cornerBR; + + private McMap _map = null!; + private double _scale = 1.0; + private double _offsetX; + private double _offsetY; + private double _fitScale = 1.0; + + private bool _isDragging; + private double _dragStartX; + private double _dragStartY; + private double _dragStartOffsetX; + private double _dragStartOffsetY; + private bool _initialLayoutDone; + + private static readonly IBrush IndicatorActive = Brushes.Yellow; + private static readonly IBrush IndicatorDim = new SolidColorBrush(Color.FromRgb(60, 60, 60)); + + public MapOverlay(McMap map) + { + ArgumentNullException.ThrowIfNull(map); + + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + Focusable = true; + Background = Brushes.Black; + + _mapBlock = new TextBlock + { + TextWrapping = TextWrapping.NoWrap, + Padding = new Thickness(0), + Margin = new Thickness(0), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + }; + + var border = new Border + { + BorderBrush = Brushes.White, + BorderThickness = new Thickness(1), + Padding = new Thickness(0), + Child = _mapBlock, + }; + + _headerBlock = new TextBlock + { + Foreground = Brushes.White, + Background = Brushes.Black, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Top, + Padding = new Thickness(1, 0), + }; + + _controlsBlock = new TextBlock + { + Text = Translations.bot_map_tui_controls, + Foreground = Brushes.Gray, + Background = Brushes.Black, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Bottom, + Padding = new Thickness(1, 0), + }; + + _cornerTL = CreateCornerIndicator(HorizontalAlignment.Left, VerticalAlignment.Top, "\u25E4"); + _cornerTR = CreateCornerIndicator(HorizontalAlignment.Right, VerticalAlignment.Top, "\u25E5"); + _cornerBL = CreateCornerIndicator(HorizontalAlignment.Left, VerticalAlignment.Bottom, "\u25E3"); + _cornerBR = CreateCornerIndicator(HorizontalAlignment.Right, VerticalAlignment.Bottom, "\u25E2"); + + Children.Add(border); + Children.Add(_headerBlock); + Children.Add(_controlsBlock); + Children.Add(_cornerTL); + Children.Add(_cornerTR); + Children.Add(_cornerBL); + Children.Add(_cornerBR); + + AttachedToVisualTree += (_, _) => + { + AddHandler(KeyDownEvent, OnTunnelKeyDown, RoutingStrategies.Tunnel); + AddHandler(TextInputEvent, OnTunnelTextInput, RoutingStrategies.Tunnel); + Focus(); + }; + + DetachedFromVisualTree += (_, _) => + { + RemoveHandler(KeyDownEvent, OnTunnelKeyDown); + RemoveHandler(TextInputEvent, OnTunnelTextInput); + }; + + _map = map; + _scale = double.MinValue; + UpdateHeaderText(); + } + + private static TextBlock CreateCornerIndicator(HorizontalAlignment hAlign, VerticalAlignment vAlign, string glyph) + { + return new TextBlock + { + Text = glyph, + Background = Brushes.Black, + Foreground = IndicatorDim, + HorizontalAlignment = hAlign, + VerticalAlignment = vAlign, + Padding = new Thickness(0), + }; + } + + public void UpdateMap(McMap map) + { + _map = map; + + if (map.Colors is null || map.Width == 0 || map.Height == 0) + return; + + RecalculateFitScale(); + _scale = _fitScale; + _offsetX = 0; + _offsetY = 0; + UpdateHeaderText(); + RenderViewport(); + } + + private void UpdateHeaderText() + { + string zoomText = _scale > 0 ? (_scale * 100).ToString("F1") : "0.0"; + _headerBlock.Text = string.Format(Translations.bot_map_tui_header, + _map.MapId, _map.Width, _map.Height, zoomText); + } + + protected override void OnSizeChanged(SizeChangedEventArgs e) + { + base.OnSizeChanged(e); + + if (_map?.Colors is null || _map.Width == 0 || _map.Height == 0) + return; + + RecalculateFitScale(); + + if (!_initialLayoutDone) + { + _scale = _fitScale; + _offsetX = 0; + _offsetY = 0; + _initialLayoutDone = true; + } + else + { + _scale = Math.Clamp(_scale, _fitScale, MaxScale); + } + + ClampOffset(); + UpdateHeaderText(); + RenderViewport(); + } + + #region Scale / Offset + + private void GetViewportCells(out int viewW, out int viewH) + { + viewW = Math.Max(1, (int)Bounds.Width - 2); + viewH = Math.Max(1, (int)Bounds.Height - 2); + } + + private void RecalculateFitScale() + { + GetViewportCells(out int viewW, out int viewH); + int viewPixelsH = viewH * 2; + + double scaleX = (double)viewW / _map.Width; + double scaleY = (double)viewPixelsH / _map.Height; + _fitScale = Math.Min(scaleX, scaleY); + + if (_fitScale > MaxScale) + _fitScale = MaxScale; + } + + private void ClampOffset() + { + GetViewportCells(out int viewW, out int viewH); + int viewPixelsH = viewH * 2; + + double visibleMapW = viewW / _scale; + double visibleMapH = viewPixelsH / _scale; + + double maxOffX = Math.Max(0, _map.Width - visibleMapW); + double maxOffY = Math.Max(0, _map.Height - visibleMapH); + + _offsetX = Math.Clamp(_offsetX, 0, maxOffX); + _offsetY = Math.Clamp(_offsetY, 0, maxOffY); + } + + private double NextStepScale(bool zoomIn) + { + if (zoomIn) + { + double next = Math.Floor(_scale / ZoomStep + 1.0 - 1e-9) * ZoomStep; + if (next <= _scale + 1e-9) + next += ZoomStep; + return Math.Clamp(next, _fitScale, MaxScale); + } + else + { + double prev = Math.Ceiling(_scale / ZoomStep - 1.0 + 1e-9) * ZoomStep; + if (prev >= _scale - 1e-9) + prev -= ZoomStep; + return Math.Clamp(prev, _fitScale, MaxScale); + } + } + + private void ZoomAtCenter(bool zoomIn) + { + if (_map?.Colors is null) return; + + GetViewportCells(out int viewW, out int viewH); + int viewPixelsH = viewH * 2; + + double centerMapX = _offsetX + (viewW / 2.0) / _scale; + double centerMapY = _offsetY + (viewPixelsH / 2.0) / _scale; + + double newScale = NextStepScale(zoomIn); + if (Math.Abs(newScale - _scale) < 1e-12) + return; + + _scale = newScale; + + _offsetX = centerMapX - (viewW / 2.0) / _scale; + _offsetY = centerMapY - (viewPixelsH / 2.0) / _scale; + ClampOffset(); + UpdateHeaderText(); + RenderViewport(); + } + + #endregion + + #region Rendering + + private void RenderViewport() + { + if (_map?.Colors is null || _map.Width == 0 || _map.Height == 0) + return; + + GetViewportCells(out int viewW, out int viewH); + int viewPixelsH = viewH * 2; + + int mapW = _map.Width; + int mapH = _map.Height; + byte[] colors = _map.Colors; + + int renderCols = Math.Min(viewW, (int)Math.Ceiling(mapW * _scale)); + int renderPixelRows = Math.Min(viewPixelsH, (int)Math.Ceiling(mapH * _scale)); + int renderTextRows = (renderPixelRows + 1) / 2; + + _mapBlock.Inlines ??= []; + _mapBlock.Inlines.Clear(); + + double invScale = 1.0 / _scale; + + for (int r = 0; r < renderTextRows; r++) + { + if (r > 0) + _mapBlock.Inlines.Add(new LineBreak()); + + IBrush? batchFg = null; + IBrush? batchBg = null; + int batchLen = 0; + + for (int c = 0; c < renderCols; c++) + { + int srcX = Math.Clamp((int)(_offsetX + c * invScale), 0, mapW - 1); + int srcTopY = Math.Clamp((int)(_offsetY + (r * 2) * invScale), 0, mapH - 1); + int srcBotY = Math.Clamp((int)(_offsetY + (r * 2 + 1) * invScale), 0, mapH - 1); + + ColorRGBA top = MapColors.ColorByteToRGBA(colors[srcX + srcTopY * mapW]); + ColorRGBA bot = MapColors.ColorByteToRGBA(colors[srcX + srcBotY * mapW]); + + var fg = new SolidColorBrush(Color.FromRgb(top.R, top.G, top.B)); + var bg = new SolidColorBrush(Color.FromRgb(bot.R, bot.G, bot.B)); + + if (batchLen > 0 && ColorsEqual(batchFg!, fg) && ColorsEqual(batchBg!, bg)) + { + batchLen++; + } + else + { + if (batchLen > 0) + FlushBatch(batchFg!, batchBg!, batchLen); + + batchFg = fg; + batchBg = bg; + batchLen = 1; + } + } + + if (batchLen > 0) + FlushBatch(batchFg!, batchBg!, batchLen); + } + + UpdateCornerIndicators(); + } + + private void FlushBatch(IBrush fg, IBrush bg, int count) + { + _mapBlock.Inlines!.Add(new Run(new string('\u2580', count)) + { + Foreground = fg, + Background = bg, + }); + } + + private static bool ColorsEqual(IBrush a, IBrush b) + { + if (a is SolidColorBrush sa && b is SolidColorBrush sb) + return sa.Color == sb.Color; + return false; + } + + private void UpdateCornerIndicators() + { + GetViewportCells(out int viewW, out int viewH); + int viewPixelsH = viewH * 2; + + double visibleW = viewW / _scale; + double visibleH = viewPixelsH / _scale; + + bool moreLeft = _offsetX > OffsetEpsilon; + bool moreTop = _offsetY > OffsetEpsilon; + bool moreRight = _offsetX + visibleW < _map.Width - OffsetEpsilon; + bool moreBottom = _offsetY + visibleH < _map.Height - OffsetEpsilon; + + _cornerTL.Foreground = (moreLeft || moreTop) ? IndicatorActive : IndicatorDim; + _cornerTR.Foreground = (moreRight || moreTop) ? IndicatorActive : IndicatorDim; + _cornerBL.Foreground = (moreLeft || moreBottom) ? IndicatorActive : IndicatorDim; + _cornerBR.Foreground = (moreRight || moreBottom) ? IndicatorActive : IndicatorDim; + } + + #endregion + + #region Mouse interaction + + protected override void OnPointerWheelChanged(PointerWheelEventArgs e) + { + ZoomAtCenter(e.Delta.Y > 0); + e.Handled = true; + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + var props = e.GetCurrentPoint(this).Properties; + if (!props.IsLeftButtonPressed) + return; + + _isDragging = true; + var pos = e.GetPosition(this); + _dragStartX = pos.X; + _dragStartY = pos.Y; + _dragStartOffsetX = _offsetX; + _dragStartOffsetY = _offsetY; + e.Pointer.Capture(this); + e.Handled = true; + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + if (!_isDragging) return; + + var pos = e.GetPosition(this); + double dxCells = pos.X - _dragStartX; + double dyCells = pos.Y - _dragStartY; + + _offsetX = _dragStartOffsetX - dxCells / _scale; + _offsetY = _dragStartOffsetY - dyCells * 2.0 / _scale; + ClampOffset(); + RenderViewport(); + e.Handled = true; + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + if (!_isDragging) return; + + _isDragging = false; + e.Pointer.Capture(null); + e.Handled = true; + } + + #endregion + + #region Keyboard interaction + + private void OnTunnelKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key is Key.Escape or Key.E) + { + TuiConsoleBackend.Instance?.DismissOverlay(); + e.Handled = true; + } + } + + private void OnTunnelTextInput(object? sender, TextInputEventArgs e) + { + if (e.Text is "+" or "=") + { + ZoomAtCenter(true); + e.Handled = true; + } + else if (e.Text is "-") + { + ZoomAtCenter(false); + e.Handled = true; + } + } + + protected override void OnKeyDown(KeyEventArgs e) + { + switch (e.Key) + { + case Key.Escape: + case Key.E: + TuiConsoleBackend.Instance?.DismissOverlay(); + e.Handled = true; + return; + + case Key.Add: + ZoomAtCenter(true); + e.Handled = true; + return; + + case Key.Subtract: + ZoomAtCenter(false); + e.Handled = true; + return; + + case Key.Left: + _offsetX -= KeyPanStep / _scale; + ClampOffset(); + RenderViewport(); + e.Handled = true; + return; + + case Key.Right: + _offsetX += KeyPanStep / _scale; + ClampOffset(); + RenderViewport(); + e.Handled = true; + return; + + case Key.Up: + _offsetY -= KeyPanStep / _scale; + ClampOffset(); + RenderViewport(); + e.Handled = true; + return; + + case Key.Down: + _offsetY += KeyPanStep / _scale; + ClampOffset(); + RenderViewport(); + e.Handled = true; + return; + } + + base.OnKeyDown(e); + } + + #endregion + } +} diff --git a/MinecraftClient/Tui/McColorParser.cs b/MinecraftClient/Tui/McColorParser.cs new file mode 100644 index 00000000..8f3ac5f2 --- /dev/null +++ b/MinecraftClient/Tui/McColorParser.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + /// + /// Parses Minecraft § color codes and produces Avalonia Inlines for rich text display. + /// + public static class McColorParser + { + private static readonly Dictionary ColorMap = new() + { + { '0', new SolidColorBrush(Color.FromRgb(0, 0, 0)) }, + { '1', new SolidColorBrush(Color.FromRgb(0, 0, 170)) }, + { '2', new SolidColorBrush(Color.FromRgb(0, 170, 0)) }, + { '3', new SolidColorBrush(Color.FromRgb(0, 170, 170)) }, + { '4', new SolidColorBrush(Color.FromRgb(170, 0, 0)) }, + { '5', new SolidColorBrush(Color.FromRgb(170, 0, 170)) }, + { '6', new SolidColorBrush(Color.FromRgb(255, 170, 0)) }, + { '7', new SolidColorBrush(Color.FromRgb(170, 170, 170)) }, + { '8', new SolidColorBrush(Color.FromRgb(85, 85, 85)) }, + { '9', new SolidColorBrush(Color.FromRgb(85, 85, 255)) }, + { 'a', new SolidColorBrush(Color.FromRgb(85, 255, 85)) }, + { 'b', new SolidColorBrush(Color.FromRgb(85, 255, 255)) }, + { 'c', new SolidColorBrush(Color.FromRgb(255, 85, 85)) }, + { 'd', new SolidColorBrush(Color.FromRgb(255, 85, 255)) }, + { 'e', new SolidColorBrush(Color.FromRgb(255, 255, 85)) }, + { 'f', Brushes.White }, + }; + + public static TextBlock CreateColoredTextBlock(string text, TextWrapping wrapping = TextWrapping.Wrap) + { + var tb = new TextBlock + { + TextWrapping = wrapping, + Padding = new Avalonia.Thickness(0), + Margin = new Avalonia.Thickness(0), + }; + + if (string.IsNullOrEmpty(text) || !text.Contains('§')) + { + tb.Text = text ?? ""; + tb.Foreground = Brushes.White; + return tb; + } + + tb.Background = Brushes.Black; + + IBrush currentColor = Brushes.White; + bool bold = false; + bool italic = false; + bool underline = false; + bool strikethrough = false; + int start = 0; + + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '§' && i + 1 < text.Length) + { + if (i > start) + AddRun(tb, text[start..i], currentColor, bold, italic, underline, strikethrough); + + if (text[i + 1] == '#' && i + 8 <= text.Length + && TryParseHexColor(text.AsSpan(i + 2, 6), out var hexBrush)) + { + currentColor = hexBrush; + bold = false; + italic = false; + underline = false; + strikethrough = false; + i += 7; + start = i + 1; + continue; + } + + char code = char.ToLower(text[i + 1]); + + if (ColorMap.TryGetValue(code, out var brush)) + { + currentColor = brush; + bold = false; + italic = false; + underline = false; + strikethrough = false; + } + else + { + switch (code) + { + case 'l': bold = true; break; + case 'o': italic = true; break; + case 'n': underline = true; break; + case 'm': strikethrough = true; break; + case 'r': + currentColor = Brushes.White; + bold = false; + italic = false; + underline = false; + strikethrough = false; + break; + } + } + + i++; + start = i + 1; + } + } + + if (start < text.Length) + AddRun(tb, text[start..], currentColor, bold, italic, underline, strikethrough); + + if (tb.Inlines?.Count == 0) + { + tb.Text = ""; + tb.Foreground = Brushes.White; + } + + return tb; + } + + private static bool TryParseHexColor(ReadOnlySpan hex, out IBrush brush) + { + if (hex.Length == 6 + && byte.TryParse(hex[..2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte r) + && byte.TryParse(hex[2..4], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte g) + && byte.TryParse(hex[4..6], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte b)) + { + brush = new SolidColorBrush(Color.FromRgb(r, g, b)); + return true; + } + brush = Brushes.White; + return false; + } + + private static void AddRun(TextBlock tb, string text, IBrush color, + bool bold, bool italic, bool underline, bool strikethrough) + { + if (text.Length == 0) return; + + tb.Inlines ??= new InlineCollection(); + + TextDecorationCollection? decorations = null; + if (underline || strikethrough) + { + decorations = []; + if (underline) + decorations.Add(new TextDecoration { Location = TextDecorationLocation.Underline }); + if (strikethrough) + decorations.Add(new TextDecoration { Location = TextDecorationLocation.Strikethrough }); + } + + tb.Inlines.Add(new Run(text) + { + Foreground = color, + FontWeight = bold ? FontWeight.Bold : FontWeight.Normal, + FontStyle = italic ? FontStyle.Italic : FontStyle.Normal, + TextDecorations = decorations, + }); + } + } +} diff --git a/MinecraftClient/Tui/MccBannerPanelBuilder.cs b/MinecraftClient/Tui/MccBannerPanelBuilder.cs new file mode 100644 index 00000000..2ebab673 --- /dev/null +++ b/MinecraftClient/Tui/MccBannerPanelBuilder.cs @@ -0,0 +1,183 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Layout; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + internal static class MccBannerPanelBuilder + { + internal static Border Build(string? buildInfo) + { + var contentPanel = new DockPanel { Background = Brushes.Black }; + + var icon = BuildIcon(); + icon.VerticalAlignment = VerticalAlignment.Center; + DockPanel.SetDock(icon, Dock.Left); + contentPanel.Children.Add(icon); + + var infoPanel = new StackPanel + { + Orientation = Orientation.Vertical, + Margin = new Thickness(1, 0, 0, 0), + VerticalAlignment = VerticalAlignment.Center, + }; + + AddTitle(infoPanel); + AddVersionRange(infoPanel); + AddGithub(infoPanel); + + if (Settings.Config.Main.Advanced.ShowGithubStarReminder) + AddStarReminder(infoPanel); + + if (buildInfo is not null) + AddBuildInfo(infoPanel, buildInfo); + + contentPanel.Children.Add(infoPanel); + + return new Border + { + BorderBrush = new SolidColorBrush(Color.FromRgb(80, 80, 80)), + BorderThickness = new Thickness(1), + Background = new SolidColorBrush(Color.FromArgb(240, 20, 20, 20)), + Padding = new Thickness(1, 0), + Child = contentPanel, + Margin = new Thickness(0), + }; + } + + private static void AddTitle(StackPanel panel) + { + var row = new TextBlock(); + row.Inlines!.Add(new Run("Minecraft Console Client") + { Foreground = Pal.Gold, FontWeight = FontWeight.Bold }); + row.Inlines.Add(new Run($" v{Program.Version}") { Foreground = Pal.Aqua }); + panel.Children.Add(row); + } + + private static void AddVersionRange(StackPanel panel) + { + var row = new TextBlock(); + row.Inlines!.Add(Lbl(Translations.mcc_banner_label_mc_versions)); + row.Inlines.Add(Val(Program.MCLowestVersion, Pal.Green)); + row.Inlines.Add(new Run(" - ") { Foreground = Pal.Gray }); + row.Inlines.Add(Val(Program.MCHighestVersion, Pal.Green)); + panel.Children.Add(row); + } + + private static void AddGithub(StackPanel panel) + { + var row = new TextBlock(); + row.Inlines!.Add(Val("https://github.com/MCCTeam", Pal.Gray)); + panel.Children.Add(row); + } + + private static void AddStarReminder(StackPanel panel) + { + var row = new TextBlock(); + row.Inlines!.Add(new Run("\u2b50 ") { Foreground = Pal.Gold }); + row.Inlines.Add(Val("Star us on GitHub!", Pal.Gold)); + panel.Children.Add(row); + } + + private static void AddBuildInfo(StackPanel panel, string buildInfo) + { + panel.Children.Add(new TextBlock + { + Text = buildInfo, + Foreground = Pal.DarkGray, + }); + } + + #region Icon + + private static readonly Color B1 = Color.FromRgb(200, 200, 200); // bezel bright + private static readonly Color B2 = Color.FromRgb(160, 160, 160); // bezel mid + private static readonly Color B3 = Color.FromRgb(120, 120, 120); // bezel dark + private static readonly Color S = Color.FromRgb(20, 20, 20); // screen bg + private static readonly Color C = Color.FromRgb(55, 200, 55); // creeper green + + // @formatter:off + private static readonly Color[,] Pixels = + { + { B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B1, B2 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, C, C, S, S, C, C, S, B3 }, + { B1, S, S, S, S, S, S, S, S, C, C, S, S, C, C, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, C, C, S, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, C, C, C, C, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, C, C, C, C, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, C, S, S, C, S, S, B3 }, + { B1, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, B3 }, + { B2, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3, B3 }, + }; + // @formatter:on + + private static Control BuildIcon() + { + int cols = Pixels.GetLength(1); + int textRows = Pixels.GetLength(0) / 2; + + var panel = new StackPanel + { + Orientation = Orientation.Vertical, + Margin = new Thickness(0), + }; + + for (int row = 0; row < textRows; row++) + { + var line = new TextBlock { Padding = new Thickness(0), Margin = new Thickness(0) }; + + for (int col = 0; col < cols; col++) + { + var topColor = Pixels[row * 2, col]; + var bottomColor = Pixels[row * 2 + 1, col]; + + if (row == 1 && col == 1) + { + line.Inlines!.Add(new Run(" \uff1e_") + { + Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255)), + Background = new SolidColorBrush(S), + }); + col += 4; + topColor = Pixels[row * 2, col]; + bottomColor = Pixels[row * 2 + 1, col]; + } + + line.Inlines!.Add(new Run("\u2580") + { + Foreground = new SolidColorBrush(topColor), + Background = new SolidColorBrush(bottomColor), + }); + } + + panel.Children.Add(line); + } + + return panel; + } + + #endregion + + private static Run Lbl(string text) => + new(text + " ") { Foreground = Pal.Gray }; + + private static Run Val(string text, IBrush color) => + new(text) { Foreground = color }; + + private static class Pal + { + public static readonly IBrush Gray = new SolidColorBrush(Color.FromRgb(170, 170, 170)); + public static readonly IBrush DarkGray = new SolidColorBrush(Color.FromRgb(85, 85, 85)); + public static readonly IBrush Aqua = new SolidColorBrush(Color.FromRgb(85, 255, 255)); + public static readonly IBrush Green = new SolidColorBrush(Color.FromRgb(85, 255, 85)); + public static readonly IBrush Gold = new SolidColorBrush(Color.FromRgb(255, 170, 0)); + } + } +} diff --git a/MinecraftClient/Tui/MccTuiApp.cs b/MinecraftClient/Tui/MccTuiApp.cs new file mode 100644 index 00000000..3db77029 --- /dev/null +++ b/MinecraftClient/Tui/MccTuiApp.cs @@ -0,0 +1,35 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Media; +using Consolonia.Themes; + +namespace MinecraftClient.Tui +{ + public class MccTuiApp : Application + { + public override void Initialize() + { + Styles.Add(new ModernTheme()); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var view = new MainTuiView(); + TuiConsoleBackend.Instance?.SetView(view); + + desktop.MainWindow = new Window + { + Content = view, + Title = "Minecraft Console Client", + Background = Brushes.Black, + Padding = new Thickness(0), + }; + } + + base.OnFrameworkInitializationCompleted(); + } + } +} diff --git a/MinecraftClient/Tui/MinimapBlockColors.json b/MinecraftClient/Tui/MinimapBlockColors.json new file mode 100644 index 00000000..aa833f29 --- /dev/null +++ b/MinecraftClient/Tui/MinimapBlockColors.json @@ -0,0 +1,3864 @@ +{ + "version": "26.1-rc-2", + "colors": { + "AcaciaDoor": [ + 216, + 127, + 51 + ], + "AcaciaFence": [ + 216, + 127, + 51 + ], + "AcaciaFenceGate": [ + 216, + 127, + 51 + ], + "AcaciaHangingSign": [ + 216, + 127, + 51 + ], + "AcaciaPlanks": [ + 216, + 127, + 51 + ], + "AcaciaPressurePlate": [ + 216, + 127, + 51 + ], + "AcaciaSapling": [ + 0, + 124, + 0 + ], + "AcaciaShelf": [ + 216, + 127, + 51 + ], + "AcaciaSign": [ + 216, + 127, + 51 + ], + "AcaciaSlab": [ + 216, + 127, + 51 + ], + "AcaciaTrapdoor": [ + 216, + 127, + 51 + ], + "AcaciaWallHangingSign": [ + 216, + 127, + 51 + ], + "AcaciaWallSign": [ + 216, + 127, + 51 + ], + "AcaciaWood": [ + 76, + 76, + 76 + ], + "Allium": [ + 0, + 124, + 0 + ], + "AmethystBlock": [ + 127, + 63, + 178 + ], + "AmethystCluster": [ + 127, + 63, + 178 + ], + "AncientDebris": [ + 25, + 25, + 25 + ], + "Andesite": [ + 112, + 112, + 112 + ], + "Anvil": [ + 167, + 167, + 167 + ], + "AttachedMelonStem": [ + 0, + 124, + 0 + ], + "AttachedPumpkinStem": [ + 0, + 124, + 0 + ], + "Azalea": [ + 0, + 124, + 0 + ], + "AzureBluet": [ + 0, + 124, + 0 + ], + "Bamboo": [ + 0, + 124, + 0 + ], + "BambooDoor": [ + 229, + 229, + 51 + ], + "BambooFence": [ + 229, + 229, + 51 + ], + "BambooFenceGate": [ + 229, + 229, + 51 + ], + "BambooHangingSign": [ + 229, + 229, + 51 + ], + "BambooMosaic": [ + 229, + 229, + 51 + ], + "BambooMosaicSlab": [ + 229, + 229, + 51 + ], + "BambooPlanks": [ + 229, + 229, + 51 + ], + "BambooPressurePlate": [ + 229, + 229, + 51 + ], + "BambooSapling": [ + 143, + 119, + 72 + ], + "BambooShelf": [ + 229, + 229, + 51 + ], + "BambooSign": [ + 229, + 229, + 51 + ], + "BambooSlab": [ + 229, + 229, + 51 + ], + "BambooTrapdoor": [ + 229, + 229, + 51 + ], + "BambooWallHangingSign": [ + 229, + 229, + 51 + ], + "BambooWallSign": [ + 229, + 229, + 51 + ], + "Barrel": [ + 143, + 119, + 72 + ], + "Barrier": [ + 0, + 0, + 0 + ], + "Basalt": [ + 25, + 25, + 25 + ], + "Beacon": [ + 92, + 219, + 213 + ], + "Bedrock": [ + 112, + 112, + 112 + ], + "BeeNest": [ + 229, + 229, + 51 + ], + "Beehive": [ + 143, + 119, + 72 + ], + "Beetroots": [ + 0, + 124, + 0 + ], + "Bell": [ + 250, + 238, + 77 + ], + "BigDripleaf": [ + 0, + 124, + 0 + ], + "BigDripleafStem": [ + 0, + 124, + 0 + ], + "BirchDoor": [ + 247, + 233, + 163 + ], + "BirchFence": [ + 247, + 233, + 163 + ], + "BirchFenceGate": [ + 247, + 233, + 163 + ], + "BirchHangingSign": [ + 247, + 233, + 163 + ], + "BirchPlanks": [ + 247, + 233, + 163 + ], + "BirchPressurePlate": [ + 247, + 233, + 163 + ], + "BirchSapling": [ + 0, + 124, + 0 + ], + "BirchShelf": [ + 247, + 233, + 163 + ], + "BirchSign": [ + 247, + 233, + 163 + ], + "BirchSlab": [ + 247, + 233, + 163 + ], + "BirchTrapdoor": [ + 247, + 233, + 163 + ], + "BirchWallHangingSign": [ + 247, + 233, + 163 + ], + "BirchWallSign": [ + 247, + 233, + 163 + ], + "BirchWood": [ + 247, + 233, + 163 + ], + "BlackBanner": [ + 143, + 119, + 72 + ], + "BlackCarpet": [ + 25, + 25, + 25 + ], + "BlackConcrete": [ + 25, + 25, + 25 + ], + "BlackConcretePowder": [ + 25, + 25, + 25 + ], + "BlackGlazedTerracotta": [ + 25, + 25, + 25 + ], + "BlackTerracotta": [ + 37, + 22, + 16 + ], + "BlackWallBanner": [ + 143, + 119, + 72 + ], + "BlackWool": [ + 25, + 25, + 25 + ], + "Blackstone": [ + 25, + 25, + 25 + ], + "BlastFurnace": [ + 112, + 112, + 112 + ], + "BlueBanner": [ + 143, + 119, + 72 + ], + "BlueCarpet": [ + 51, + 76, + 178 + ], + "BlueConcrete": [ + 51, + 76, + 178 + ], + "BlueConcretePowder": [ + 51, + 76, + 178 + ], + "BlueGlazedTerracotta": [ + 51, + 76, + 178 + ], + "BlueIce": [ + 160, + 160, + 255 + ], + "BlueOrchid": [ + 0, + 124, + 0 + ], + "BlueTerracotta": [ + 76, + 62, + 92 + ], + "BlueWallBanner": [ + 143, + 119, + 72 + ], + "BlueWool": [ + 51, + 76, + 178 + ], + "BoneBlock": [ + 247, + 233, + 163 + ], + "Bookshelf": [ + 143, + 119, + 72 + ], + "BrainCoral": [ + 242, + 127, + 165 + ], + "BrainCoralBlock": [ + 242, + 127, + 165 + ], + "BrainCoralFan": [ + 242, + 127, + 165 + ], + "BrainCoralWallFan": [ + 242, + 127, + 165 + ], + "BrewingStand": [ + 167, + 167, + 167 + ], + "BrickSlab": [ + 153, + 51, + 51 + ], + "Bricks": [ + 153, + 51, + 51 + ], + "BrownBanner": [ + 143, + 119, + 72 + ], + "BrownCarpet": [ + 102, + 76, + 51 + ], + "BrownConcrete": [ + 102, + 76, + 51 + ], + "BrownConcretePowder": [ + 102, + 76, + 51 + ], + "BrownGlazedTerracotta": [ + 102, + 76, + 51 + ], + "BrownMushroom": [ + 102, + 76, + 51 + ], + "BrownMushroomBlock": [ + 151, + 109, + 77 + ], + "BrownTerracotta": [ + 76, + 50, + 35 + ], + "BrownWallBanner": [ + 143, + 119, + 72 + ], + "BrownWool": [ + 102, + 76, + 51 + ], + "BubbleColumn": [ + 64, + 64, + 255 + ], + "BubbleCoral": [ + 127, + 63, + 178 + ], + "BubbleCoralBlock": [ + 127, + 63, + 178 + ], + "BubbleCoralFan": [ + 127, + 63, + 178 + ], + "BubbleCoralWallFan": [ + 127, + 63, + 178 + ], + "BuddingAmethyst": [ + 127, + 63, + 178 + ], + "Bush": [ + 0, + 124, + 0 + ], + "Cactus": [ + 0, + 124, + 0 + ], + "CactusFlower": [ + 242, + 127, + 165 + ], + "Calcite": [ + 209, + 177, + 161 + ], + "Campfire": [ + 129, + 86, + 49 + ], + "Carrots": [ + 0, + 124, + 0 + ], + "CartographyTable": [ + 143, + 119, + 72 + ], + "CarvedPumpkin": [ + 216, + 127, + 51 + ], + "Cauldron": [ + 112, + 112, + 112 + ], + "CaveVines": [ + 0, + 124, + 0 + ], + "CaveVinesPlant": [ + 0, + 124, + 0 + ], + "ChainCommandBlock": [ + 102, + 127, + 51 + ], + "CherryDoor": [ + 209, + 177, + 161 + ], + "CherryFence": [ + 209, + 177, + 161 + ], + "CherryFenceGate": [ + 209, + 177, + 161 + ], + "CherryHangingSign": [ + 160, + 77, + 78 + ], + "CherryLeaves": [ + 242, + 127, + 165 + ], + "CherryPlanks": [ + 209, + 177, + 161 + ], + "CherryPressurePlate": [ + 209, + 177, + 161 + ], + "CherrySapling": [ + 242, + 127, + 165 + ], + "CherryShelf": [ + 209, + 177, + 161 + ], + "CherrySign": [ + 209, + 177, + 161 + ], + "CherrySlab": [ + 209, + 177, + 161 + ], + "CherryTrapdoor": [ + 209, + 177, + 161 + ], + "CherryWallHangingSign": [ + 160, + 77, + 78 + ], + "CherryWood": [ + 57, + 41, + 35 + ], + "Chest": [ + 143, + 119, + 72 + ], + "ChippedAnvil": [ + 167, + 167, + 167 + ], + "ChiseledBookshelf": [ + 143, + 119, + 72 + ], + "ChiseledNetherBricks": [ + 112, + 2, + 0 + ], + "ChiseledQuartzBlock": [ + 255, + 252, + 245 + ], + "ChiseledRedSandstone": [ + 216, + 127, + 51 + ], + "ChiseledResinBricks": [ + 159, + 82, + 36 + ], + "ChiseledSandstone": [ + 247, + 233, + 163 + ], + "ChiseledStoneBricks": [ + 112, + 112, + 112 + ], + "ChorusFlower": [ + 127, + 63, + 178 + ], + "ChorusPlant": [ + 127, + 63, + 178 + ], + "Clay": [ + 164, + 168, + 184 + ], + "ClosedEyeblossom": [ + 167, + 167, + 167 + ], + "CoalBlock": [ + 25, + 25, + 25 + ], + "CoalOre": [ + 112, + 112, + 112 + ], + "CoarseDirt": [ + 151, + 109, + 77 + ], + "Cobblestone": [ + 112, + 112, + 112 + ], + "CobblestoneSlab": [ + 112, + 112, + 112 + ], + "Cobweb": [ + 199, + 199, + 199 + ], + "Cocoa": [ + 0, + 124, + 0 + ], + "CommandBlock": [ + 102, + 76, + 51 + ], + "Composter": [ + 143, + 119, + 72 + ], + "Conduit": [ + 92, + 219, + 213 + ], + "CopperBlock": [ + 216, + 127, + 51 + ], + "CopperBulb": [ + 216, + 127, + 51 + ], + "CopperChest": [ + 216, + 127, + 51 + ], + "CopperDoor": [ + 216, + 127, + 51 + ], + "CopperGolemStatue": [ + 216, + 127, + 51 + ], + "CopperGrate": [ + 216, + 127, + 51 + ], + "CopperTrapdoor": [ + 216, + 127, + 51 + ], + "Cornflower": [ + 0, + 124, + 0 + ], + "CrackedNetherBricks": [ + 112, + 2, + 0 + ], + "CrackedStoneBricks": [ + 112, + 112, + 112 + ], + "Crafter": [ + 112, + 112, + 112 + ], + "CraftingTable": [ + 143, + 119, + 72 + ], + "CreakingHeart": [ + 216, + 127, + 51 + ], + "CrimsonDoor": [ + 148, + 63, + 97 + ], + "CrimsonFence": [ + 148, + 63, + 97 + ], + "CrimsonFenceGate": [ + 148, + 63, + 97 + ], + "CrimsonFungus": [ + 112, + 2, + 0 + ], + "CrimsonHangingSign": [ + 148, + 63, + 97 + ], + "CrimsonHyphae": [ + 92, + 25, + 29 + ], + "CrimsonNylium": [ + 189, + 48, + 49 + ], + "CrimsonPlanks": [ + 148, + 63, + 97 + ], + "CrimsonPressurePlate": [ + 148, + 63, + 97 + ], + "CrimsonRoots": [ + 112, + 2, + 0 + ], + "CrimsonShelf": [ + 148, + 63, + 97 + ], + "CrimsonSign": [ + 148, + 63, + 97 + ], + "CrimsonSlab": [ + 148, + 63, + 97 + ], + "CrimsonTrapdoor": [ + 148, + 63, + 97 + ], + "CrimsonWallHangingSign": [ + 148, + 63, + 97 + ], + "CrimsonWallSign": [ + 148, + 63, + 97 + ], + "CryingObsidian": [ + 25, + 25, + 25 + ], + "CutRedSandstone": [ + 216, + 127, + 51 + ], + "CutRedSandstoneSlab": [ + 216, + 127, + 51 + ], + "CutSandstone": [ + 247, + 233, + 163 + ], + "CutSandstoneSlab": [ + 247, + 233, + 163 + ], + "CyanBanner": [ + 143, + 119, + 72 + ], + "CyanCarpet": [ + 76, + 127, + 153 + ], + "CyanConcrete": [ + 76, + 127, + 153 + ], + "CyanConcretePowder": [ + 76, + 127, + 153 + ], + "CyanGlazedTerracotta": [ + 76, + 127, + 153 + ], + "CyanTerracotta": [ + 87, + 92, + 92 + ], + "CyanWallBanner": [ + 143, + 119, + 72 + ], + "CyanWool": [ + 76, + 127, + 153 + ], + "DamagedAnvil": [ + 167, + 167, + 167 + ], + "Dandelion": [ + 0, + 124, + 0 + ], + "DarkOakDoor": [ + 102, + 76, + 51 + ], + "DarkOakFence": [ + 102, + 76, + 51 + ], + "DarkOakFenceGate": [ + 102, + 76, + 51 + ], + "DarkOakPlanks": [ + 102, + 76, + 51 + ], + "DarkOakPressurePlate": [ + 102, + 76, + 51 + ], + "DarkOakSapling": [ + 0, + 124, + 0 + ], + "DarkOakSlab": [ + 102, + 76, + 51 + ], + "DarkOakTrapdoor": [ + 102, + 76, + 51 + ], + "DarkOakWood": [ + 102, + 76, + 51 + ], + "DarkPrismarine": [ + 92, + 219, + 213 + ], + "DarkPrismarineSlab": [ + 92, + 219, + 213 + ], + "DaylightDetector": [ + 143, + 119, + 72 + ], + "DeadBrainCoral": [ + 76, + 76, + 76 + ], + "DeadBrainCoralBlock": [ + 76, + 76, + 76 + ], + "DeadBrainCoralFan": [ + 76, + 76, + 76 + ], + "DeadBrainCoralWallFan": [ + 76, + 76, + 76 + ], + "DeadBubbleCoral": [ + 76, + 76, + 76 + ], + "DeadBubbleCoralBlock": [ + 76, + 76, + 76 + ], + "DeadBubbleCoralFan": [ + 76, + 76, + 76 + ], + "DeadBubbleCoralWallFan": [ + 76, + 76, + 76 + ], + "DeadBush": [ + 143, + 119, + 72 + ], + "DeadFireCoral": [ + 76, + 76, + 76 + ], + "DeadFireCoralBlock": [ + 76, + 76, + 76 + ], + "DeadFireCoralFan": [ + 76, + 76, + 76 + ], + "DeadFireCoralWallFan": [ + 76, + 76, + 76 + ], + "DeadHornCoral": [ + 76, + 76, + 76 + ], + "DeadHornCoralBlock": [ + 76, + 76, + 76 + ], + "DeadHornCoralFan": [ + 76, + 76, + 76 + ], + "DeadHornCoralWallFan": [ + 76, + 76, + 76 + ], + "DeadTubeCoral": [ + 76, + 76, + 76 + ], + "DeadTubeCoralBlock": [ + 76, + 76, + 76 + ], + "DeadTubeCoralFan": [ + 76, + 76, + 76 + ], + "DeadTubeCoralWallFan": [ + 76, + 76, + 76 + ], + "DecoratedPot": [ + 142, + 60, + 46 + ], + "Deepslate": [ + 100, + 100, + 100 + ], + "DeepslateCoalOre": [ + 100, + 100, + 100 + ], + "DeepslateCopperOre": [ + 100, + 100, + 100 + ], + "DeepslateDiamondOre": [ + 100, + 100, + 100 + ], + "DeepslateEmeraldOre": [ + 100, + 100, + 100 + ], + "DeepslateGoldOre": [ + 100, + 100, + 100 + ], + "DeepslateIronOre": [ + 100, + 100, + 100 + ], + "DeepslateLapisOre": [ + 100, + 100, + 100 + ], + "DeepslateRedstoneOre": [ + 100, + 100, + 100 + ], + "DiamondBlock": [ + 92, + 219, + 213 + ], + "DiamondOre": [ + 112, + 112, + 112 + ], + "Diorite": [ + 255, + 252, + 245 + ], + "Dirt": [ + 151, + 109, + 77 + ], + "DirtPath": [ + 151, + 109, + 77 + ], + "Dispenser": [ + 112, + 112, + 112 + ], + "DragonEgg": [ + 25, + 25, + 25 + ], + "DriedGhast": [ + 76, + 76, + 76 + ], + "DriedKelpBlock": [ + 102, + 127, + 51 + ], + "DripstoneBlock": [ + 76, + 50, + 35 + ], + "Dropper": [ + 112, + 112, + 112 + ], + "EmeraldBlock": [ + 0, + 217, + 58 + ], + "EmeraldOre": [ + 112, + 112, + 112 + ], + "EnchantingTable": [ + 153, + 51, + 51 + ], + "EndGateway": [ + 25, + 25, + 25 + ], + "EndPortal": [ + 25, + 25, + 25 + ], + "EndPortalFrame": [ + 102, + 127, + 51 + ], + "EndStone": [ + 247, + 233, + 163 + ], + "EndStoneBricks": [ + 247, + 233, + 163 + ], + "EnderChest": [ + 112, + 112, + 112 + ], + "ExposedCopper": [ + 135, + 107, + 98 + ], + "ExposedCopperBulb": [ + 135, + 107, + 98 + ], + "ExposedCopperChest": [ + 135, + 107, + 98 + ], + "ExposedCopperDoor": [ + 135, + 107, + 98 + ], + "ExposedCopperGolemStatue": [ + 135, + 107, + 98 + ], + "ExposedCopperGrate": [ + 135, + 107, + 98 + ], + "ExposedCopperTrapdoor": [ + 135, + 107, + 98 + ], + "ExposedLightningRod": [ + 135, + 107, + 98 + ], + "Farmland": [ + 151, + 109, + 77 + ], + "Fern": [ + 0, + 124, + 0 + ], + "Fire": [ + 255, + 0, + 0 + ], + "FireCoral": [ + 153, + 51, + 51 + ], + "FireCoralBlock": [ + 153, + 51, + 51 + ], + "FireCoralFan": [ + 153, + 51, + 51 + ], + "FireCoralWallFan": [ + 153, + 51, + 51 + ], + "FireflyBush": [ + 0, + 124, + 0 + ], + "FletchingTable": [ + 143, + 119, + 72 + ], + "FloweringAzalea": [ + 0, + 124, + 0 + ], + "Frogspawn": [ + 64, + 64, + 255 + ], + "FrostedIce": [ + 160, + 160, + 255 + ], + "Furnace": [ + 112, + 112, + 112 + ], + "GlowLichen": [ + 127, + 167, + 150 + ], + "Glowstone": [ + 247, + 233, + 163 + ], + "GoldBlock": [ + 250, + 238, + 77 + ], + "GoldOre": [ + 112, + 112, + 112 + ], + "GoldenDandelion": [ + 0, + 124, + 0 + ], + "Granite": [ + 151, + 109, + 77 + ], + "GrassBlock": [ + 127, + 178, + 56 + ], + "Gravel": [ + 112, + 112, + 112 + ], + "GrayBanner": [ + 143, + 119, + 72 + ], + "GrayCarpet": [ + 76, + 76, + 76 + ], + "GrayConcrete": [ + 76, + 76, + 76 + ], + "GrayConcretePowder": [ + 76, + 76, + 76 + ], + "GrayGlazedTerracotta": [ + 76, + 76, + 76 + ], + "GrayTerracotta": [ + 57, + 41, + 35 + ], + "GrayWallBanner": [ + 143, + 119, + 72 + ], + "GrayWool": [ + 76, + 76, + 76 + ], + "GreenBanner": [ + 143, + 119, + 72 + ], + "GreenCarpet": [ + 102, + 127, + 51 + ], + "GreenConcrete": [ + 102, + 127, + 51 + ], + "GreenConcretePowder": [ + 102, + 127, + 51 + ], + "GreenGlazedTerracotta": [ + 102, + 127, + 51 + ], + "GreenTerracotta": [ + 76, + 82, + 42 + ], + "GreenWallBanner": [ + 143, + 119, + 72 + ], + "GreenWool": [ + 102, + 127, + 51 + ], + "Grindstone": [ + 167, + 167, + 167 + ], + "HangingRoots": [ + 151, + 109, + 77 + ], + "HayBlock": [ + 229, + 229, + 51 + ], + "HeavyCore": [ + 167, + 167, + 167 + ], + "HeavyWeightedPressurePlate": [ + 167, + 167, + 167 + ], + "HoneyBlock": [ + 216, + 127, + 51 + ], + "HoneycombBlock": [ + 216, + 127, + 51 + ], + "Hopper": [ + 112, + 112, + 112 + ], + "HornCoral": [ + 229, + 229, + 51 + ], + "HornCoralBlock": [ + 229, + 229, + 51 + ], + "HornCoralFan": [ + 229, + 229, + 51 + ], + "HornCoralWallFan": [ + 229, + 229, + 51 + ], + "Ice": [ + 160, + 160, + 255 + ], + "InfestedChiseledStoneBricks": [ + 164, + 168, + 184 + ], + "InfestedCobblestone": [ + 164, + 168, + 184 + ], + "InfestedCrackedStoneBricks": [ + 164, + 168, + 184 + ], + "InfestedDeepslate": [ + 100, + 100, + 100 + ], + "InfestedMossyStoneBricks": [ + 164, + 168, + 184 + ], + "InfestedStone": [ + 164, + 168, + 184 + ], + "InfestedStoneBricks": [ + 164, + 168, + 184 + ], + "IronBlock": [ + 167, + 167, + 167 + ], + "IronDoor": [ + 167, + 167, + 167 + ], + "IronOre": [ + 112, + 112, + 112 + ], + "IronTrapdoor": [ + 167, + 167, + 167 + ], + "JackOLantern": [ + 216, + 127, + 51 + ], + "Jigsaw": [ + 153, + 153, + 153 + ], + "Jukebox": [ + 151, + 109, + 77 + ], + "JungleDoor": [ + 151, + 109, + 77 + ], + "JungleFence": [ + 151, + 109, + 77 + ], + "JungleFenceGate": [ + 151, + 109, + 77 + ], + "JunglePlanks": [ + 151, + 109, + 77 + ], + "JunglePressurePlate": [ + 151, + 109, + 77 + ], + "JungleSapling": [ + 0, + 124, + 0 + ], + "JungleSlab": [ + 151, + 109, + 77 + ], + "JungleTrapdoor": [ + 151, + 109, + 77 + ], + "JungleWood": [ + 151, + 109, + 77 + ], + "Kelp": [ + 64, + 64, + 255 + ], + "KelpPlant": [ + 64, + 64, + 255 + ], + "Lantern": [ + 167, + 167, + 167 + ], + "LapisBlock": [ + 74, + 128, + 255 + ], + "LapisOre": [ + 112, + 112, + 112 + ], + "LargeFern": [ + 0, + 124, + 0 + ], + "Lava": [ + 255, + 0, + 0 + ], + "LeafLitter": [ + 102, + 76, + 51 + ], + "Lectern": [ + 143, + 119, + 72 + ], + "Light": [ + 0, + 0, + 0 + ], + "LightBlueBanner": [ + 143, + 119, + 72 + ], + "LightBlueCarpet": [ + 102, + 153, + 216 + ], + "LightBlueConcrete": [ + 102, + 153, + 216 + ], + "LightBlueConcretePowder": [ + 102, + 153, + 216 + ], + "LightBlueGlazedTerracotta": [ + 102, + 153, + 216 + ], + "LightBlueTerracotta": [ + 112, + 108, + 138 + ], + "LightBlueWallBanner": [ + 143, + 119, + 72 + ], + "LightBlueWool": [ + 102, + 153, + 216 + ], + "LightGrayBanner": [ + 143, + 119, + 72 + ], + "LightGrayCarpet": [ + 153, + 153, + 153 + ], + "LightGrayConcrete": [ + 153, + 153, + 153 + ], + "LightGrayConcretePowder": [ + 153, + 153, + 153 + ], + "LightGrayGlazedTerracotta": [ + 153, + 153, + 153 + ], + "LightGrayTerracotta": [ + 135, + 107, + 98 + ], + "LightGrayWallBanner": [ + 143, + 119, + 72 + ], + "LightGrayWool": [ + 153, + 153, + 153 + ], + "LightWeightedPressurePlate": [ + 250, + 238, + 77 + ], + "LightningRod": [ + 216, + 127, + 51 + ], + "Lilac": [ + 0, + 124, + 0 + ], + "LilyOfTheValley": [ + 0, + 124, + 0 + ], + "LilyPad": [ + 0, + 124, + 0 + ], + "LimeBanner": [ + 143, + 119, + 72 + ], + "LimeCarpet": [ + 127, + 204, + 25 + ], + "LimeConcrete": [ + 127, + 204, + 25 + ], + "LimeConcretePowder": [ + 127, + 204, + 25 + ], + "LimeGlazedTerracotta": [ + 127, + 204, + 25 + ], + "LimeTerracotta": [ + 103, + 117, + 53 + ], + "LimeWallBanner": [ + 143, + 119, + 72 + ], + "LimeWool": [ + 127, + 204, + 25 + ], + "Lodestone": [ + 167, + 167, + 167 + ], + "Loom": [ + 143, + 119, + 72 + ], + "MagentaBanner": [ + 143, + 119, + 72 + ], + "MagentaCarpet": [ + 178, + 76, + 216 + ], + "MagentaConcrete": [ + 178, + 76, + 216 + ], + "MagentaConcretePowder": [ + 178, + 76, + 216 + ], + "MagentaGlazedTerracotta": [ + 178, + 76, + 216 + ], + "MagentaTerracotta": [ + 149, + 87, + 108 + ], + "MagentaWallBanner": [ + 143, + 119, + 72 + ], + "MagentaWool": [ + 178, + 76, + 216 + ], + "MagmaBlock": [ + 112, + 2, + 0 + ], + "MangroveDoor": [ + 153, + 51, + 51 + ], + "MangroveFence": [ + 153, + 51, + 51 + ], + "MangroveFenceGate": [ + 153, + 51, + 51 + ], + "MangrovePlanks": [ + 153, + 51, + 51 + ], + "MangrovePressurePlate": [ + 153, + 51, + 51 + ], + "MangrovePropagule": [ + 0, + 124, + 0 + ], + "MangroveRoots": [ + 129, + 86, + 49 + ], + "MangroveSlab": [ + 153, + 51, + 51 + ], + "MangroveTrapdoor": [ + 153, + 51, + 51 + ], + "MangroveWood": [ + 153, + 51, + 51 + ], + "Melon": [ + 127, + 204, + 25 + ], + "MelonStem": [ + 0, + 124, + 0 + ], + "MossBlock": [ + 102, + 127, + 51 + ], + "MossCarpet": [ + 102, + 127, + 51 + ], + "MossyCobblestone": [ + 112, + 112, + 112 + ], + "MossyStoneBricks": [ + 112, + 112, + 112 + ], + "MovingPiston": [ + 112, + 112, + 112 + ], + "Mud": [ + 87, + 92, + 92 + ], + "MudBrickSlab": [ + 135, + 107, + 98 + ], + "MudBricks": [ + 135, + 107, + 98 + ], + "MuddyMangroveRoots": [ + 129, + 86, + 49 + ], + "MushroomStem": [ + 199, + 199, + 199 + ], + "Mycelium": [ + 127, + 63, + 178 + ], + "NetherBrickFence": [ + 112, + 2, + 0 + ], + "NetherBrickSlab": [ + 112, + 2, + 0 + ], + "NetherBricks": [ + 112, + 2, + 0 + ], + "NetherGoldOre": [ + 112, + 2, + 0 + ], + "NetherQuartzOre": [ + 112, + 2, + 0 + ], + "NetherSprouts": [ + 76, + 127, + 153 + ], + "NetherWart": [ + 153, + 51, + 51 + ], + "NetherWartBlock": [ + 153, + 51, + 51 + ], + "NetheriteBlock": [ + 25, + 25, + 25 + ], + "Netherrack": [ + 112, + 2, + 0 + ], + "NoteBlock": [ + 143, + 119, + 72 + ], + "OakDoor": [ + 143, + 119, + 72 + ], + "OakFence": [ + 143, + 119, + 72 + ], + "OakFenceGate": [ + 143, + 119, + 72 + ], + "OakPlanks": [ + 143, + 119, + 72 + ], + "OakPressurePlate": [ + 143, + 119, + 72 + ], + "OakSapling": [ + 0, + 124, + 0 + ], + "OakShelf": [ + 143, + 119, + 72 + ], + "OakSign": [ + 143, + 119, + 72 + ], + "OakSlab": [ + 143, + 119, + 72 + ], + "OakTrapdoor": [ + 143, + 119, + 72 + ], + "OakWallSign": [ + 143, + 119, + 72 + ], + "OakWood": [ + 143, + 119, + 72 + ], + "Observer": [ + 112, + 112, + 112 + ], + "Obsidian": [ + 25, + 25, + 25 + ], + "OchreFroglight": [ + 247, + 233, + 163 + ], + "OpenEyeblossom": [ + 216, + 127, + 51 + ], + "OrangeBanner": [ + 143, + 119, + 72 + ], + "OrangeCarpet": [ + 216, + 127, + 51 + ], + "OrangeConcrete": [ + 216, + 127, + 51 + ], + "OrangeConcretePowder": [ + 216, + 127, + 51 + ], + "OrangeGlazedTerracotta": [ + 216, + 127, + 51 + ], + "OrangeTerracotta": [ + 159, + 82, + 36 + ], + "OrangeTulip": [ + 0, + 124, + 0 + ], + "OrangeWallBanner": [ + 143, + 119, + 72 + ], + "OrangeWool": [ + 216, + 127, + 51 + ], + "OxeyeDaisy": [ + 0, + 124, + 0 + ], + "OxidizedCopper": [ + 22, + 126, + 134 + ], + "OxidizedCopperBulb": [ + 22, + 126, + 134 + ], + "OxidizedCopperChest": [ + 22, + 126, + 134 + ], + "OxidizedCopperDoor": [ + 22, + 126, + 134 + ], + "OxidizedCopperGolemStatue": [ + 22, + 126, + 134 + ], + "OxidizedCopperGrate": [ + 22, + 126, + 134 + ], + "OxidizedCopperTrapdoor": [ + 22, + 126, + 134 + ], + "OxidizedLightningRod": [ + 22, + 126, + 134 + ], + "PackedIce": [ + 160, + 160, + 255 + ], + "PaleHangingMoss": [ + 153, + 153, + 153 + ], + "PaleMossBlock": [ + 153, + 153, + 153 + ], + "PaleMossCarpet": [ + 153, + 153, + 153 + ], + "PaleOakDoor": [ + 255, + 252, + 245 + ], + "PaleOakFence": [ + 255, + 252, + 245 + ], + "PaleOakFenceGate": [ + 255, + 252, + 245 + ], + "PaleOakHangingSign": [ + 255, + 252, + 245 + ], + "PaleOakLeaves": [ + 167, + 167, + 167 + ], + "PaleOakPlanks": [ + 255, + 252, + 245 + ], + "PaleOakPressurePlate": [ + 255, + 252, + 245 + ], + "PaleOakSapling": [ + 167, + 167, + 167 + ], + "PaleOakShelf": [ + 255, + 252, + 245 + ], + "PaleOakSign": [ + 255, + 252, + 245 + ], + "PaleOakSlab": [ + 255, + 252, + 245 + ], + "PaleOakTrapdoor": [ + 255, + 252, + 245 + ], + "PaleOakWallHangingSign": [ + 255, + 252, + 245 + ], + "PaleOakWallSign": [ + 255, + 252, + 245 + ], + "PaleOakWood": [ + 112, + 112, + 112 + ], + "PearlescentFroglight": [ + 242, + 127, + 165 + ], + "Peony": [ + 0, + 124, + 0 + ], + "PetrifiedOakSlab": [ + 143, + 119, + 72 + ], + "PinkBanner": [ + 143, + 119, + 72 + ], + "PinkCarpet": [ + 242, + 127, + 165 + ], + "PinkConcrete": [ + 242, + 127, + 165 + ], + "PinkConcretePowder": [ + 242, + 127, + 165 + ], + "PinkGlazedTerracotta": [ + 242, + 127, + 165 + ], + "PinkPetals": [ + 0, + 124, + 0 + ], + "PinkTerracotta": [ + 160, + 77, + 78 + ], + "PinkTulip": [ + 0, + 124, + 0 + ], + "PinkWallBanner": [ + 143, + 119, + 72 + ], + "PinkWool": [ + 242, + 127, + 165 + ], + "PistonHead": [ + 112, + 112, + 112 + ], + "PitcherCrop": [ + 0, + 124, + 0 + ], + "PitcherPlant": [ + 0, + 124, + 0 + ], + "Podzol": [ + 129, + 86, + 49 + ], + "PointedDripstone": [ + 76, + 50, + 35 + ], + "PolishedAndesite": [ + 112, + 112, + 112 + ], + "PolishedBasalt": [ + 25, + 25, + 25 + ], + "PolishedBlackstonePressurePlate": [ + 25, + 25, + 25 + ], + "PolishedDiorite": [ + 255, + 252, + 245 + ], + "PolishedGranite": [ + 151, + 109, + 77 + ], + "Poppy": [ + 0, + 124, + 0 + ], + "Potatoes": [ + 0, + 124, + 0 + ], + "PowderSnow": [ + 255, + 255, + 255 + ], + "Prismarine": [ + 76, + 127, + 153 + ], + "PrismarineBrickSlab": [ + 92, + 219, + 213 + ], + "PrismarineBricks": [ + 92, + 219, + 213 + ], + "PrismarineSlab": [ + 76, + 127, + 153 + ], + "Pumpkin": [ + 216, + 127, + 51 + ], + "PumpkinStem": [ + 0, + 124, + 0 + ], + "PurpleBanner": [ + 143, + 119, + 72 + ], + "PurpleCarpet": [ + 127, + 63, + 178 + ], + "PurpleConcrete": [ + 127, + 63, + 178 + ], + "PurpleConcretePowder": [ + 127, + 63, + 178 + ], + "PurpleGlazedTerracotta": [ + 127, + 63, + 178 + ], + "PurpleTerracotta": [ + 122, + 73, + 88 + ], + "PurpleWallBanner": [ + 143, + 119, + 72 + ], + "PurpleWool": [ + 127, + 63, + 178 + ], + "PurpurBlock": [ + 178, + 76, + 216 + ], + "PurpurPillar": [ + 178, + 76, + 216 + ], + "PurpurSlab": [ + 178, + 76, + 216 + ], + "QuartzBlock": [ + 255, + 252, + 245 + ], + "QuartzPillar": [ + 255, + 252, + 245 + ], + "QuartzSlab": [ + 255, + 252, + 245 + ], + "RawCopperBlock": [ + 216, + 127, + 51 + ], + "RawGoldBlock": [ + 250, + 238, + 77 + ], + "RawIronBlock": [ + 216, + 175, + 147 + ], + "RedBanner": [ + 143, + 119, + 72 + ], + "RedCarpet": [ + 153, + 51, + 51 + ], + "RedConcrete": [ + 153, + 51, + 51 + ], + "RedConcretePowder": [ + 153, + 51, + 51 + ], + "RedGlazedTerracotta": [ + 153, + 51, + 51 + ], + "RedMushroom": [ + 153, + 51, + 51 + ], + "RedMushroomBlock": [ + 153, + 51, + 51 + ], + "RedNetherBricks": [ + 112, + 2, + 0 + ], + "RedSand": [ + 216, + 127, + 51 + ], + "RedSandstone": [ + 216, + 127, + 51 + ], + "RedSandstoneSlab": [ + 216, + 127, + 51 + ], + "RedTerracotta": [ + 142, + 60, + 46 + ], + "RedTulip": [ + 0, + 124, + 0 + ], + "RedWallBanner": [ + 143, + 119, + 72 + ], + "RedWool": [ + 153, + 51, + 51 + ], + "RedstoneBlock": [ + 255, + 0, + 0 + ], + "RedstoneLamp": [ + 159, + 82, + 36 + ], + "RedstoneOre": [ + 112, + 112, + 112 + ], + "ReinforcedDeepslate": [ + 100, + 100, + 100 + ], + "RepeatingCommandBlock": [ + 127, + 63, + 178 + ], + "ResinBlock": [ + 159, + 82, + 36 + ], + "ResinBrickSlab": [ + 159, + 82, + 36 + ], + "ResinBrickWall": [ + 159, + 82, + 36 + ], + "ResinBricks": [ + 159, + 82, + 36 + ], + "ResinClump": [ + 159, + 82, + 36 + ], + "RespawnAnchor": [ + 25, + 25, + 25 + ], + "RootedDirt": [ + 151, + 109, + 77 + ], + "RoseBush": [ + 0, + 124, + 0 + ], + "Sand": [ + 247, + 233, + 163 + ], + "Sandstone": [ + 247, + 233, + 163 + ], + "SandstoneSlab": [ + 247, + 233, + 163 + ], + "Scaffolding": [ + 247, + 233, + 163 + ], + "Sculk": [ + 25, + 25, + 25 + ], + "SculkCatalyst": [ + 25, + 25, + 25 + ], + "SculkSensor": [ + 76, + 127, + 153 + ], + "SculkShrieker": [ + 25, + 25, + 25 + ], + "SculkVein": [ + 25, + 25, + 25 + ], + "SeaLantern": [ + 255, + 252, + 245 + ], + "SeaPickle": [ + 102, + 127, + 51 + ], + "Seagrass": [ + 64, + 64, + 255 + ], + "ShortDryGrass": [ + 229, + 229, + 51 + ], + "ShortGrass": [ + 0, + 124, + 0 + ], + "Shroomlight": [ + 153, + 51, + 51 + ], + "SlimeBlock": [ + 127, + 178, + 56 + ], + "SmallDripleaf": [ + 0, + 124, + 0 + ], + "SmithingTable": [ + 143, + 119, + 72 + ], + "Smoker": [ + 112, + 112, + 112 + ], + "SmoothQuartz": [ + 255, + 252, + 245 + ], + "SmoothRedSandstone": [ + 216, + 127, + 51 + ], + "SmoothSandstone": [ + 247, + 233, + 163 + ], + "SmoothStone": [ + 112, + 112, + 112 + ], + "SmoothStoneSlab": [ + 112, + 112, + 112 + ], + "SnifferEgg": [ + 153, + 51, + 51 + ], + "Snow": [ + 255, + 255, + 255 + ], + "SnowBlock": [ + 255, + 255, + 255 + ], + "SoulCampfire": [ + 129, + 86, + 49 + ], + "SoulFire": [ + 102, + 153, + 216 + ], + "SoulLantern": [ + 167, + 167, + 167 + ], + "SoulSand": [ + 102, + 76, + 51 + ], + "SoulSoil": [ + 102, + 76, + 51 + ], + "Spawner": [ + 112, + 112, + 112 + ], + "Sponge": [ + 229, + 229, + 51 + ], + "SporeBlossom": [ + 0, + 124, + 0 + ], + "SpruceDoor": [ + 129, + 86, + 49 + ], + "SpruceFence": [ + 129, + 86, + 49 + ], + "SpruceFenceGate": [ + 129, + 86, + 49 + ], + "SprucePlanks": [ + 129, + 86, + 49 + ], + "SprucePressurePlate": [ + 129, + 86, + 49 + ], + "SpruceSapling": [ + 0, + 124, + 0 + ], + "SpruceSlab": [ + 129, + 86, + 49 + ], + "SpruceTrapdoor": [ + 129, + 86, + 49 + ], + "SpruceWallHangingSign": [ + 143, + 119, + 72 + ], + "SpruceWood": [ + 129, + 86, + 49 + ], + "Stone": [ + 112, + 112, + 112 + ], + "StoneBrickSlab": [ + 112, + 112, + 112 + ], + "StoneBricks": [ + 112, + 112, + 112 + ], + "StonePressurePlate": [ + 112, + 112, + 112 + ], + "StoneSlab": [ + 112, + 112, + 112 + ], + "Stonecutter": [ + 112, + 112, + 112 + ], + "StrippedAcaciaWood": [ + 216, + 127, + 51 + ], + "StrippedBirchWood": [ + 247, + 233, + 163 + ], + "StrippedCherryWood": [ + 160, + 77, + 78 + ], + "StrippedCrimsonHyphae": [ + 92, + 25, + 29 + ], + "StrippedDarkOakWood": [ + 102, + 76, + 51 + ], + "StrippedJungleWood": [ + 151, + 109, + 77 + ], + "StrippedOakWood": [ + 143, + 119, + 72 + ], + "StrippedPaleOakWood": [ + 255, + 252, + 245 + ], + "StrippedSpruceWood": [ + 129, + 86, + 49 + ], + "StrippedWarpedHyphae": [ + 86, + 44, + 62 + ], + "StructureBlock": [ + 153, + 153, + 153 + ], + "SugarCane": [ + 0, + 124, + 0 + ], + "Sunflower": [ + 0, + 124, + 0 + ], + "SuspiciousGravel": [ + 112, + 112, + 112 + ], + "SuspiciousSand": [ + 247, + 233, + 163 + ], + "SweetBerryBush": [ + 0, + 124, + 0 + ], + "TallDryGrass": [ + 229, + 229, + 51 + ], + "TallGrass": [ + 0, + 124, + 0 + ], + "TallSeagrass": [ + 64, + 64, + 255 + ], + "Target": [ + 255, + 252, + 245 + ], + "Terracotta": [ + 216, + 127, + 51 + ], + "TestBlock": [ + 153, + 153, + 153 + ], + "TintedGlass": [ + 76, + 76, + 76 + ], + "Tnt": [ + 255, + 0, + 0 + ], + "Torchflower": [ + 0, + 124, + 0 + ], + "TorchflowerCrop": [ + 0, + 124, + 0 + ], + "TrappedChest": [ + 143, + 119, + 72 + ], + "TrialSpawner": [ + 112, + 112, + 112 + ], + "TubeCoral": [ + 51, + 76, + 178 + ], + "TubeCoralBlock": [ + 51, + 76, + 178 + ], + "TubeCoralFan": [ + 51, + 76, + 178 + ], + "TubeCoralWallFan": [ + 51, + 76, + 178 + ], + "Tuff": [ + 57, + 41, + 35 + ], + "TurtleEgg": [ + 247, + 233, + 163 + ], + "TwistingVines": [ + 76, + 127, + 153 + ], + "TwistingVinesPlant": [ + 76, + 127, + 153 + ], + "Vault": [ + 112, + 112, + 112 + ], + "VerdantFroglight": [ + 127, + 167, + 150 + ], + "Vine": [ + 0, + 124, + 0 + ], + "WarpedDoor": [ + 58, + 142, + 140 + ], + "WarpedFence": [ + 58, + 142, + 140 + ], + "WarpedFenceGate": [ + 58, + 142, + 140 + ], + "WarpedFungus": [ + 76, + 127, + 153 + ], + "WarpedHangingSign": [ + 58, + 142, + 140 + ], + "WarpedHyphae": [ + 86, + 44, + 62 + ], + "WarpedNylium": [ + 22, + 126, + 134 + ], + "WarpedPlanks": [ + 58, + 142, + 140 + ], + "WarpedPressurePlate": [ + 58, + 142, + 140 + ], + "WarpedRoots": [ + 76, + 127, + 153 + ], + "WarpedShelf": [ + 58, + 142, + 140 + ], + "WarpedSign": [ + 58, + 142, + 140 + ], + "WarpedSlab": [ + 58, + 142, + 140 + ], + "WarpedTrapdoor": [ + 58, + 142, + 140 + ], + "WarpedWallHangingSign": [ + 58, + 142, + 140 + ], + "WarpedWallSign": [ + 58, + 142, + 140 + ], + "WarpedWartBlock": [ + 20, + 180, + 133 + ], + "Water": [ + 64, + 64, + 255 + ], + "WeatheredCopper": [ + 58, + 142, + 140 + ], + "WeatheredCopperBulb": [ + 58, + 142, + 140 + ], + "WeatheredCopperChest": [ + 58, + 142, + 140 + ], + "WeatheredCopperDoor": [ + 58, + 142, + 140 + ], + "WeatheredCopperGolemStatue": [ + 58, + 142, + 140 + ], + "WeatheredCopperGrate": [ + 58, + 142, + 140 + ], + "WeatheredCopperTrapdoor": [ + 58, + 142, + 140 + ], + "WeatheredLightningRod": [ + 58, + 142, + 140 + ], + "WeepingVines": [ + 112, + 2, + 0 + ], + "WeepingVinesPlant": [ + 112, + 2, + 0 + ], + "WetSponge": [ + 229, + 229, + 51 + ], + "WhiteBanner": [ + 143, + 119, + 72 + ], + "WhiteCarpet": [ + 255, + 255, + 255 + ], + "WhiteConcrete": [ + 255, + 255, + 255 + ], + "WhiteConcretePowder": [ + 255, + 255, + 255 + ], + "WhiteGlazedTerracotta": [ + 255, + 255, + 255 + ], + "WhiteTerracotta": [ + 209, + 177, + 161 + ], + "WhiteTulip": [ + 0, + 124, + 0 + ], + "WhiteWallBanner": [ + 143, + 119, + 72 + ], + "WhiteWool": [ + 255, + 255, + 255 + ], + "Wildflowers": [ + 0, + 124, + 0 + ], + "WitherRose": [ + 0, + 124, + 0 + ], + "YellowBanner": [ + 143, + 119, + 72 + ], + "YellowCarpet": [ + 229, + 229, + 51 + ], + "YellowConcrete": [ + 229, + 229, + 51 + ], + "YellowConcretePowder": [ + 229, + 229, + 51 + ], + "YellowGlazedTerracotta": [ + 229, + 229, + 51 + ], + "YellowTerracotta": [ + 186, + 133, + 36 + ], + "YellowWallBanner": [ + 143, + 119, + 72 + ], + "YellowWool": [ + 229, + 229, + 51 + ] + }, + "transparent": [ + "Air", + "Barrier", + "BlackStainedGlass", + "BlackStainedGlassPane", + "BlueStainedGlass", + "BlueStainedGlassPane", + "BrownStainedGlass", + "BrownStainedGlassPane", + "CaveAir", + "CyanStainedGlass", + "CyanStainedGlassPane", + "Glass", + "GlassPane", + "GrayStainedGlass", + "GrayStainedGlassPane", + "GreenStainedGlass", + "GreenStainedGlassPane", + "Light", + "LightBlueStainedGlass", + "LightBlueStainedGlassPane", + "LightGrayStainedGlass", + "LightGrayStainedGlassPane", + "LimeStainedGlass", + "LimeStainedGlassPane", + "MagentaStainedGlass", + "MagentaStainedGlassPane", + "OrangeStainedGlass", + "OrangeStainedGlassPane", + "PinkStainedGlass", + "PinkStainedGlassPane", + "PurpleStainedGlass", + "PurpleStainedGlassPane", + "RedStainedGlass", + "RedStainedGlassPane", + "StructureVoid", + "TintedGlass", + "VoidAir", + "WhiteStainedGlass", + "WhiteStainedGlassPane", + "YellowStainedGlass", + "YellowStainedGlassPane" + ], + "water": [ + "Water" + ], + "ice": [ + "Ice", + "PackedIce", + "BlueIce", + "FrostedIce" + ], + "map_palette": { + "0": [ + 0, + 0, + 0 + ], + "1": [ + 127, + 178, + 56 + ], + "2": [ + 247, + 233, + 163 + ], + "3": [ + 199, + 199, + 199 + ], + "4": [ + 255, + 0, + 0 + ], + "5": [ + 160, + 160, + 255 + ], + "6": [ + 167, + 167, + 167 + ], + "7": [ + 0, + 124, + 0 + ], + "8": [ + 255, + 255, + 255 + ], + "9": [ + 164, + 168, + 184 + ], + "10": [ + 151, + 109, + 77 + ], + "11": [ + 112, + 112, + 112 + ], + "12": [ + 64, + 64, + 255 + ], + "13": [ + 143, + 119, + 72 + ], + "14": [ + 255, + 252, + 245 + ], + "15": [ + 216, + 127, + 51 + ], + "16": [ + 178, + 76, + 216 + ], + "17": [ + 102, + 153, + 216 + ], + "18": [ + 229, + 229, + 51 + ], + "19": [ + 127, + 204, + 25 + ], + "20": [ + 242, + 127, + 165 + ], + "21": [ + 76, + 76, + 76 + ], + "22": [ + 153, + 153, + 153 + ], + "23": [ + 76, + 127, + 153 + ], + "24": [ + 127, + 63, + 178 + ], + "25": [ + 51, + 76, + 178 + ], + "26": [ + 102, + 76, + 51 + ], + "27": [ + 102, + 127, + 51 + ], + "28": [ + 153, + 51, + 51 + ], + "29": [ + 25, + 25, + 25 + ], + "30": [ + 250, + 238, + 77 + ], + "31": [ + 92, + 219, + 213 + ], + "32": [ + 74, + 128, + 255 + ], + "33": [ + 0, + 217, + 58 + ], + "34": [ + 129, + 86, + 49 + ], + "35": [ + 112, + 2, + 0 + ], + "36": [ + 209, + 177, + 161 + ], + "37": [ + 159, + 82, + 36 + ], + "38": [ + 149, + 87, + 108 + ], + "39": [ + 112, + 108, + 138 + ], + "40": [ + 186, + 133, + 36 + ], + "41": [ + 103, + 117, + 53 + ], + "42": [ + 160, + 77, + 78 + ], + "43": [ + 57, + 41, + 35 + ], + "44": [ + 135, + 107, + 98 + ], + "45": [ + 87, + 92, + 92 + ], + "46": [ + 122, + 73, + 88 + ], + "47": [ + 76, + 62, + 92 + ], + "48": [ + 76, + 50, + 35 + ], + "49": [ + 76, + 82, + 42 + ], + "50": [ + 142, + 60, + 46 + ], + "51": [ + 37, + 22, + 16 + ], + "52": [ + 189, + 48, + 49 + ], + "53": [ + 148, + 63, + 97 + ], + "54": [ + 92, + 25, + 29 + ], + "55": [ + 22, + 126, + 134 + ], + "56": [ + 58, + 142, + 140 + ], + "57": [ + 86, + 44, + 62 + ], + "58": [ + 20, + 180, + 133 + ], + "59": [ + 100, + 100, + 100 + ], + "60": [ + 216, + 175, + 147 + ], + "61": [ + 127, + 167, + 150 + ] + } +} \ No newline at end of file diff --git a/MinecraftClient/Tui/MinimapColorMap.cs b/MinecraftClient/Tui/MinimapColorMap.cs new file mode 100644 index 00000000..b0b09596 --- /dev/null +++ b/MinecraftClient/Tui/MinimapColorMap.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json; +using Avalonia.Media; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Tui +{ + /// + /// Maps block Materials to minimap colors using data extracted from Minecraft's + /// official MapColor table. Colors are loaded from the embedded MinimapBlockColors.json + /// resource generated by tools/gen_block_color_map.py. + /// + public static class MinimapColorMap + { + public static readonly Color WaterColor = Color.FromRgb(64, 64, 255); + public static readonly Color IceColor = Color.FromRgb(160, 160, 255); + public static readonly Color LavaColor = Color.FromRgb(255, 100, 0); + public static readonly Color DefaultColor = Color.FromRgb(60, 60, 60); + public static readonly Color VoidColor = Color.FromRgb(0, 0, 0); + public static readonly Color CaveBorderColor = Color.FromRgb(16, 16, 16); + public static readonly Color CaveSolidColor = Color.FromRgb(24, 20, 18); + + private static readonly FrozenDictionary ColorTable; + private static readonly FrozenSet FullyTransparentMats; + private static readonly FrozenSet WaterMats; + private static readonly FrozenSet IceMats; + + static MinimapColorMap() + { + var colors = new Dictionary(); + var transparent = new HashSet(); + var water = new HashSet(); + var ice = new HashSet(); + + try + { + using var stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("MinimapBlockColors.json"); + if (stream is not null) + { + using var doc = JsonDocument.Parse(stream); + var root = doc.RootElement; + + if (root.TryGetProperty("colors", out var colorsEl)) + { + foreach (var prop in colorsEl.EnumerateObject()) + { + if (!Enum.TryParse(prop.Name, out var mat)) + continue; + var arr = prop.Value; + if (arr.GetArrayLength() < 3) continue; + byte r = (byte)arr[0].GetInt32(); + byte g = (byte)arr[1].GetInt32(); + byte b = (byte)arr[2].GetInt32(); + colors[mat] = Color.FromRgb(r, g, b); + } + } + + if (root.TryGetProperty("transparent", out var transEl)) + { + foreach (var item in transEl.EnumerateArray()) + { + if (Enum.TryParse(item.GetString(), out var mat)) + transparent.Add(mat); + } + } + + if (root.TryGetProperty("water", out var waterEl)) + { + foreach (var item in waterEl.EnumerateArray()) + { + if (Enum.TryParse(item.GetString(), out var mat)) + water.Add(mat); + } + } + + if (root.TryGetProperty("ice", out var iceEl)) + { + foreach (var item in iceEl.EnumerateArray()) + { + if (Enum.TryParse(item.GetString(), out var mat)) + ice.Add(mat); + } + } + } + } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"\u00a7e[Minimap] Failed to load color data: {ex.Message}"); + } + + if (transparent.Count == 0) + { + transparent.Add(Material.Air); + transparent.Add(Material.CaveAir); + transparent.Add(Material.VoidAir); + } + if (water.Count == 0) + water.Add(Material.Water); + if (ice.Count == 0) + { + ice.Add(Material.Ice); + ice.Add(Material.PackedIce); + ice.Add(Material.BlueIce); + ice.Add(Material.FrostedIce); + } + + ColorTable = colors.ToFrozenDictionary(); + FullyTransparentMats = transparent.ToFrozenSet(); + WaterMats = water.ToFrozenSet(); + IceMats = ice.ToFrozenSet(); + } + + public static bool IsFullyTransparent(Material m) => FullyTransparentMats.Contains(m); + + /// + /// Returns true for materials that block light propagation (solid, liquids), + /// used by cave mode to find the surface from the player's Y level. + /// Mirrors VoxelMap's lightDampening > 0 check. + /// + public static bool IsLightBlocking(Material m) + => (m == Material.Lava) || (!FullyTransparentMats.Contains(m) && m.IsSolid()); + + public static bool IsWater(Material m) => WaterMats.Contains(m); + + public static bool IsIce(Material m) => IceMats.Contains(m); + + public static Color GetBaseColor(Material m) + { + if (m == Material.Lava) + return LavaColor; + return ColorTable.GetValueOrDefault(m, DefaultColor); + } + + /// + /// Apply Minecraft-style height shading. The shade multiplier depends on + /// the height difference between the current block and the block to its north. + /// Vanilla maps use four brightness levels: LOW (180/255), NORMAL (220/255), + /// HIGH (255/255), and LOWEST (135/255). We use NORMAL as baseline and shift + /// up/down based on delta. + /// + public static Color ApplyHeightShade(Color baseColor, int heightDelta) + { + int multiplier = heightDelta switch + { + > 0 => 255, // higher than neighbor: brightest + 0 => 220, // same height: normal + _ => 180, // lower than neighbor: darker + }; + byte r = (byte)(baseColor.R * multiplier / 255); + byte g = (byte)(baseColor.G * multiplier / 255); + byte b = (byte)(baseColor.B * multiplier / 255); + return Color.FromRgb(r, g, b); + } + + public static Color BlendWaterColor(Color bottomColor, int waterDepth) + { + double alpha = Math.Min(0.85, 0.35 + waterDepth * 0.08); + return Blend(WaterColor, bottomColor, alpha); + } + + public static Color BlendIceColor(Color bottomColor) + { + return Blend(IceColor, bottomColor, 0.35); + } + + /// + /// Darken a color to simulate underground lighting. Cave floors receive + /// a minimum brightness of ~32/255 for non-solid blocks (matching VoxelMap), + /// while solid/unreachable columns render as near-black. + /// + public static Color ApplyCaveDarkening(Color baseColor, double factor = 0.55) + { + byte r = (byte)(baseColor.R * factor); + byte g = (byte)(baseColor.G * factor); + byte b = (byte)(baseColor.B * factor); + return Color.FromRgb(r, g, b); + } + + private static Color Blend(Color top, Color bottom, double topAlpha) + { + byte r = (byte)(top.R * topAlpha + bottom.R * (1.0 - topAlpha)); + byte g = (byte)(top.G * topAlpha + bottom.G * (1.0 - topAlpha)); + byte b = (byte)(top.B * topAlpha + bottom.B * (1.0 - topAlpha)); + return Color.FromRgb(r, g, b); + } + } +} diff --git a/MinecraftClient/Tui/MinimapControl.cs b/MinecraftClient/Tui/MinimapControl.cs new file mode 100644 index 00000000..616b44e5 --- /dev/null +++ b/MinecraftClient/Tui/MinimapControl.cs @@ -0,0 +1,1267 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Tui +{ + public enum CaveModeOption { auto, on, off } + + /// + /// TUI minimap control rendered as a grid of TextBlocks using half-block characters. + /// Zoom is expressed as blocks-per-pixel (1 = 1:1, 16 = 16 blocks per pixel). + /// Entity names are drawn directly on the map below their icon. + /// + public class MinimapControl : UserControl + { + public const int MinZoom = 1; + public const int MaxZoom = 16; + public const int DefaultZoom = 2; + public const int DefaultWidth = 40; + public const int DefaultHeight = 40; + public const int DefaultRefreshMs = 1000; + public const int MinRefreshMs = 100; + public const int MaxRefreshMs = 5000; + + private int _mapWidth; + private int _mapHeight; + private int _cellRows; + + private int _blocksPerPixel = DefaultZoom; + private volatile bool _sampling; + private CancellationTokenSource? _cts; + + private readonly NameDisplayConfig _nameConfig = new(); + + private TextBlock[,] _cells; + private readonly StackPanel _infoRow; + private readonly StackPanel _legendPanel; + private readonly Grid _mapGrid; + private readonly DispatcherTimer _timer; + + private SampleResult? _lastResult; + private int _hoverCol = -1; + private int _hoverRow = -1; + private double _hoverGlobalX; + private double _hoverGlobalY; + + public int BlocksPerPixel + { + get => _blocksPerPixel; + set => _blocksPerPixel = Math.Clamp(value, MinZoom, MaxZoom); + } + + public NameDisplayConfig NameConfig => _nameConfig; + + public TuiTooltipService? TooltipService { get; set; } + + public MinimapPosition Position { get; set; } = MinimapPosition.top_right; + + public CaveModeOption CaveMode { get; set; } = CaveModeOption.auto; + + public int MapPixelWidth => _mapWidth; + public int MapPixelHeight => _mapHeight; + + public int RefreshIntervalMs + { + get => (int)_timer.Interval.TotalMilliseconds; + set => _timer.Interval = TimeSpan.FromMilliseconds(Math.Clamp(value, MinRefreshMs, MaxRefreshMs)); + } + + public MinimapControl() : this(DefaultWidth, DefaultHeight) { } + + public MinimapControl(int width, int height) + { + _mapWidth = Math.Max(10, width); + _mapHeight = Math.Max(4, height % 2 == 0 ? height : height + 1); + _cellRows = _mapHeight / 2; + + _mapGrid = new Grid(); + _cells = BuildGrid(_mapGrid, _cellRows, _mapWidth); + + _infoRow = new StackPanel { Orientation = Orientation.Horizontal }; + _legendPanel = new StackPanel { Orientation = Orientation.Horizontal }; + + var root = new StackPanel + { + Orientation = Orientation.Vertical, + Children = { _mapGrid, _infoRow, _legendPanel }, + }; + + Content = root; + + _mapGrid.PointerMoved += OnMapPointerMoved; + _mapGrid.PointerExited += OnMapPointerExited; + + _timer = new DispatcherTimer + { + Interval = TimeSpan.FromMilliseconds(DefaultRefreshMs), + }; + _timer.Tick += (_, _) => RequestSample(); + } + + public void Resize(int width, int height) + { + _mapWidth = Math.Max(10, width); + _mapHeight = Math.Max(4, height % 2 == 0 ? height : height + 1); + _cellRows = _mapHeight / 2; + + _mapGrid.Children.Clear(); + _mapGrid.RowDefinitions.Clear(); + _mapGrid.ColumnDefinitions.Clear(); + _cells = BuildGrid(_mapGrid, _cellRows, _mapWidth); + } + + private static TextBlock[,] BuildGrid(Grid grid, int rows, int cols) + { + var cells = new TextBlock[rows, cols]; + for (int r = 0; r < rows; r++) + grid.RowDefinitions.Add(new RowDefinition(GridLength.Auto)); + for (int c = 0; c < cols; c++) + grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto)); + + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + var tb = new TextBlock + { + Text = "\u2580", + Foreground = Brushes.Black, + Background = Brushes.Black, + Padding = new Thickness(0), + Margin = new Thickness(0), + FontSize = 1, + }; + Grid.SetRow(tb, r); + Grid.SetColumn(tb, c); + grid.Children.Add(tb); + cells[r, c] = tb; + } + } + return cells; + } + + public void Start() + { + _cts = new CancellationTokenSource(); + _timer.Start(); + RequestSample(); + } + + public void Stop() + { + _timer.Stop(); + _cts?.Cancel(); + _cts?.Dispose(); + _cts = null; + } + + private void RequestSample() + { + if (_sampling) return; + if (McClient.Instance is not McClient client) return; + if (!client.GetTerrainEnabled()) return; + + _sampling = true; + var ct = _cts?.Token ?? CancellationToken.None; + int bpp = _blocksPerPixel; + int w = _mapWidth; + int h = _mapHeight; + + bool showPlayers = _nameConfig.Players; + bool showHostile = _nameConfig.Hostile; + bool showNeutral = _nameConfig.Neutral; + bool showPassive = _nameConfig.Passive; + var caveOpt = CaveMode; + + Task.Run(() => + { + try + { + var result = SampleTerrain(client, bpp, w, h, + showPlayers, showHostile, showNeutral, showPassive, caveOpt, ct); + if (ct.IsCancellationRequested) return; + + Dispatcher.UIThread.Post(() => + { + ApplyPixelBuffer(result, w, h); + UpdateInfoBarAndLegend(client, bpp, result.VisibleCategories, w, + result.CaveModeActive); + }); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"\u00a7e[Minimap] Sample error: {ex.Message}"); + } + finally + { + _sampling = false; + } + }, ct); + } + + internal sealed class EntityLabel + { + public string Name = ""; + public Color LabelColor; + public int PixelX; + public int PixelY; + } + + internal sealed class PixelEntityInfo + { + public string Name = ""; + public MobCategory Category; + public double X, Y, Z; + public float Health; + public float MaxHealth; + public int Priority; + } + + private sealed class SampleResult + { + public Color[,] Pixels = null!; + public (char Ch, Color Fg, Color Bg)?[,] CharOverlay = null!; + public HashSet VisibleCategories = []; + public int[,] Heights = null!; + public Material[,]? BlockTypes; + public List<(Material Mat, int Count)>?[,]? BlockSummary; + public List?[,]? EntityMap; + public int PlayerBlockX; + public int PlayerBlockZ; + public int CenterX; + public int CenterY; + public int Bpp; + public bool CaveModeActive; + } + + private static bool ShouldShowNameLocal(MobCategory cat, + bool showPlayers, bool showHostile, bool showNeutral, bool showPassive) + { + return cat switch + { + MobCategory.Player => showPlayers, + MobCategory.Hostile => showHostile, + MobCategory.Neutral => showNeutral, + MobCategory.Passive => showPassive, + _ => false, + }; + } + + private static SampleResult SampleTerrain(McClient client, int bpp, int mapW, int mapH, + bool showPlayers, bool showHostile, bool showNeutral, bool showPassive, + CaveModeOption caveOpt, CancellationToken ct) + { + var result = new SampleResult + { + Pixels = new Color[mapW, mapH], + CharOverlay = new (char, Color, Color)?[mapW, mapH / 2], + Heights = new int[mapW, mapH], + EntityMap = new List?[mapW, mapH], + BlockTypes = bpp == 1 ? new Material[mapW, mapH] : null, + BlockSummary = bpp > 1 ? new List<(Material, int)>?[mapW, mapH] : null, + Bpp = bpp, + }; + var world = client.GetWorld(); + var playerLoc = client.GetCurrentLocation(); + + int playerBlockX = (int)Math.Floor(playerLoc.X); + int playerBlockZ = (int)Math.Floor(playerLoc.Z); + int playerBlockY = (int)Math.Floor(playerLoc.Y); + + result.PlayerBlockX = playerBlockX; + result.PlayerBlockZ = playerBlockZ; + result.CenterX = mapW / 2; + result.CenterY = mapH / 2; + + var dim = World.GetDimension(); + int minY = dim.minY; + int scanTop = Math.Min(playerBlockY + 32, dim.maxY - 1); + + bool caveMode = ResolveCaveMode(caveOpt, world, dim, playerBlockX, playerBlockY, playerBlockZ, scanTop); + result.CaveModeActive = caveMode; + + var entities = client.GetEntityHandlingEnabled() + ? client.GetEntities() + : null; + + var entityPixels = new Dictionary<(int, int), (Color Color, int Priority)>(); + int centerX = mapW / 2; + int centerY = mapH / 2; + + var nameLabels = new List(); + var uuidNameMap = client.GetOnlinePlayersWithUUID(); + + if (entities is not null) + { + int playerEntityId = client.GetPlayerEntityID(); + foreach (var kvp in entities) + { + if (ct.IsCancellationRequested) return result; + var entity = kvp.Value; + var cat = MinimapEntityClassifier.Classify(entity.Type); + if (cat == MobCategory.NonLiving) continue; + if (kvp.Key == playerEntityId) continue; + + if (!MinimapEntityClassifier.ShouldDisplay(cat, playerLoc.Y, entity.Location.Y)) + continue; + + double relX = (entity.Location.X - playerLoc.X) / bpp; + double relZ = (entity.Location.Z - playerLoc.Z) / bpp; + int px = (int)Math.Floor(relX) + centerX; + int py = (int)Math.Floor(relZ) + centerY; + + if (px < 0 || px >= mapW || py < 0 || py >= mapH) continue; + + var baseColor = MinimapEntityClassifier.GetBaseColor(cat); + Color color; + if (cat == MobCategory.Player) + color = baseColor; + else + color = MinimapEntityClassifier.ApplyDepthFade(baseColor, playerLoc.Y, entity.Location.Y); + int priority = MinimapEntityClassifier.GetPriority(cat); + + var key = (px, py); + if (!entityPixels.TryGetValue(key, out var existing) || priority > existing.Priority) + entityPixels[key] = (color, priority); + + result.VisibleCategories.Add(cat); + + string eName = ResolveEntityName(client, entity, cat, uuidNameMap); + var pixelList = result.EntityMap![px, py] ??= []; + pixelList.Add(new PixelEntityInfo + { + Name = eName, + Category = cat, + X = entity.Location.X, + Y = entity.Location.Y, + Z = entity.Location.Z, + Health = entity.Health, + MaxHealth = -1, + Priority = priority, + }); + + if (ShouldShowNameLocal(cat, showPlayers, showHostile, showNeutral, showPassive)) + { + string name = ResolveEntityName(client, entity, cat, uuidNameMap); + nameLabels.Add(new EntityLabel + { + Name = name, + LabelColor = color, + PixelX = px, + PixelY = py, + }); + } + } + } + + entityPixels[(centerX, centerY)] = (MinimapEntityClassifier.PlayerColor, 5); + result.VisibleCategories.Add(MobCategory.Player); + + var selfList = result.EntityMap![centerX, centerY] ??= []; + selfList.Add(new PixelEntityInfo + { + Name = client.GetUsername(), + Category = MobCategory.Player, + X = playerLoc.X, + Y = playerLoc.Y, + Z = playerLoc.Z, + Health = client.GetHealth(), + MaxHealth = 20f, + Priority = 5, + }); + + ChunkColumn? cachedColumn = null; + int cachedChunkX = int.MinValue, cachedChunkZ = int.MinValue; + + bool[,]? caveMask = caveMode ? new bool[mapW, mapH] : null; + + for (int px = 0; px < mapW; px++) + { + for (int py = 0; py < mapH; py++) + { + if (ct.IsCancellationRequested) return result; + + int baseX = playerBlockX + (px - centerX) * bpp; + int baseZ = playerBlockZ + (py - centerY) * bpp; + + if (caveMode) + { + if (bpp == 1) + { + var (color, surfY, surfMat, inCave) = SampleColumnCave( + world, baseX, baseZ, playerBlockY, minY, dim.maxY - 1, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + result.Pixels[px, py] = color; + result.Heights[px, py] = surfY; + result.BlockTypes![px, py] = surfMat; + caveMask![px, py] = inCave; + } + else + { + var (color, surfY, matSum, inCave) = SampleAreaDominantCave( + world, baseX, baseZ, bpp, playerBlockY, minY, dim.maxY - 1, + result.BlockSummary is not null, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + result.Pixels[px, py] = color; + result.Heights[px, py] = surfY; + if (result.BlockSummary is not null) + result.BlockSummary[px, py] = matSum; + caveMask![px, py] = inCave; + } + } + else + { + if (bpp == 1) + { + var (color, surfY, surfMat) = SampleColumn(world, baseX, baseZ, scanTop, minY, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + result.Pixels[px, py] = color; + result.Heights[px, py] = surfY; + result.BlockTypes![px, py] = surfMat; + } + else + { + var (color, surfY, matSum) = SampleAreaDominant(world, baseX, baseZ, bpp, + scanTop, minY, result.BlockSummary is not null, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + result.Pixels[px, py] = color; + result.Heights[px, py] = surfY; + if (result.BlockSummary is not null) + result.BlockSummary[px, py] = matSum; + } + } + } + } + + for (int px = 0; px < mapW; px++) + { + for (int py = 0; py < mapH; py++) + { + if (entityPixels.ContainsKey((px, py))) continue; + + int northHeight = py > 0 ? result.Heights[px, py - 1] : result.Heights[px, py]; + int delta = result.Heights[px, py] - northHeight; + result.Pixels[px, py] = MinimapColorMap.ApplyHeightShade(result.Pixels[px, py], delta); + } + } + + if (caveMask is not null) + ApplyCaveBorder(result, caveMask, mapW, mapH, entityPixels); + + foreach (var (key, info) in entityPixels) + { + var (px, py) = key; + if (px >= 0 && px < mapW && py >= 0 && py < mapH) + result.Pixels[px, py] = info.Color; + } + + BakeNameLabels(result, nameLabels, mapW, mapH); + + return result; + } + + private static string ResolveEntityName(McClient client, Entity entity, + MobCategory cat, Dictionary? uuidNameMap) + { + if (cat == MobCategory.Player) + { + if (!string.IsNullOrWhiteSpace(entity.Name)) + return entity.Name; + + if (entity.UUID != System.Guid.Empty) + { + var playerInfo = client.GetPlayerInfo(entity.UUID); + if (!string.IsNullOrWhiteSpace(playerInfo?.Name)) + return playerInfo.Name; + + if (uuidNameMap is not null && + uuidNameMap.TryGetValue(entity.UUID.ToString(), out string? mapped) && + !string.IsNullOrWhiteSpace(mapped)) + return mapped; + } + + return "Player"; + } + + if (!string.IsNullOrWhiteSpace(entity.Name)) + return entity.Name; + + return entity.Type.ToString(); + } + + private static void BakeNameLabels(SampleResult result, List labels, + int mapW, int mapH) + { + if (labels.Count == 0) return; + int cellRows = mapH / 2; + + var occupied = new HashSet<(int col, int row)>(); + + labels.Sort((a, b) => + { + int pa = MinimapEntityClassifier.GetPriority( + a.LabelColor == MinimapEntityClassifier.PlayerColor ? MobCategory.Player : + a.LabelColor == MinimapEntityClassifier.HostileColor ? MobCategory.Hostile : + a.LabelColor == MinimapEntityClassifier.NeutralColor ? MobCategory.Neutral : MobCategory.Passive); + int pb = MinimapEntityClassifier.GetPriority( + b.LabelColor == MinimapEntityClassifier.PlayerColor ? MobCategory.Player : + b.LabelColor == MinimapEntityClassifier.HostileColor ? MobCategory.Hostile : + b.LabelColor == MinimapEntityClassifier.NeutralColor ? MobCategory.Neutral : MobCategory.Passive); + return pb.CompareTo(pa); + }); + + foreach (var lbl in labels) + { + int cellRow = (lbl.PixelY / 2) + 1; + if (cellRow >= cellRows) cellRow = lbl.PixelY / 2 - 1; + if (cellRow < 0 || cellRow >= cellRows) continue; + + int startCol = lbl.PixelX - lbl.Name.Length / 2; + startCol = Math.Clamp(startCol, 0, mapW - 1); + + bool fits = true; + int endCol = Math.Min(startCol + lbl.Name.Length, mapW); + for (int c = startCol; c < endCol; c++) + { + if (occupied.Contains((c, cellRow))) + { + fits = false; + break; + } + } + if (!fits) continue; + + for (int i = 0; i < lbl.Name.Length && startCol + i < mapW; i++) + { + int col = startCol + i; + occupied.Add((col, cellRow)); + + var bgTop = result.Pixels[col, cellRow * 2]; + var bgBot = (cellRow * 2 + 1 < mapH) + ? result.Pixels[col, cellRow * 2 + 1] + : bgTop; + + var avgBg = Color.FromRgb( + (byte)((bgTop.R + bgBot.R) / 2), + (byte)((bgTop.G + bgBot.G) / 2), + (byte)((bgTop.B + bgBot.B) / 2)); + + result.CharOverlay[col, cellRow] = (lbl.Name[i], lbl.LabelColor, avgBg); + } + } + } + + private static (Color color, int surfaceY, Material surfaceMat) SampleColumn(World world, int x, int z, + int scanTop, int minY, + ref ChunkColumn? cachedColumn, ref int cachedChunkX, ref int cachedChunkZ) + { + int chunkX = x >> 4; + int chunkZ = z >> 4; + if (chunkX != cachedChunkX || chunkZ != cachedChunkZ) + { + cachedColumn = world[chunkX, chunkZ]; + cachedChunkX = chunkX; + cachedChunkZ = chunkZ; + } + + if (cachedColumn is null) + return (MinimapColorMap.VoidColor, minY, Material.Air); + + int waterDepth = 0; + bool inIce = false; + int surfaceY = minY; + Material topMat = Material.Air; + + for (int y = scanTop; y >= minY; y--) + { + var loc = new Mapping.Location(x, y, z); + var chunk = cachedColumn.GetChunk(loc); + if (chunk is null) continue; + + var block = chunk.GetBlock(loc); + var mat = block.Type; + + if (MinimapColorMap.IsFullyTransparent(mat)) + continue; + + if (MinimapColorMap.IsWater(mat)) + { + if (waterDepth == 0) { surfaceY = y; topMat = mat; } + waterDepth++; + continue; + } + + if (MinimapColorMap.IsIce(mat) && !inIce) + { + if (waterDepth == 0) { surfaceY = y; topMat = mat; } + inIce = true; + continue; + } + + if (waterDepth == 0 && !inIce) { surfaceY = y; topMat = mat; } + + var baseColor = MinimapColorMap.GetBaseColor(mat); + + if (waterDepth > 0) + baseColor = MinimapColorMap.BlendWaterColor(baseColor, waterDepth); + if (inIce) + baseColor = MinimapColorMap.BlendIceColor(baseColor); + + return (baseColor, surfaceY, topMat); + } + + if (waterDepth > 0) + return (MinimapColorMap.WaterColor, surfaceY, topMat); + + return (MinimapColorMap.VoidColor, minY, Material.Air); + } + + private static (Color color, int surfaceY, List<(Material Mat, int Count)>? matSummary) + SampleAreaDominant(World world, int baseX, int baseZ, + int size, int scanTop, int minY, bool collectMats, + ref ChunkColumn? cachedColumn, ref int cachedChunkX, ref int cachedChunkZ) + { + var colorCounts = new Dictionary(); + Dictionary? matCounts = collectMats ? [] : null; + + int step = Math.Max(1, size / 3); + for (int dx = 0; dx < size; dx += step) + { + for (int dz = 0; dz < size; dz += step) + { + var (c, surfY, surfMat) = SampleColumn(world, baseX + dx, baseZ + dz, scanTop, minY, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + + if (colorCounts.TryGetValue(c, out var existing)) + colorCounts[c] = (existing.Count + 1, existing.SumY + surfY); + else + colorCounts[c] = (1, surfY); + + if (matCounts is not null) + { + if (matCounts.TryGetValue(surfMat, out int mc)) + matCounts[surfMat] = mc + 1; + else + matCounts[surfMat] = 1; + } + } + } + + Color best = MinimapColorMap.VoidColor; + int bestCount = 0; + int avgY = minY; + foreach (var kvp in colorCounts) + { + if (kvp.Value.Count > bestCount) + { + bestCount = kvp.Value.Count; + best = kvp.Key; + avgY = kvp.Value.SumY / kvp.Value.Count; + } + } + + List<(Material, int)>? summary = null; + if (matCounts is not null && matCounts.Count > 0) + { + summary = matCounts + .OrderByDescending(kv => kv.Value) + .Select(kv => (kv.Key, kv.Value)) + .ToList(); + } + + return (best, avgY, summary); + } + + /// + /// Determine whether cave mode should be active for this frame. + /// Mirrors VoxelMap's detection: hasCeiling dimensions always use cave mode, + /// otherwise check whether the player's column has a solid block above. + /// + private static bool ResolveCaveMode(CaveModeOption opt, World world, Dimension dim, + int playerX, int playerY, int playerZ, int scanTop) + { + if (opt == CaveModeOption.off) return false; + if (opt == CaveModeOption.on) return true; + + if (dim.hasCeiling) return true; + + for (int y = playerY + 2; y <= scanTop; y++) + { + var mat = world.GetBlock(new Mapping.Location(playerX, y, playerZ)).Type; + if (MinimapColorMap.IsLightBlocking(mat)) + return true; + } + return false; + } + + /// + /// Cave-mode column sampler. Starting from playerY, scans down through air + /// to find the first light-blocking block (the cave floor), or scans up if + /// the player is embedded in solid. Returns the floor block color with cave + /// darkening applied, plus an inCave flag indicating the column has a reachable + /// air pocket at the player's Y level. + /// + private static (Color color, int surfaceY, Material surfaceMat, bool inCave) SampleColumnCave( + World world, int x, int z, int playerY, int minY, int maxY, + ref ChunkColumn? cachedColumn, ref int cachedChunkX, ref int cachedChunkZ) + { + int chunkX = x >> 4; + int chunkZ = z >> 4; + if (chunkX != cachedChunkX || chunkZ != cachedChunkZ) + { + cachedColumn = world[chunkX, chunkZ]; + cachedChunkX = chunkX; + cachedChunkZ = chunkZ; + } + + if (cachedColumn is null) + return (MinimapColorMap.VoidColor, minY, Material.Air, false); + + int caveFloorY = FindCaveFloorY(cachedColumn, x, z, playerY, minY, maxY); + + if (caveFloorY == int.MinValue) + { + var fallback = SampleColumn(world, x, z, maxY, minY, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + return (MinimapColorMap.CaveSolidColor, fallback.surfaceY, fallback.surfaceMat, false); + } + + var loc = new Mapping.Location(x, caveFloorY, z); + var chunk = cachedColumn.GetChunk(loc); + if (chunk is null) + return (MinimapColorMap.CaveSolidColor, caveFloorY, Material.Air, false); + + var block = chunk.GetBlock(loc); + var mat = block.Type; + var color = MinimapColorMap.GetBaseColor(mat); + color = MinimapColorMap.ApplyCaveDarkening(color); + + return (color, caveFloorY, mat, true); + } + + /// + /// Find the cave floor Y at (x, z) by scanning from playerY. + /// If the block at playerY is air-like, scan down for the first solid block. + /// If the block at playerY is solid, scan up (up to playerY + 10) for the + /// first air block, then return that Y (the cave ceiling opening). + /// Returns int.MinValue if no cave floor is found. + /// + private static int FindCaveFloorY(ChunkColumn column, int x, int z, int playerY, int minY, int maxY) + { + var startLoc = new Mapping.Location(x, playerY, z); + var startChunk = column.GetChunk(startLoc); + + bool startIsAir; + if (startChunk is null) + { + startIsAir = true; + } + else + { + var startMat = startChunk.GetBlock(startLoc).Type; + startIsAir = !MinimapColorMap.IsLightBlocking(startMat); + } + + if (startIsAir) + { + for (int y = playerY - 1; y >= minY; y--) + { + var loc = new Mapping.Location(x, y, z); + var chunk = column.GetChunk(loc); + if (chunk is null) continue; + + var mat = chunk.GetBlock(loc).Type; + if (MinimapColorMap.IsLightBlocking(mat)) + return y; + } + return minY; + } + else + { + int upLimit = Math.Min(playerY + 10, maxY); + for (int y = playerY + 1; y <= upLimit; y++) + { + var loc = new Mapping.Location(x, y, z); + var chunk = column.GetChunk(loc); + if (chunk is null) continue; + + var mat = chunk.GetBlock(loc).Type; + if (!MinimapColorMap.IsLightBlocking(mat)) + { + for (int y2 = y - 1; y2 >= minY; y2--) + { + var loc2 = new Mapping.Location(x, y2, z); + var chunk2 = column.GetChunk(loc2); + if (chunk2 is null) continue; + + var mat2 = chunk2.GetBlock(loc2).Type; + if (MinimapColorMap.IsLightBlocking(mat2)) + return y2; + } + return minY; + } + } + return int.MinValue; + } + } + + private static (Color color, int surfaceY, List<(Material Mat, int Count)>? matSummary, bool inCave) + SampleAreaDominantCave(World world, int baseX, int baseZ, + int size, int playerY, int minY, int maxY, bool collectMats, + ref ChunkColumn? cachedColumn, ref int cachedChunkX, ref int cachedChunkZ) + { + var colorCounts = new Dictionary(); + Dictionary? matCounts = collectMats ? [] : null; + int caveCount = 0; + + int step = Math.Max(1, size / 3); + for (int dx = 0; dx < size; dx += step) + { + for (int dz = 0; dz < size; dz += step) + { + var (c, surfY, surfMat, inCave) = SampleColumnCave( + world, baseX + dx, baseZ + dz, playerY, minY, maxY, + ref cachedColumn, ref cachedChunkX, ref cachedChunkZ); + + if (inCave) caveCount++; + + if (colorCounts.TryGetValue(c, out var existing)) + colorCounts[c] = (existing.Count + 1, existing.SumY + surfY); + else + colorCounts[c] = (1, surfY); + + if (matCounts is not null) + { + if (matCounts.TryGetValue(surfMat, out int mc)) + matCounts[surfMat] = mc + 1; + else + matCounts[surfMat] = 1; + } + } + } + + Color best = MinimapColorMap.VoidColor; + int bestCount = 0; + int avgY = minY; + foreach (var kvp in colorCounts) + { + if (kvp.Value.Count > bestCount) + { + bestCount = kvp.Value.Count; + best = kvp.Key; + avgY = kvp.Value.SumY / kvp.Value.Count; + } + } + + List<(Material, int)>? summary = null; + if (matCounts is not null && matCounts.Count > 0) + { + summary = matCounts + .OrderByDescending(kv => kv.Value) + .Select(kv => (kv.Key, kv.Value)) + .ToList(); + } + + int totalSamples = 0; + foreach (var kvp in colorCounts) + totalSamples += kvp.Value.Count; + + bool majorityInCave = caveCount * 2 >= totalSamples; + return (best, avgY, summary, majorityInCave); + } + + /// + /// Draw a 1-pixel dark border around the boundary between cave-reachable pixels + /// and non-cave (solid/surface) pixels, giving the cave region a visible edge. + /// + private static void ApplyCaveBorder(SampleResult result, bool[,] caveMask, + int mapW, int mapH, Dictionary<(int, int), (Color, int)> entityPixels) + { + for (int px = 0; px < mapW; px++) + { + for (int py = 0; py < mapH; py++) + { + if (entityPixels.ContainsKey((px, py))) continue; + if (caveMask[px, py]) continue; + + bool neighborInCave = false; + if (px > 0 && caveMask[px - 1, py]) neighborInCave = true; + if (!neighborInCave && px < mapW - 1 && caveMask[px + 1, py]) neighborInCave = true; + if (!neighborInCave && py > 0 && caveMask[px, py - 1]) neighborInCave = true; + if (!neighborInCave && py < mapH - 1 && caveMask[px, py + 1]) neighborInCave = true; + + if (neighborInCave) + result.Pixels[px, py] = MinimapColorMap.CaveBorderColor; + } + } + } + + private void ApplyPixelBuffer(SampleResult result, int w, int h) + { + int rows = h / 2; + for (int row = 0; row < rows && row < _cellRows; row++) + { + for (int col = 0; col < w && col < _mapWidth; col++) + { + var overlay = result.CharOverlay[col, row]; + if (overlay is not null) + { + var (ch, fg, bg) = overlay.Value; + _cells[row, col].Text = ch.ToString(); + _cells[row, col].Foreground = new SolidColorBrush(fg); + _cells[row, col].Background = new SolidColorBrush(bg); + } + else + { + var topColor = result.Pixels[col, row * 2]; + var bottomColor = result.Pixels[col, row * 2 + 1]; + + _cells[row, col].Text = "\u2580"; + _cells[row, col].Foreground = new SolidColorBrush(topColor); + _cells[row, col].Background = new SolidColorBrush(bottomColor); + } + } + } + + _lastResult = result; + + if (_hoverCol >= 0 && _hoverRow >= 0) + UpdateTooltip(_hoverCol, _hoverRow); + } + + private void OnMapPointerMoved(object? sender, PointerEventArgs e) + { + var pos = e.GetPosition(_mapGrid); + int col = (int)pos.X; + int row = (int)pos.Y; + + if (col < 0 || col >= _mapWidth || row < 0 || row >= _cellRows) + { + HideTooltip(); + return; + } + + _hoverCol = col; + _hoverRow = row; + + if (this.VisualRoot is Visual root + && _mapGrid.TranslatePoint(pos, root) is { } gp) + { + _hoverGlobalX = gp.X; + _hoverGlobalY = gp.Y; + } + else + { + _hoverGlobalX = pos.X; + _hoverGlobalY = pos.Y; + } + + UpdateTooltip(col, row); + } + + private void OnMapPointerExited(object? sender, PointerEventArgs e) + { + HideTooltip(); + } + + private void HideTooltip() + { + _hoverCol = -1; + _hoverRow = -1; + TooltipService?.Hide(); + } + + private void UpdateTooltip(int col, int row) + { + var svc = TooltipService; + var result = _lastResult; + if (svc is null || result is null) { svc?.Hide(); return; } + + int bpp = result.Bpp; + int centerX = result.CenterX; + int centerY = result.CenterY; + + int topPixelY = row * 2; + int botPixelY = row * 2 + 1; + + int baseX = result.PlayerBlockX + (col - centerX) * bpp; + int baseZ_top = result.PlayerBlockZ + (topPixelY - centerY) * bpp; + int baseZ_bot = result.PlayerBlockZ + (botPixelY - centerY) * bpp; + + var lines = new List(); + + if (bpp == 1) + { + int surfY_top = (topPixelY < result.Heights.GetLength(1)) ? result.Heights[col, topPixelY] : 0; + int surfY_bot = (botPixelY < result.Heights.GetLength(1)) ? result.Heights[col, botPixelY] : 0; + + string coordLine = baseZ_top == baseZ_bot + ? $"{baseX}, {surfY_top}, {baseZ_top}" + : $"{baseX}, {surfY_top}, {baseZ_top} / {baseX}, {surfY_bot}, {baseZ_bot}"; + lines.Add(new TuiTooltipLine { Text = coordLine, Foreground = Brushes.White }); + + if (result.BlockTypes is not null) + { + var mat_top = result.BlockTypes[col, topPixelY]; + var mat_bot = (botPixelY < result.BlockTypes.GetLength(1)) + ? result.BlockTypes[col, botPixelY] : mat_top; + string blockLine = mat_top == mat_bot + ? FormatMaterialName(mat_top) + : $"{FormatMaterialName(mat_top)} / {FormatMaterialName(mat_bot)}"; + lines.Add(new TuiTooltipLine { Text = blockLine, Foreground = Brushes.LightGray }); + } + } + else + { + int endX = baseX + bpp - 1; + int endZ_bot = baseZ_bot + bpp - 1; + string coordLine = $"X {baseX}~{endX} Z {baseZ_top}~{endZ_bot}"; + lines.Add(new TuiTooltipLine { Text = coordLine, Foreground = Brushes.White }); + + AppendBlockSummaryLines(result, col, topPixelY, botPixelY, lines); + } + + AppendEntityInfoLines(result, col, topPixelY, botPixelY, lines); + + if (lines.Count == 0) + { + svc.Hide(); + return; + } + + bool preferRight = Position switch + { + MinimapPosition.top_left or MinimapPosition.bottom_left => true, + MinimapPosition.top_right or MinimapPosition.bottom_right => false, + _ => true, + }; + + double mx = _hoverGlobalX; + double my = _hoverGlobalY; + + if (Position == MinimapPosition.center + && this.VisualRoot is Visual root) + { + preferRight = mx < root.Bounds.Width / 2; + } + + svc.Show(mx, my, lines, preferRight); + } + + private void AppendBlockSummaryLines(SampleResult result, int col, int topPy, int botPy, + List lines) + { + if (result.BlockSummary is null) return; + + var merged = new Dictionary(); + MergeBlockCounts(result.BlockSummary, col, topPy, merged); + if (botPy < result.BlockSummary.GetLength(1)) + MergeBlockCounts(result.BlockSummary, col, botPy, merged); + + if (merged.Count == 0) return; + + var sorted = merged.OrderByDescending(kv => kv.Value).Take(4); + + var parts = new List(); + foreach (var kv in sorted) + { + if (kv.Key == Material.Air && merged.Count > 1) continue; + parts.Add(kv.Value > 1 + ? $"{FormatMaterialName(kv.Key)} x{kv.Value}" + : FormatMaterialName(kv.Key)); + } + + if (parts.Count == 0) return; + + lines.Add(new TuiTooltipLine + { + Text = string.Join(", ", parts), + Foreground = Brushes.LightGray, + }); + } + + private static void MergeBlockCounts(List<(Material Mat, int Count)>?[,] summary, + int px, int py, Dictionary target) + { + var list = summary[px, py]; + if (list is null) return; + foreach (var (mat, count) in list) + { + if (target.TryGetValue(mat, out int c)) + target[mat] = c + count; + else + target[mat] = count; + } + } + + private static void AppendEntityInfoLines(SampleResult result, int col, int topPy, int botPy, + List lines) + { + var entityMap = result.EntityMap; + if (entityMap is null) return; + + var combined = new List(); + AddEntitiesFromPixel(entityMap, col, topPy, combined); + if (botPy < entityMap.GetLength(1)) + AddEntitiesFromPixel(entityMap, col, botPy, combined); + + if (combined.Count == 0) return; + + combined.Sort((a, b) => b.Priority.CompareTo(a.Priority)); + int shown = 0; + var seen = new HashSet(); + foreach (var ent in combined) + { + if (shown >= 4) break; + string key = $"{ent.Name}_{ent.Health:F0}"; + if (!seen.Add(key)) continue; + + var catColor = MinimapEntityClassifier.GetBaseColor(ent.Category); + string coordStr = $"({ent.X:F1}, {ent.Y:F1}, {ent.Z:F1})"; + string hpStr = ""; + if (ent.Health > 0) + { + hpStr = ent.MaxHealth > 0 + ? $" HP:{ent.Health:F0}/{ent.MaxHealth:F0}" + : $" HP:{ent.Health:F0}"; + } + + lines.Add(new TuiTooltipLine + { + Text = $"{ent.Name} {coordStr}{hpStr}", + Foreground = new SolidColorBrush(catColor), + }); + shown++; + } + } + + private static void AddEntitiesFromPixel(List?[,] map, + int px, int py, List target) + { + if (px >= 0 && px < map.GetLength(0) && py >= 0 && py < map.GetLength(1)) + { + var list = map[px, py]; + if (list is not null) + target.AddRange(list); + } + } + + private static string FormatMaterialName(Material mat) + { + if (mat == Material.Air) return "Air"; + string raw = mat.ToString(); + return raw.Replace('_', ' '); + } + + private void UpdateInfoBarAndLegend(McClient client, int bpp, + HashSet categories, int mapW, bool caveModeActive) + { + var loc = client.GetCurrentLocation(); + float yaw = client.GetYaw(); + string arrow = GetDirectionArrow(yaw); + + int x = (int)Math.Floor(loc.X); + int y = (int)Math.Floor(loc.Y); + int z = (int)Math.Floor(loc.Z); + + string caveSuffix = caveModeActive ? " \u25bc" : ""; + string coordPart = $"{x}, {y}, {z} {arrow} {bpp}:1{caveSuffix}"; + + var legendParts = new List(); + var legendColors = new List(); + + var sorted = categories + .Where(c => c != MobCategory.NonLiving) + .OrderByDescending(MinimapEntityClassifier.GetPriority); + + int catCount = 0; + foreach (var cat in sorted) + { + if (catCount >= 4) break; + legendParts.Add(MinimapEntityClassifier.GetCategoryLabel(cat)); + legendColors.Add(MinimapEntityClassifier.GetBaseColor(cat)); + catCount++; + } + + int legendLen = 0; + for (int i = 0; i < legendParts.Count; i++) + legendLen += 1 + legendParts[i].Length + (i > 0 ? 1 : 0); + + bool fitsOnOneLine = legendParts.Count > 0 + && coordPart.Length + 2 + legendLen <= mapW; + + _infoRow.Children.Clear(); + _infoRow.Children.Add(new TextBlock + { + Text = coordPart, + Foreground = Brushes.Gray, + Padding = new Thickness(0), + }); + + if (fitsOnOneLine) + { + AppendLegendItems(_infoRow, legendParts, legendColors, leftMargin: 2); + _legendPanel.Children.Clear(); + _legendPanel.IsVisible = false; + } + else + { + _legendPanel.IsVisible = legendParts.Count > 0; + _legendPanel.Children.Clear(); + AppendLegendItems(_legendPanel, legendParts, legendColors, leftMargin: 0); + } + } + + private static void AppendLegendItems(StackPanel panel, + List parts, List colors, int leftMargin) + { + for (int i = 0; i < parts.Count; i++) + { + int ml = i == 0 ? leftMargin : 1; + panel.Children.Add(new TextBlock + { + Text = "\u25cf", + Foreground = new SolidColorBrush(colors[i]), + Padding = new Thickness(0), + Margin = ml > 0 ? new Thickness(ml, 0, 0, 0) : new Thickness(0), + }); + panel.Children.Add(new TextBlock + { + Text = parts[i], + Foreground = Brushes.Gray, + Padding = new Thickness(0), + Margin = new Thickness(0), + }); + } + } + + private static string GetDirectionArrow(float yaw) + { + double normalized = ((yaw % 360) + 360) % 360; + int index = (int)Math.Round(normalized / 45.0) % 8; + return index switch + { + 0 => "\u2193", // S + 1 => "\u2199", // SW + 2 => "\u2190", // W + 3 => "\u2196", // NW + 4 => "\u2191", // N + 5 => "\u2197", // NE + 6 => "\u2192", // E + 7 => "\u2198", // SE + _ => "\u2193", + }; + } + } +} diff --git a/MinecraftClient/Tui/MinimapEntityCategories.json b/MinecraftClient/Tui/MinimapEntityCategories.json new file mode 100644 index 00000000..c80b7c0b --- /dev/null +++ b/MinecraftClient/Tui/MinimapEntityCategories.json @@ -0,0 +1,167 @@ +{ + "version": "26.1-rc-2", + "hostile": [ + "Blaze", + "Bogged", + "Breeze", + "CamelHusk", + "Creaking", + "Creeper", + "Drowned", + "ElderGuardian", + "EnderDragon", + "Endermite", + "Evoker", + "Ghast", + "Giant", + "Guardian", + "Hoglin", + "Husk", + "Illusioner", + "MagmaCube", + "Parched", + "Phantom", + "Piglin", + "PiglinBrute", + "Pillager", + "Ravager", + "Shulker", + "Silverfish", + "Skeleton", + "Slime", + "Stray", + "Vex", + "Vindicator", + "Warden", + "Witch", + "Wither", + "WitherSkeleton", + "Zoglin", + "Zombie", + "ZombieNautilus", + "ZombieVillager" + ], + "passive": [ + "Allay", + "Armadillo", + "Axolotl", + "Bat", + "Camel", + "Cat", + "Chicken", + "Cod", + "Cow", + "Donkey", + "Fox", + "Frog", + "GlowSquid", + "HappyGhast", + "Horse", + "Mooshroom", + "Mule", + "Nautilus", + "Ocelot", + "Parrot", + "Pig", + "Pufferfish", + "Rabbit", + "Salmon", + "Sheep", + "SkeletonHorse", + "Sniffer", + "Squid", + "Strider", + "Tadpole", + "TropicalFish", + "Turtle", + "Villager", + "WanderingTrader", + "ZombieHorse" + ], + "neutral": [ + "Bee", + "CaveSpider", + "CopperGolem", + "Dolphin", + "Enderman", + "Goat", + "IronGolem", + "Llama", + "Panda", + "PolarBear", + "SnowGolem", + "Spider", + "TraderLlama", + "Wolf", + "ZombifiedPiglin" + ], + "non_living": [ + "AcaciaBoat", + "AcaciaChestBoat", + "AreaEffectCloud", + "ArmorStand", + "Arrow", + "BambooChestRaft", + "BambooRaft", + "BirchBoat", + "BirchChestBoat", + "BlockDisplay", + "BreezeWindCharge", + "CherryBoat", + "CherryChestBoat", + "ChestMinecart", + "CommandBlockMinecart", + "DarkOakBoat", + "DarkOakChestBoat", + "DragonFireball", + "Egg", + "EndCrystal", + "EnderPearl", + "EvokerFangs", + "ExperienceBottle", + "ExperienceOrb", + "EyeOfEnder", + "FallingBlock", + "Fireball", + "FireworkRocket", + "FishingBobber", + "FurnaceMinecart", + "GlowItemFrame", + "HopperMinecart", + "Interaction", + "Item", + "ItemDisplay", + "ItemFrame", + "JungleBoat", + "JungleChestBoat", + "LeashKnot", + "LightningBolt", + "LingeringPotion", + "LlamaSpit", + "MangroveBoat", + "MangroveChestBoat", + "Mannequin", + "Marker", + "Minecart", + "OakBoat", + "OakChestBoat", + "OminousItemSpawner", + "Painting", + "PaleOakBoat", + "PaleOakChestBoat", + "ShulkerBullet", + "SmallFireball", + "Snowball", + "SpawnerMinecart", + "SpectralArrow", + "SplashPotion", + "SpruceBoat", + "SpruceChestBoat", + "TextDisplay", + "Tnt", + "TntMinecart", + "Trident", + "WindCharge", + "WitherSkull" + ] +} \ No newline at end of file diff --git a/MinecraftClient/Tui/MinimapEntityClassifier.cs b/MinecraftClient/Tui/MinimapEntityClassifier.cs new file mode 100644 index 00000000..2daf1ea6 --- /dev/null +++ b/MinecraftClient/Tui/MinimapEntityClassifier.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json; +using Avalonia.Media; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Tui +{ + public enum MobCategory + { + Hostile, + Passive, + Neutral, + Player, + NonLiving, + } + + public enum MinimapPosition + { + top_left, + top_right, + center, + bottom_left, + bottom_right, + } + + public sealed class NameDisplayConfig + { + public volatile bool Players = false; + public volatile bool Hostile = false; + public volatile bool Neutral = false; + public volatile bool Passive = false; + + public bool AnyEnabled => Players || Hostile || Neutral || Passive; + + public void SetAll(bool value) + { + Players = value; + Hostile = value; + Neutral = value; + Passive = value; + } + + public bool ShouldShowName(MobCategory category) => category switch + { + MobCategory.Player => Players, + MobCategory.Hostile => Hostile, + MobCategory.Neutral => Neutral, + MobCategory.Passive => Passive, + _ => false, + }; + } + + /// + /// Classifies entities into minimap categories using data extracted from + /// Minecraft's MobCategory assignments. Categories are loaded from the + /// embedded MinimapEntityCategories.json resource generated by + /// tools/gen_entity_category_map.py. + /// + public static class MinimapEntityClassifier + { + public static readonly Color HostileColor = Color.FromRgb(255, 68, 68); + public static readonly Color PassiveColor = Color.FromRgb(68, 255, 68); + public static readonly Color NeutralColor = Color.FromRgb(255, 170, 0); + public static readonly Color PlayerColor = Color.FromRgb(255, 255, 255); + public static readonly Color FadedGray = Color.FromRgb(100, 100, 100); + + private static readonly FrozenDictionary CategoryTable; + + static MinimapEntityClassifier() + { + var table = new Dictionary(); + + try + { + using var stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("MinimapEntityCategories.json"); + if (stream is not null) + { + using var doc = JsonDocument.Parse(stream); + var root = doc.RootElement; + + LoadCategory(root, "hostile", MobCategory.Hostile, table); + LoadCategory(root, "passive", MobCategory.Passive, table); + LoadCategory(root, "neutral", MobCategory.Neutral, table); + LoadCategory(root, "non_living", MobCategory.NonLiving, table); + } + } + catch (Exception ex) + { + ConsoleIO.WriteLogLine($"[Minimap] Failed to load entity categories: {ex.Message}"); + } + + CategoryTable = table.ToFrozenDictionary(); + } + + private static void LoadCategory(JsonElement root, string key, + MobCategory category, Dictionary table) + { + if (!root.TryGetProperty(key, out var arr)) + return; + + foreach (var el in arr.EnumerateArray()) + { + var name = el.GetString(); + if (name is not null && Enum.TryParse(name, out var et)) + table.TryAdd(et, category); + } + } + + public static MobCategory Classify(EntityType type) + { + if (type == EntityType.Player) + return MobCategory.Player; + return CategoryTable.GetValueOrDefault(type, MobCategory.NonLiving); + } + + public static Color GetBaseColor(MobCategory category) => category switch + { + MobCategory.Hostile => HostileColor, + MobCategory.Passive => PassiveColor, + MobCategory.Neutral => NeutralColor, + MobCategory.Player => PlayerColor, + _ => FadedGray, + }; + + public static Color ApplyDepthFade(Color baseColor, double playerY, double entityY) + { + double depth = playerY - entityY; + + if (depth <= 5.0) + return baseColor; + + if (depth >= 15.0) + return FadedGray; + + double t = (depth - 5.0) / 10.0; + return Lerp(baseColor, FadedGray, t); + } + + public static bool ShouldDisplay(MobCategory category, double playerY, double entityY) + { + if (category == MobCategory.Player) + return true; + if (entityY >= playerY) + return true; + return playerY - entityY <= 15.0; + } + + public static int GetPriority(MobCategory category) => category switch + { + MobCategory.Hostile => 4, + MobCategory.Player => 3, + MobCategory.Neutral => 2, + MobCategory.Passive => 1, + _ => 0, + }; + + public static string GetCategoryLabel(MobCategory category) => category switch + { + MobCategory.Hostile => Translations.tui_minimap_legend_hostile, + MobCategory.Passive => Translations.tui_minimap_legend_passive, + MobCategory.Neutral => Translations.tui_minimap_legend_neutral, + MobCategory.Player => Translations.tui_minimap_legend_player, + _ => "?", + }; + + private static Color Lerp(Color a, Color b, double t) + { + byte r = (byte)(a.R + (b.R - a.R) * t); + byte g = (byte)(a.G + (b.G - a.G) * t); + byte bl = (byte)(a.B + (b.B - a.B) * t); + return Color.FromRgb(r, g, bl); + } + } +} diff --git a/MinecraftClient/Tui/ServerStatusPanelBuilder.cs b/MinecraftClient/Tui/ServerStatusPanelBuilder.cs new file mode 100644 index 00000000..bff8fe7c --- /dev/null +++ b/MinecraftClient/Tui/ServerStatusPanelBuilder.cs @@ -0,0 +1,196 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Layout; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + internal static class ServerStatusPanelBuilder + { + private const int MaxSamplePlayers = 10; + private const int FaviconDisplaySize = 16; + + internal static Border Build(Protocol.ServerStatusInfo info) + { + var contentPanel = new DockPanel { Background = Brushes.Black }; + + if (info.FaviconBase64 is not null) + { + var iconGrid = BuildFaviconGrid(info.FaviconBase64, FaviconDisplaySize); + iconGrid.VerticalAlignment = VerticalAlignment.Center; + DockPanel.SetDock(iconGrid, Dock.Left); + contentPanel.Children.Add(iconGrid); + } + + var infoPanel = new StackPanel + { + Orientation = Orientation.Vertical, + Margin = new Thickness(1, 0, 0, 0), + VerticalAlignment = VerticalAlignment.Center, + }; + + AddMotd(infoPanel, info); + AddAddress(infoPanel, info); + AddVersion(infoPanel, info); + AddConnectingAs(infoPanel, info); + AddPing(infoPanel, info); + AddPlayers(infoPanel, info); + AddSamplePlayers(infoPanel, info); + + contentPanel.Children.Add(infoPanel); + + return new Border + { + BorderBrush = new SolidColorBrush(Color.FromRgb(80, 80, 80)), + BorderThickness = new Thickness(1), + Background = new SolidColorBrush(Color.FromArgb(240, 20, 20, 20)), + Padding = new Thickness(1, 0), + Child = contentPanel, + Margin = new Thickness(0), + }; + } + + private static void AddMotd(StackPanel panel, Protocol.ServerStatusInfo info) + { + if (string.IsNullOrEmpty(info.MotdRaw)) + return; + + try + { + string motdFormatted = Protocol.Message.ChatParser.ParseText(info.MotdRaw); + foreach (string line in motdFormatted.Split('\n')) + panel.Children.Add(McColorParser.CreateColoredTextBlock(line, TextWrapping.NoWrap)); + } + catch + { + panel.Children.Add(new TextBlock + { + Text = info.MotdRaw, + Foreground = Brushes.White, + TextWrapping = TextWrapping.NoWrap, + }); + } + } + + private static void AddAddress(StackPanel panel, Protocol.ServerStatusInfo info) + { + var row = new TextBlock(); + row.Inlines!.Add(Label(Translations.mcc_server_info_label_server)); + row.Inlines.Add(Value(info.Host, McColors.Aqua)); + row.Inlines.Add(new Run($":{info.Port}") { Foreground = McColors.Gray }); + panel.Children.Add(row); + } + + private static void AddVersion(StackPanel panel, Protocol.ServerStatusInfo info) + { + string versionClean = Scripting.ChatBot.GetVerbatim(info.VersionName); + var row = new TextBlock(); + row.Inlines!.Add(Label(Translations.mcc_server_info_label_version)); + row.Inlines.Add(Value(versionClean, McColors.Aqua)); + row.Inlines.Add(new Run(" (") { Foreground = McColors.Gray }); + row.Inlines.Add(new Run(string.Format(Translations.mcc_server_info_label_protocol, info.ProtocolVersion)) + { Foreground = McColors.Gray }); + row.Inlines.Add(new Run(")") { Foreground = McColors.Gray }); + panel.Children.Add(row); + } + + private static void AddConnectingAs(StackPanel panel, Protocol.ServerStatusInfo info) + { + if (info.ResolvedProtocol == 0) + return; + + string resolvedMcVer = Protocol.ProtocolHandler.ProtocolVersion2MCVer(info.ResolvedProtocol); + var row = new TextBlock(); + row.Inlines!.Add(Label(Translations.mcc_server_info_label_connecting_as)); + row.Inlines.Add(Value(resolvedMcVer, McColors.Green)); + row.Inlines.Add(new Run(" (") { Foreground = McColors.Gray }); + row.Inlines.Add(new Run(string.Format(Translations.mcc_server_info_label_protocol, info.ResolvedProtocol)) + { Foreground = McColors.Gray }); + row.Inlines.Add(new Run(")") { Foreground = McColors.Gray }); + panel.Children.Add(row); + } + + private static void AddPing(StackPanel panel, Protocol.ServerStatusInfo info) + { + if (info.PingMs < 0) + return; + + var pingColor = info.PingMs < 100 + ? McColors.Green + : info.PingMs < 300 + ? McColors.Yellow + : McColors.Red; + + var row = new TextBlock(); + row.Inlines!.Add(Label(Translations.mcc_server_info_label_ping)); + row.Inlines.Add(new Run(string.Format(Translations.mcc_server_info_label_ping_ms, info.PingMs)) + { Foreground = pingColor }); + panel.Children.Add(row); + } + + private static void AddPlayers(StackPanel panel, Protocol.ServerStatusInfo info) + { + var row = new TextBlock(); + row.Inlines!.Add(Label(Translations.mcc_server_info_label_players)); + row.Inlines.Add(Value($"{info.OnlinePlayers}", McColors.Green)); + row.Inlines.Add(new Run("/") { Foreground = McColors.Gray }); + row.Inlines.Add(Value($"{info.MaxPlayers}", McColors.Red)); + panel.Children.Add(row); + } + + private static void AddSamplePlayers(StackPanel panel, Protocol.ServerStatusInfo info) + { + if (info.SamplePlayers.Count == 0) + return; + + panel.Children.Add(new TextBlock + { + Text = Translations.mcc_server_info_label_online, + Foreground = McColors.Gray, + }); + + int shown = Math.Min(info.SamplePlayers.Count, MaxSamplePlayers); + for (int i = 0; i < shown; i++) + { + string name = info.SamplePlayers[i].Name; + if (name.Contains('\u00a7')) + panel.Children.Add(McColorParser.CreateColoredTextBlock($" {name}", TextWrapping.NoWrap)); + else + panel.Children.Add(new TextBlock + { + Text = $" {name}", + Foreground = McColors.Green, + }); + } + + if (info.SamplePlayers.Count > shown) + { + panel.Children.Add(new TextBlock + { + Text = $" {string.Format(Translations.mcc_server_info_sample_more, info.SamplePlayers.Count - shown)}", + Foreground = McColors.Gray, + }); + } + } + + private static Run Label(string text) => + new(text + " ") { Foreground = McColors.Gray }; + + private static Run Value(string text, IBrush color) => + new(text) { Foreground = color }; + + private static Grid BuildFaviconGrid(string base64Png, int displaySize) => + IconGridBuilder.BuildFromBase64(base64Png, displaySize); + + private static class McColors + { + public static readonly IBrush Gray = new SolidColorBrush(Color.FromRgb(170, 170, 170)); + public static readonly IBrush Aqua = new SolidColorBrush(Color.FromRgb(85, 255, 255)); + public static readonly IBrush Green = new SolidColorBrush(Color.FromRgb(85, 255, 85)); + public static readonly IBrush Red = new SolidColorBrush(Color.FromRgb(255, 85, 85)); + public static readonly IBrush Yellow = new SolidColorBrush(Color.FromRgb(255, 255, 85)); + } + } +} diff --git a/MinecraftClient/Tui/SlotViewModel.cs b/MinecraftClient/Tui/SlotViewModel.cs new file mode 100644 index 00000000..2a8f6bda --- /dev/null +++ b/MinecraftClient/Tui/SlotViewModel.cs @@ -0,0 +1,157 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Tui +{ + public class SlotViewModel : INotifyPropertyChanged + { + private bool _isSelected; + private bool _isHovered; + + public int SlotId { get; } + public string ItemDisplayText { get; private set; } + public string CountDisplay { get; private set; } + public string FullInfo { get; private set; } + public string ItemTypeName { get; private set; } + public bool IsEmpty { get; private set; } + public bool IsHotbar { get; } + public int HotbarIndex { get; } + public ItemType ItemType { get; private set; } + public int ItemCount { get; private set; } + public Item? RawItem { get; private set; } + public int NameMaxWidth { get; set; } = 9; + public int NameMaxLines { get; set; } = 1; + + public bool IsSelected + { + get => _isSelected; + set { _isSelected = value; OnPropertyChanged(); } + } + + public bool IsHovered + { + get => _isHovered; + set { _isHovered = value; OnPropertyChanged(); } + } + + public SlotViewModel(int slotId, bool isHotbar = false, int hotbarIndex = -1) + { + SlotId = slotId; + IsHotbar = isHotbar; + HotbarIndex = hotbarIndex; + ItemDisplayText = ""; + CountDisplay = ""; + FullInfo = ""; + ItemTypeName = ""; + IsEmpty = true; + ItemType = ItemType.Air; + ItemCount = 0; + } + + public void Update(Item? item) + { + RawItem = item; + if (item == null || item.IsEmpty) + { + ItemDisplayText = ""; + CountDisplay = ""; + FullInfo = ""; + ItemTypeName = ""; + IsEmpty = true; + ItemType = ItemType.Air; + ItemCount = 0; + } + else + { + ItemType = item.Type; + ItemCount = item.Count; + string typeName = item.GetTypeString(); + ItemTypeName = typeName; + ItemDisplayText = FormatMultiLine(typeName, NameMaxWidth, NameMaxLines); + CountDisplay = item.Count > 1 ? $"x{item.Count}" : ""; + FullInfo = item.ToFullString(); + IsEmpty = false; + } + + OnPropertyChanged(nameof(ItemDisplayText)); + OnPropertyChanged(nameof(CountDisplay)); + OnPropertyChanged(nameof(FullInfo)); + OnPropertyChanged(nameof(IsEmpty)); + OnPropertyChanged(nameof(ItemType)); + OnPropertyChanged(nameof(ItemTypeName)); + OnPropertyChanged(nameof(ItemCount)); + } + + /// + /// Format item name into multi-line display text that fits within + /// maxWidth columns and maxLines lines. Breaks at word boundaries. + /// + private static string FormatMultiLine(string name, int maxWidth, int maxLines) + { + if (string.IsNullOrEmpty(name)) + return ""; + + int colonIdx = name.LastIndexOf(':'); + if (colonIdx >= 0 && colonIdx < name.Length - 1) + name = name[(colonIdx + 1)..]; + + name = name.Replace("_", " ").Trim(); + name = InsertCamelCaseSpaces(name); + + if (maxLines <= 1 || name.Length <= maxWidth) + return name.Length <= maxWidth ? name : name[..maxWidth]; + + var lines = new System.Collections.Generic.List(); + string remaining = name; + + for (int line = 0; line < maxLines && remaining.Length > 0; line++) + { + if (remaining.Length <= maxWidth) + { + lines.Add(remaining); + break; + } + + int breakAt = -1; + for (int i = maxWidth; i >= 1; i--) + { + if (remaining[i] == ' ') + { + breakAt = i; + break; + } + } + + if (breakAt < 0) + breakAt = maxWidth; + + lines.Add(remaining[..breakAt].TrimEnd()); + remaining = remaining[breakAt..].TrimStart(); + } + + return string.Join("\n", lines); + } + + private static string InsertCamelCaseSpaces(string s) + { + if (s.Length < 2) return s; + var sb = new System.Text.StringBuilder(s.Length + 4); + sb.Append(s[0]); + for (int i = 1; i < s.Length; i++) + { + if (char.IsUpper(s[i]) && char.IsLower(s[i - 1])) + sb.Append(' '); + sb.Append(s[i]); + } + return sb.ToString(); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string? name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + } +} diff --git a/MinecraftClient/Tui/TabListOverlay.cs b/MinecraftClient/Tui/TabListOverlay.cs new file mode 100644 index 00000000..6e4c5c29 --- /dev/null +++ b/MinecraftClient/Tui/TabListOverlay.cs @@ -0,0 +1,92 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; + +namespace MinecraftClient.Tui +{ + internal sealed class TabListOverlay : Border + { + private readonly McClient _handler; + private readonly ScrollViewer _scrollViewer; + private readonly DispatcherTimer _refreshTimer; + + public TabListOverlay(McClient handler) + { + ArgumentNullException.ThrowIfNull(handler); + _handler = handler; + + BorderBrush = Brushes.White; + BorderThickness = new Thickness(1); + Background = Brushes.Black; + Padding = new Thickness(1); + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + Focusable = true; + + _scrollViewer = new ScrollViewer + { + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + Focusable = true, + }; + + Child = _scrollViewer; + + _refreshTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, static (_, _) => { }) + { + IsEnabled = false + }; + _refreshTimer.Tick += (_, _) => Refresh(); + + AttachedToVisualTree += (_, _) => + { + AddHandler(KeyDownEvent, OnTunnelKeyDown, RoutingStrategies.Tunnel); + Refresh(); + _refreshTimer.Start(); + Focus(); + Dispatcher.UIThread.Post(() => _scrollViewer.Focus(), DispatcherPriority.Input); + }; + + DetachedFromVisualTree += (_, _) => + { + RemoveHandler(KeyDownEvent, OnTunnelKeyDown); + _refreshTimer.Stop(); + }; + } + + private void OnTunnelKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key != Key.Escape) + return; + + TuiConsoleBackend.Instance?.DismissOverlay(); + e.Handled = true; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + TuiConsoleBackend.Instance?.DismissOverlay(); + e.Handled = true; + return; + } + + base.OnKeyDown(e); + } + + private void Refresh() + { + string text = TabListFormatter.Render(_handler.GetTabListSnapshot(), includeOverlayHint: true); + var block = McColorParser.CreateColoredTextBlock(text, TextWrapping.NoWrap); + block.Margin = new Thickness(0); + _scrollViewer.Content = block; + } + } +} diff --git a/MinecraftClient/Tui/TuiConsoleBackend.cs b/MinecraftClient/Tui/TuiConsoleBackend.cs new file mode 100644 index 00000000..9579cd23 --- /dev/null +++ b/MinecraftClient/Tui/TuiConsoleBackend.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia; +using Avalonia.Threading; +using Consolonia; + +namespace MinecraftClient.Tui +{ + /// + /// Console backend that uses Avalonia/Consolonia for a full-screen TUI. + /// Avalonia Dispatcher runs on the main thread; MCC logic runs on background threads. + /// + public class TuiConsoleBackend : IConsoleBackend + { + public event EventHandler? MessageReceived; + public event EventHandler? OnInputChange; + + private MainTuiView? _view; + + public bool DisplayUserInput { get; set; } = true; + + internal static TuiConsoleBackend? Instance { get; private set; } + + private Program.StartupState? _pendingStartupState; + private readonly ManualResetEventSlim _viewReady = new(false); + + /// + /// Initializes the Avalonia app and starts the main UI loop. + /// This blocks the calling thread until the TUI exits. + /// Before blocking, it starts MCC's remaining initialization on a background thread. + /// + internal void RunTuiMainLoop(string[] args, Program.StartupState startupState) + { + Instance = this; + _pendingStartupState = startupState; + + AppDomain.CurrentDomain.ProcessExit += (_, _) => RestoreTerminalState(); + + System.Console.CancelKeyPress += (_, e) => + { + e.Cancel = true; + var view = _view; + if (view != null) + Dispatcher.UIThread.Post(() => view.HandleCtrlC()); + }; + + new Thread(() => + { + _viewReady.Wait(); + ContinueMccStartup(args); + }) + { Name = "MCC-Main", IsBackground = true }.Start(); + + AppBuilder builder = AppBuilder.Configure() + .UseConsolonia() + .UseAutoDetectedConsole() + .LogToException(); + + try + { + builder.StartWithConsoleLifetime(Array.Empty()); + } + finally + { + RestoreTerminalState(); + } + } + + private static volatile bool _terminalRestored; + + private static void RestoreTerminalState() + { + if (_terminalRestored) return; + _terminalRestored = true; + + try + { + System.Console.Write("\x1b[?1000l"); // disable X11 mouse + System.Console.Write("\x1b[?1001l"); // disable highlight mouse + System.Console.Write("\x1b[?1002l"); // disable button-event mouse + System.Console.Write("\x1b[?1003l"); // disable any-event mouse + System.Console.Write("\x1b[?1004l"); // disable focus events + System.Console.Write("\x1b[?1005l"); // disable UTF-8 mouse encoding + System.Console.Write("\x1b[?1006l"); // disable SGR mouse encoding + System.Console.Write("\x1b[?1015l"); // disable urxvt mouse encoding + System.Console.Write("\x1b[?1049l"); // leave alternate screen + System.Console.Write("\x1b[?25h"); // show cursor + System.Console.Write("\x1b[?7h"); // re-enable line wrap + System.Console.Write("\x1b[0m"); // reset attributes + System.Console.Write("\x1b[2J"); // clear entire screen + System.Console.Write("\x1b[H"); // cursor to home + System.Console.Out.Flush(); + } + catch { } + + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + try + { + using var proc = Process.Start(new ProcessStartInfo + { + FileName = "stty", + Arguments = "sane", + UseShellExecute = false, + }); + proc?.WaitForExit(500); + } + catch { } + } + } + + private static void ContinueMccStartup(string[] args) + { + try + { + var instance = Instance; + if (instance?._pendingStartupState is { } state) + { + instance._pendingStartupState = null; + if (!Program.ProcessStartupState(state)) + return; + } + + Program.RunStartupSequence(args); + } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"§c[MCC] Fatal: {ex.Message}"); + } + } + + internal void SetView(MainTuiView view) + { + _view = view; + _viewReady.Set(); + } + + internal MainTuiView? GetView() => _view; + + public void Init() + { + } + + public void WriteLine(string text) + { + var view = _view; + if (view == null) + { + System.Console.WriteLine(text); + return; + } + + if (Dispatcher.UIThread.CheckAccess()) + view.AppendLogLine(text); + else + Dispatcher.UIThread.Post(() => view.AppendLogLine(text)); + } + + public void WriteLineFormatted(string text) + { + var view = _view; + if (view == null) + { + System.Console.WriteLine(Scripting.ChatBot.GetVerbatim(text)); + return; + } + + if (Dispatcher.UIThread.CheckAccess()) + view.AppendFormattedLogLine(text); + else + Dispatcher.UIThread.Post(() => view.AppendFormattedLogLine(text)); + } + + public void BeginReadThread() + { + } + + public void StopReadThread() + { + DismissOverlay(); + } + + /// + /// Close any open overlay (e.g. inventory) so the user can interact + /// with the main console again. Safe to call from any thread. + /// + internal void DismissOverlay() + { + var view = _view; + if (view == null) return; + + if (Dispatcher.UIThread.CheckAccess()) + { + view.HideOverlay(); + } + else + { + Dispatcher.UIThread.Post(() => view.HideOverlay()); + } + } + + public string RequestImmediateInput() + { + if (_shutdownRequested) + { + Thread.Sleep(Timeout.Infinite); + return string.Empty; + } + + var mre = new ManualResetEventSlim(false); + string? result = null; + + void Handler(object? sender, string e) + { + result = e; + mre.Set(); + } + + MessageReceived += Handler; + mre.Wait(); + MessageReceived -= Handler; + + return result ?? string.Empty; + } + + public string? ReadPassword() + { + return RequestImmediateInput(); + } + + public void ClearInputBuffer() + { + if (_view == null) return; + if (Dispatcher.UIThread.CheckAccess()) + _view.ClearInput(); + else + Dispatcher.UIThread.Post(() => _view?.ClearInput()); + } + + public void ClearScreen() + { + if (_view == null) return; + if (Dispatcher.UIThread.CheckAccess()) + _view.ClearLog(); + else + Dispatcher.UIThread.Post(() => _view?.ClearLog()); + } + + public void SetInputVisible(bool visible) + { + } + + public void SetBackreadBufferLimit(int limit) + { + } + + public void Shutdown() + { + _shutdownRequested = true; + RestoreTerminalState(); + + var lifetime = Application.Current?.ApplicationLifetime + as Avalonia.Controls.ApplicationLifetimes.IControlledApplicationLifetime; + + if (lifetime != null) + { + if (Dispatcher.UIThread.CheckAccess()) + lifetime.Shutdown(); + else + Dispatcher.UIThread.Post(() => lifetime.Shutdown()); + } + + new Thread(() => + { + Thread.Sleep(1000); + Environment.Exit(0); + }) + { Name = "TUI-Exit-Guard", IsBackground = true }.Start(); + } + + private volatile bool _shutdownRequested; + + /// + /// Called from the TUI view when user presses Enter in the command input. + /// Always fires MessageReceived so that both the normal read-thread path + /// and RequestImmediateInput (used by offline prompt) receive the input. + /// + internal void OnCommandSubmitted(string command) + { + MessageReceived?.Invoke(this, command); + } + + /// + /// Called from the TUI view when user types in the command input. + /// + internal void OnInputChanged(string text, int cursorPos) + { + OnInputChange?.Invoke(this, new ConsoleInputBuffer(text, cursorPos)); + } + + internal void UpdateSuggestions(CommandSuggestion[] suggestions, (int Start, int End) range) + { + var view = _view; + if (view == null) return; + + if (Dispatcher.UIThread.CheckAccess()) + view.UpdateSuggestions(suggestions, range); + else + Dispatcher.UIThread.Post(() => view.UpdateSuggestions(suggestions, range)); + } + + internal void ClearSuggestions() + { + var view = _view; + if (view == null) return; + + if (Dispatcher.UIThread.CheckAccess()) + view.ClearSuggestions(); + else + Dispatcher.UIThread.Post(() => view.ClearSuggestions()); + } + } +} diff --git a/MinecraftClient/Tui/TuiTooltipService.cs b/MinecraftClient/Tui/TuiTooltipService.cs new file mode 100644 index 00000000..0ae51607 --- /dev/null +++ b/MinecraftClient/Tui/TuiTooltipService.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace MinecraftClient.Tui +{ + public sealed class TuiTooltipLine + { + public string Text { get; init; } = ""; + public IBrush Foreground { get; init; } = Brushes.White; + } + + /// + /// Global tooltip that floats above all TUI content. + /// Owned by MainTuiView, used by minimap / chat / other components. + /// + public sealed class TuiTooltipService + { + private readonly Panel _rootPanel; + private readonly Canvas _canvas; + private readonly Border _border; + private readonly StackPanel _content; + + internal TuiTooltipService(Panel rootPanel) + { + _content = new StackPanel { Orientation = Avalonia.Layout.Orientation.Vertical }; + _border = new Border + { + Background = new SolidColorBrush(Color.FromArgb(230, 20, 20, 20)), + BorderBrush = new SolidColorBrush(Color.FromRgb(120, 120, 120)), + BorderThickness = new Thickness(1), + Padding = new Thickness(1), + Child = _content, + IsVisible = false, + }; + + _canvas = new Canvas + { + IsHitTestVisible = false, + Children = { _border }, + }; + + _rootPanel = rootPanel; + rootPanel.Children.Add(_canvas); + } + + /// Global X of the mouse cursor. + /// Global Y of the mouse cursor. + /// + /// If true, try placing tooltip to the right of mouseX; + /// if false, try placing to the left. + /// The service auto-flips when the tooltip would overflow the screen. + /// + public void Show(double mouseX, double mouseY, IReadOnlyList lines, + bool preferRight = true) + { + _content.Children.Clear(); + + if (lines.Count == 0) + { + _border.IsVisible = false; + return; + } + + int maxChars = 0; + foreach (var line in lines) + { + _content.Children.Add(new TextBlock + { + Text = line.Text, + Foreground = line.Foreground, + TextWrapping = TextWrapping.Wrap, + Padding = new Thickness(0), + Margin = new Thickness(0), + FontSize = 1, + }); + if (line.Text.Length > maxChars) + maxChars = line.Text.Length; + } + + double tipW = maxChars + 4; + double screenW = _rootPanel.Bounds.Width; + + const double gap = 1; + double gx; + if (preferRight) + { + gx = mouseX + gap; + if (gx + tipW > screenW) + gx = mouseX - tipW - gap; + } + else + { + gx = mouseX - tipW - gap; + if (gx < 0) + gx = mouseX + gap; + } + + Canvas.SetLeft(_border, Math.Max(0, gx)); + Canvas.SetTop(_border, Math.Max(0, mouseY)); + _border.IsVisible = true; + } + + public void Hide() + { + _border.IsVisible = false; + _content.Children.Clear(); + } + + public bool IsVisible => _border.IsVisible; + } +} diff --git a/MinecraftClient/UpgradeHelper.cs b/MinecraftClient/UpgradeHelper.cs index 8c53da16..cd08a1a7 100644 --- a/MinecraftClient/UpgradeHelper.cs +++ b/MinecraftClient/UpgradeHelper.cs @@ -211,7 +211,7 @@ namespace MinecraftClient if (!cancellationToken.IsCancellationRequested) { HttpResponseMessage res = httpWebRequest.Result; - if (res.Headers.Location != null) + if (res.Headers.Location is not null) { Match match = Regex.Match(res.Headers.Location.ToString(), GithubReleaseUrl + @"/tag/(\d{4})(\d{2})(\d{2})-(\d+)"); if (match.Success && match.Groups.Count == 5) @@ -284,7 +284,7 @@ namespace MinecraftClient private static bool CompareVersionInfo(string? current, string? latest) { - if (current == null || latest == null) + if (current is null || latest is null) return false; Regex reg = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s(\d{4})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{2}).*"); Regex reg2 = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s\w+\s(\d{2})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{4}).*"); @@ -297,13 +297,13 @@ namespace MinecraftClient try { curTime = new(int.Parse(curMatch.Groups[2].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[4].Value)); } catch { curTime = null; } } - if (curTime == null) + if (curTime is null) { curMatch = reg2.Match(current); try { curTime = new(int.Parse(curMatch.Groups[4].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[2].Value)); } catch { curTime = null; } } - if (curTime == null) + if (curTime is null) return false; Match latestMatch = reg.Match(latest); @@ -312,13 +312,13 @@ namespace MinecraftClient try { latestTime = new(int.Parse(latestMatch.Groups[2].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[4].Value)); } catch { latestTime = null; } } - if (latestTime == null) + if (latestTime is null) { latestMatch = reg2.Match(latest); try { latestTime = new(int.Parse(latestMatch.Groups[4].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[2].Value)); } catch { latestTime = null; } } - if (latestTime == null) + if (latestTime is null) return false; int curBuildId, latestBuildId; diff --git a/MinecraftClient/WinAPI/ConsoleIcon.cs b/MinecraftClient/WinAPI/ConsoleIcon.cs index e66eca89..cc243dda 100644 --- a/MinecraftClient/WinAPI/ConsoleIcon.cs +++ b/MinecraftClient/WinAPI/ConsoleIcon.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Drawing; using System.IO; using System.Net.Http; @@ -13,6 +13,7 @@ namespace MinecraftClient.WinAPI /// Allow to set the player skin as console icon, on Windows only. /// See StackOverflow no. 2986853 /// + [SupportedOSPlatform("windows")] public static class ConsoleIcon { [DllImport("kernel32.dll", SetLastError = true)] @@ -32,18 +33,14 @@ namespace MinecraftClient.WinAPI private static void SetWindowIcon(System.Drawing.Icon icon) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - IntPtr mwHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; - SendMessage(mwHandle, (int)WinMessages.SETICON, 0, icon.Handle); - SendMessage(mwHandle, (int)WinMessages.SETICON, 1, icon.Handle); - } + IntPtr mwHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; + SendMessage(mwHandle, (int)WinMessages.SETICON, 0, icon.Handle); + SendMessage(mwHandle, (int)WinMessages.SETICON, 1, icon.Handle); } /// /// Asynchronously download the player's skin and set the head as console icon /// - [SupportedOSPlatform("windows")] public static void SetPlayerIconAsync(string playerName) { Thread t = new(new ThreadStart(delegate @@ -99,16 +96,13 @@ namespace MinecraftClient.WinAPI /// public static void RevertToMCCIcon() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) //Windows Only + try { - try - { - Icon defaultIcon = Icon.ExtractAssociatedIcon(Environment.ProcessPath!)!; - SetWindowIcon(Icon.FromHandle(defaultIcon.Handle)); // Windows 10+ (New console) - SetConsoleIcon(defaultIcon.Handle); // Windows 8 and lower (Older console) - } - catch { } + Icon defaultIcon = Icon.ExtractAssociatedIcon(Environment.ProcessPath!)!; + SetWindowIcon(Icon.FromHandle(defaultIcon.Handle)); // Windows 10+ (New console) + SetConsoleIcon(defaultIcon.Handle); // Windows 8 and lower (Older console) } + catch { } } } } diff --git a/MinecraftClient/config/ChatBots/AutoTree.cs b/MinecraftClient/config/ChatBots/AutoTree.cs index 5488d8ad..21d731b7 100644 --- a/MinecraftClient/config/ChatBots/AutoTree.cs +++ b/MinecraftClient/config/ChatBots/AutoTree.cs @@ -75,7 +75,7 @@ public class AutoTree : ChatBot } } - public override void Initialize(CommandDispatcher dispatcher) + public override void Initialize() { if (!GetTerrainEnabled()) { @@ -89,7 +89,7 @@ public class AutoTree : ChatBot } else { - dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Then(l => l.Literal("set") @@ -99,7 +99,7 @@ public class AutoTree : ChatBot ) ); - dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("toggle") .Executes(r => { return r.Source.SetAndReturn(CmdResult.Status.Done, Toggle() ? "Now is running" : "Now is stopping"); })) .Then(l => l.Literal("set") @@ -109,17 +109,17 @@ public class AutoTree : ChatBot .Then(l => l.Argument("TreeType", Arguments.String()) .Executes(r => OnCommandType(r.Source, Arguments.GetString(r, "TreeType"))))) .Then(l => l.Literal("_help") - .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); LogToConsole("Loaded."); } } - public override void OnUnload(CommandDispatcher dispatcher) + public override void OnUnload() { - dispatcher.Unregister(CommandName); - dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) diff --git a/MinecraftClient/config/ChatBots/DiscordWebhook.cs b/MinecraftClient/config/ChatBots/DiscordWebhook.cs index c3d7f9c2..ce0e6d22 100644 --- a/MinecraftClient/config/ChatBots/DiscordWebhook.cs +++ b/MinecraftClient/config/ChatBots/DiscordWebhook.cs @@ -143,7 +143,7 @@ class SkinAPI var request = new ProxiedWebRequest("https://api.mojang.com/users/profiles/minecraft/" + name); request.Accept = "application/json"; var response = request.Get(); - string uuid = Json.ParseJson(response.Body).Properties["id"].StringValue; + string uuid = Json.ParseJson(response.Body)!["id"]!.GetStringValue(); settings.GetNamesToUuidMojangCache().Add(name, uuid); return uuid; } @@ -306,7 +306,7 @@ class DiscordWebhook : ChatBot LogToConsole("Made by Daenges.\nSpecial thanks to Crafatar for providing the beautiful avatars!"); LogToConsole("Please set a Webhook with '/dw changeurl [URL]'. For further information type '/discordwebhook help'."); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Argument("Commands", Arguments.GreedyString()) .Executes(r => { CommandHandler(Arguments.GetString(r, "Commands").Split(' ', StringSplitOptions.TrimEntries)); @@ -317,7 +317,7 @@ class DiscordWebhook : ChatBot public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); + McClient.dispatcher.Unregister(CommandName); } public override void Update() diff --git a/MinecraftClient/config/ChatBots/MineCube.cs b/MinecraftClient/config/ChatBots/MineCube.cs index 71d760a6..1f64f0da 100644 --- a/MinecraftClient/config/ChatBots/MineCube.cs +++ b/MinecraftClient/config/ChatBots/MineCube.cs @@ -46,7 +46,7 @@ class MineCube : ChatBot LogToConsole("Mining bot created by Daenges."); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Argument("Commands", Arguments.GreedyString()) .Executes(r => { EvaluateMineCommand(CommandName + ' ' + Arguments.GetString(r, "Commands"), Arguments.GetString(r, "Commands").Split(' ', StringSplitOptions.TrimEntries)); @@ -57,7 +57,7 @@ class MineCube : ChatBot public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); + McClient.dispatcher.Unregister(CommandName); } /// diff --git a/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs b/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs index 68979e4b..9b670b48 100644 --- a/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs +++ b/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs @@ -151,7 +151,7 @@ class SugarCaneFarmer : SugarCaneFarmerBase { LogToConsole("Sugar Cane farming bot created by Daenges."); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Argument("Commands", Arguments.GreedyString()) .Executes(r => { CommandHandler(Arguments.GetString(r, "Commands").Split(' ', StringSplitOptions.TrimEntries)); @@ -162,7 +162,7 @@ class SugarCaneFarmer : SugarCaneFarmerBase public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); + McClient.dispatcher.Unregister(CommandName); } /// diff --git a/MinecraftClient/config/ChatBots/VkMessager.cs b/MinecraftClient/config/ChatBots/VkMessager.cs index 448e6840..f57b2efa 100644 --- a/MinecraftClient/config/ChatBots/VkMessager.cs +++ b/MinecraftClient/config/ChatBots/VkMessager.cs @@ -426,9 +426,9 @@ internal class VkLongPoolClient var jsonResult = CallVkMethod("groups.getLongPollServer", "group_id=" + BotCommunityId); var data = Json.ParseJson(jsonResult); - Key = data.Properties["response"].Properties["key"].StringValue; - Server = data.Properties["response"].Properties["server"].StringValue; - LastTs = Convert.ToInt32(data.Properties["response"].Properties["ts"].StringValue); + Key = data!["response"]!["key"]!.GetStringValue(); + Server = data["response"]!["server"]!.GetStringValue(); + LastTs = Convert.ToInt32(data["response"]!["ts"].GetStringValue()); } private void StartLongPoolAsync() @@ -457,25 +457,25 @@ internal class VkLongPoolClient { var j = JsonConvert.DeserializeObject(jsonData) as JObject; var data = Json.ParseJson(jsonData); - if (data.Properties.ContainsKey("failed")) + if (data?.AsObject().ContainsKey("failed") == true) { Init(); } - LastTs = Convert.ToInt32(data.Properties["ts"].StringValue); - var updates = data.Properties["updates"].DataArray; + LastTs = Convert.ToInt32(data!["ts"].GetStringValue()); + var updates = data["updates"]!.AsArray(); List> messages = new List>(); foreach (var str in updates) { - if (str.Properties["type"].StringValue != "message_new") continue; + if (str!["type"]!.GetStringValue() != "message_new") continue; - var msgData = str.Properties["object"].Properties; + var msgData = str["object"]!.AsObject(); - var id = msgData["from_id"].StringValue; - var userId = msgData["from_id"].StringValue; - var peer_id = msgData["peer_id"].StringValue; + var id = msgData["from_id"]!.GetStringValue(); + var userId = msgData["from_id"]!.GetStringValue(); + var peer_id = msgData["peer_id"]!.GetStringValue(); string event_id = ""; - var msgText = msgData["text"].StringValue; - var conversation_message_id = msgData["conversation_message_id"].StringValue; + var msgText = msgData["text"]!.GetStringValue(); + var conversation_message_id = msgData["conversation_message_id"]!.GetStringValue(); messages.Add(new Tuple(userId, peer_id, msgText, conversation_message_id, id, event_id)); } diff --git a/MinecraftClient/config/ChatBots/WebSocketBot.cs b/MinecraftClient/config/ChatBots/WebSocketBot.cs new file mode 100644 index 00000000..4983aeb8 --- /dev/null +++ b/MinecraftClient/config/ChatBots/WebSocketBot.cs @@ -0,0 +1,1188 @@ +//MCCScript 1.0 +//using System.Collections.Concurrent; +//using System.Net.Sockets; +//using System.Net.WebSockets; +//using System.Text.Json; +//using System.Text.Json.Serialization; +//using System.Threading.Tasks; +//using MinecraftClient.CommandHandler; + +// IMPORTANT: Change the password below before use! +MCC.LoadBot(new WebSocketBot("127.0.0.1", 8043, "CHANGE_THIS_PASSWORD")); + +//MCCScript Extensions + +public class WebSocketSession +{ + public string SessionId { get; set; } + public WebSocket WebSocket { get; } + public bool IsAuthenticated { get; set; } + + public WebSocketSession(string sessionId, WebSocket webSocket) + { + SessionId = sessionId; + WebSocket = webSocket; + IsAuthenticated = false; + } +} + +public class WebSocketServer +{ + private HttpListener? _listener; + private CancellationTokenSource? _cts; + private readonly ConcurrentDictionary _sessions = new(); + + public event Action? NewSession; + public event Action? SessionDropped; + public event Action? MessageReceived; + + public IReadOnlyDictionary Sessions => _sessions; + + public async Task Start(string ip, int port) + { + _cts = new CancellationTokenSource(); + _listener = new HttpListener(); + _listener.Prefixes.Add($"http://{ip}:{port}/"); + _listener.Start(); + + while (!_cts.Token.IsCancellationRequested) + { + try + { + var context = await _listener.GetContextAsync().ConfigureAwait(false); + + if (context.Request.IsWebSocketRequest) + _ = Task.Run(() => ProcessWebSocketSession(context, _cts.Token)); + else + { + context.Response.StatusCode = 400; + context.Response.Close(); + } + } + catch (ObjectDisposedException) { break; } + catch (HttpListenerException) { break; } + catch { /* ignore transient errors */ } + } + } + + private async Task ProcessWebSocketSession(HttpListenerContext context, CancellationToken ct) + { + WebSocketContext wsContext; + try + { + wsContext = await context.AcceptWebSocketAsync(null).ConfigureAwait(false); + } + catch { return; } + + var ws = wsContext.WebSocket; + var sessionId = Guid.NewGuid().ToString("D"); + var session = new WebSocketSession(sessionId, ws); + + _sessions.TryAdd(sessionId, session); + NewSession?.Invoke(sessionId, session); + + var buffer = new byte[4096]; + var messageBuffer = new List(); + + try + { + while (ws.State == WebSocketState.Open && !ct.IsCancellationRequested) + { + var result = await ws.ReceiveAsync(new ArraySegment(buffer), ct).ConfigureAwait(false); + + if (result.MessageType == WebSocketMessageType.Close) + break; + + messageBuffer.AddRange(new ArraySegment(buffer, 0, result.Count)); + + if (result.EndOfMessage) + { + var message = Encoding.UTF8.GetString(messageBuffer.ToArray()); + messageBuffer.Clear(); + MessageReceived?.Invoke(session.SessionId, message); + } + } + } + catch { /* connection dropped */ } + finally + { + _sessions.TryRemove(session.SessionId, out _); + SessionDropped?.Invoke(session.SessionId); + + if (ws.State == WebSocketState.Open || ws.State == WebSocketState.CloseReceived) + { + try + { + await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Session ended", CancellationToken.None) + .ConfigureAwait(false); + } + catch { /* best effort */ } + } + + ws.Dispose(); + } + } + + public bool RenameSession(string oldId, string newId) + { + if (!_sessions.TryRemove(oldId, out var session)) + return false; + + if (_sessions.ContainsKey(newId)) + { + _sessions.TryAdd(oldId, session); + return false; + } + + session.SessionId = newId; + _sessions.TryAdd(newId, session); + return true; + } + + public async Task SendToSession(string sessionId, string message) + { + if (!_sessions.TryGetValue(sessionId, out var session)) + return; + + if (session.WebSocket.State != WebSocketState.Open) + return; + + var bytes = Encoding.UTF8.GetBytes(message); + + try + { + await session.WebSocket.SendAsync( + new ArraySegment(bytes), + WebSocketMessageType.Text, + true, + CancellationToken.None).ConfigureAwait(false); + } + catch { /* send failed, session will be cleaned up */ } + } + + public void Stop() + { + _cts?.Cancel(); + + foreach (var kvp in _sessions) + { + try + { + var ws = kvp.Value.WebSocket; + if (ws.State == WebSocketState.Open) + ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server stopping", CancellationToken.None) + .GetAwaiter().GetResult(); + ws.Dispose(); + } + catch { /* best effort */ } + } + + _sessions.Clear(); + + try { _listener?.Stop(); } catch { } + try { _listener?.Close(); } catch { } + } +} + +public class WebSocketBot : ChatBot +{ + private readonly string _ip; + private readonly int _port; + private readonly string _password; + private readonly bool _debugMode; + + private WebSocketServer? _server; + private JsonSerializerOptions _jsonOptions = null!; + private readonly List _waitingEvents = new(); + private bool _gameJoined; + + private static readonly Regex Ipv4Regex = new( + @"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$", + RegexOptions.Compiled); + + public WebSocketBot(string ip, int port, string password, bool debugMode = false) + { + if (!Ipv4Regex.IsMatch(ip) && ip != "+" && ip != "*") + throw new ArgumentException($"Invalid IP address: {ip}"); + + if (port is < 1 or > 65535) + throw new ArgumentException($"Invalid port: {port}. Must be between 1 and 65535."); + + _ip = ip; + _port = port; + _password = password; + _debugMode = debugMode; + } + + public override void Initialize() + { + _jsonOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true, + Converters = { new JsonStringEnumConverter() } + }; + + _server = new WebSocketServer(); + _server.NewSession += OnNewSession; + _server.SessionDropped += OnSessionDropped; + _server.MessageReceived += OnMessageReceived; + + _ = Task.Run(async () => + { + try + { + await _server.Start(_ip, _port); + } + catch (Exception ex) + { + LogToConsole($"[WebSocketBot] Server failed to start: {ex.Message}"); + } + }); + + LogToConsole($"[WebSocketBot] Starting on {_ip}:{_port}"); + } + + public override void AfterGameJoined() + { + _gameJoined = true; + _waitingEvents.Add("OnGameJoined"); + } + + public override void Update() + { + if (_waitingEvents.Count > 0) + { + var events = _waitingEvents.ToList(); + _waitingEvents.Clear(); + + foreach (var evt in events) + BroadcastEvent(evt, "N/A"); + } + } + + public override void OnUnload() + { + BroadcastEvent("OnWsConnectionClose", "N/A"); + _server?.Stop(); + } + + // --- Event Overrides --- + + public override void GetText(string text) + { + text = GetVerbatim(text); + string message = "", username = ""; + + if (IsPrivateMessage(text, ref message, ref username)) + BroadcastEvent("OnChatPrivate", SerializeData(new { sender = username, message, rawText = text })); + else if (IsChatMessage(text, ref message, ref username)) + BroadcastEvent("OnChatPublic", SerializeData(new { sender = username, message, rawText = text })); + + string tpSender = ""; + if (IsTeleportRequest(text, ref tpSender)) + BroadcastEvent("OnTeleportRequest", SerializeData(new { sender = tpSender, rawText = text })); + } + + public override void GetText(string text, string? json) + { + BroadcastEvent("OnChatRaw", SerializeData(new { text, json })); + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + BroadcastEvent("OnDisconnect", SerializeData(new { reason = reason.ToString(), message })); + return false; + } + + public override void OnBlockBreakAnimation(Entity entity, Location location, byte stage) + { + BroadcastEvent("OnBlockBreakAnimation", SerializeData(new { entity, location, stage })); + } + + public override void OnEntityAnimation(Entity entity, byte animation) + { + BroadcastEvent("OnEntityAnimation", SerializeData(new { entity, animation })); + } + + public override void OnPlayerProperty(Dictionary prop) + { + BroadcastEvent("OnPlayerProperty", SerializeData(prop)); + } + + public override void OnServerTpsUpdate(double tps) + { + BroadcastEvent("OnServerTpsUpdate", SerializeData(new { tps })); + } + + public override void OnTimeUpdate(long worldAge, long timeOfDay) + { + BroadcastEvent("OnTimeUpdate", SerializeData(new { worldAge, timeOfDay })); + } + + public override void OnEntityMove(Entity entity) + { + BroadcastEvent("OnEntityMove", SerializeData(entity)); + } + + public override void OnInternalCommand(string commandName, string commandParams, CmdResult result) + { + BroadcastEvent("OnInternalCommand", SerializeData(new + { + commandName, + commandParams, + result = new { status = result.status.ToString(), result = result.result } + })); + } + + public override void OnEntitySpawn(Entity entity) + { + BroadcastEvent("OnEntitySpawn", SerializeData(entity)); + } + + public override void OnEntityDespawn(Entity entity) + { + BroadcastEvent("OnEntityDespawn", SerializeData(entity)); + } + + public override void OnHeldItemChange(byte slot) + { + BroadcastEvent("OnHeldItemChange", SerializeData(new { slot })); + } + + public override void OnHealthUpdate(float health, int food) + { + BroadcastEvent("OnHealthUpdate", SerializeData(new { health, food })); + } + + public override void OnExplosion(Location explode, float strength, int recordcount) + { + BroadcastEvent("OnExplosion", SerializeData(new { location = explode, strength, recordcount })); + } + + public override void OnSetExperience(float experienceBar, int level, int totalExperience) + { + BroadcastEvent("OnSetExperience", SerializeData(new { experienceBar, level, totalExperience })); + } + + public override void OnGamemodeUpdate(string playerName, Guid uuid, int gamemode) + { + BroadcastEvent("OnGamemodeUpdate", SerializeData(new { playerName, uuid, gamemode })); + } + + public override void OnLatencyUpdate(string playerName, Guid uuid, int latency) + { + BroadcastEvent("OnLatencyUpdate", SerializeData(new { playerName, uuid, latency })); + } + + public override void OnMapData(int mapId, byte scale, bool trackingPosition, bool locked, + List icons, byte columnsUpdated, byte rowsUpdated, byte mapColumnX, + byte mapRowZ, byte[]? colors) + { + BroadcastEvent("OnMapData", SerializeData(new + { + mapId, scale, trackingPosition, locked, icons, + columnsUpdated, rowsUpdated, mapColumnX, mapRowZ, + colors = colors != null ? Convert.ToBase64String(colors) : null + })); + } + + public override void OnTradeList(int windowId, List trades, VillagerInfo villagerInfo) + { + BroadcastEvent("OnTradeList", SerializeData(new { windowId, trades, villagerInfo })); + } + + public override void OnTitle(int action, string titleText, string subtitleText, + string actionBarText, int fadeIn, int stay, int fadeOut, string json) + { + BroadcastEvent("OnTitle", SerializeData(new + { + action, titleText, subtitleText, actionBarText, + fadeIn, stay, fadeOut, json + })); + } + + public override void OnEntityEquipment(Entity entity, int slot, Item? item) + { + BroadcastEvent("OnEntityEquipment", SerializeData(new { entity, slot, item })); + } + + public override void OnEntityEffect(Entity entity, Effects effect, int amplifier, int duration, byte flags) + { + BroadcastEvent("OnEntityEffect", SerializeData(new + { + entity, effect = effect.ToString(), amplifier, duration, flags + })); + } + + public override void OnScoreboardObjective(string objectiveName, byte mode, + string objectiveValue, int type, string json, int numberFormat) + { + BroadcastEvent("OnScoreboardObjective", SerializeData(new + { + objectiveName, mode, objectiveValue, type, json, numberFormat + })); + } + + public override void OnUpdateScore(string entityName, int action, string objectiveName, + string objectiveDisplayName, int value, int numberFormat) + { + BroadcastEvent("OnUpdateScore", SerializeData(new + { + entityName, action, objectiveName, objectiveDisplayName, value, numberFormat + })); + } + + public override void OnInventoryUpdate(int inventoryId) + { + BroadcastEvent("OnInventoryUpdate", SerializeData(new { inventoryId })); + } + + public override void OnInventoryOpen(int inventoryId) + { + BroadcastEvent("OnInventoryOpen", SerializeData(new { inventoryId })); + } + + public override void OnInventoryClose(int inventoryId) + { + BroadcastEvent("OnInventoryClose", SerializeData(new { inventoryId })); + } + + public override void OnPlayerJoin(Guid uuid, string name) + { + BroadcastEvent("OnPlayerJoin", SerializeData(new { uuid, name })); + } + + public override void OnPlayerLeave(Guid uuid, string? name) + { + BroadcastEvent("OnPlayerLeave", SerializeData(new { uuid, name })); + } + + public override void OnDeath() + { + BroadcastEvent("OnDeath", "N/A"); + } + + public override void OnRespawn() + { + BroadcastEvent("OnRespawn", "N/A"); + } + + public override void OnEntityHealth(Entity entity, float health) + { + BroadcastEvent("OnEntityHealth", SerializeData(new { entity, health })); + } + + public override void OnEntityMetadata(Entity entity, Dictionary metadata) + { + BroadcastEvent("OnEntityMetadata", SerializeData(new { entity, metadata })); + } + + public override void OnPlayerStatus(byte statusId) + { + BroadcastEvent("OnPlayerStatus", SerializeData(new { statusId })); + } + + public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) + { + BroadcastEvent("OnNetworkPacket", SerializeData(new + { + packetID, + data = Convert.ToBase64String(packetData.ToArray()), + isLogin, + isInbound + })); + } + + // --- Serialization helpers --- + + private string SerializeData(object data) + { + return JsonSerializer.Serialize(data, _jsonOptions); + } + + private void BroadcastEvent(string eventName, string data) + { + if (_server == null) return; + + var envelope = new Dictionary { ["event"] = eventName, ["data"] = data }; + var json = JsonSerializer.Serialize(envelope); + + foreach (var kvp in _server.Sessions) + { + if (!kvp.Value.IsAuthenticated) continue; + _ = _server.SendToSession(kvp.Key, json); + } + } + + private void SendSessionEvent(string sessionId, string eventName, string data) + { + if (_server == null) return; + + var envelope = new Dictionary { ["event"] = eventName, ["data"] = data }; + var json = JsonSerializer.Serialize(envelope); + + _ = _server.SendToSession(sessionId, json); + } + + // --- Session event handlers --- + + private void OnNewSession(string sessionId, WebSocketSession session) + { + if (_debugMode) + LogToConsole($"[WebSocketBot] New session: {sessionId}"); + } + + private void OnSessionDropped(string sessionId) + { + if (_debugMode) + LogToConsole($"[WebSocketBot] Session dropped: {sessionId}"); + } + + private void OnMessageReceived(string sessionId, string message) + { + if (_debugMode) + LogToConsole($"[WebSocketBot] [{sessionId}] Received: {message}"); + + if (_server == null || !_server.Sessions.TryGetValue(sessionId, out var session)) + return; + + try + { + using var doc = JsonDocument.Parse(message); + var root = doc.RootElement; + + if (root.TryGetProperty("command", out var commandElement)) + { + var command = commandElement.GetString() ?? ""; + var requestId = root.TryGetProperty("requestId", out var rid) ? rid.GetString() ?? "" : ""; + + var parameters = new List(); + if (root.TryGetProperty("parameters", out var paramsElement) && + paramsElement.ValueKind == JsonValueKind.Array) + { + foreach (var p in paramsElement.EnumerateArray()) + { + parameters.Add(p.ValueKind switch + { + JsonValueKind.String => p.GetString(), + JsonValueKind.Number => p.TryGetInt64(out var l) ? (object)l : p.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => p.GetRawText() + }); + } + } + + HandleCommand(sessionId, session, command, requestId, parameters); + return; + } + } + catch + { + // Not valid JSON, treat as plain text + } + + HandlePlainText(sessionId, session, message); + } + + private void HandlePlainText(string sessionId, WebSocketSession session, string text) + { + if (!session.IsAuthenticated) + { + SendSessionEvent(sessionId, "OnWsCommandResponse", + SerializeData(new { success = false, message = "Not authenticated", requestId = "" })); + return; + } + + if (text.StartsWith('/')) + { + var cmd = text[1..]; + var result = new CmdResult(); + PerformInternalCommand("send " + cmd, ref result); + SendSessionEvent(sessionId, "OnMccCommandResponse", + SerializeData(new { command = cmd, status = result.status.ToString(), result = result.result ?? "" })); + } + else + { + SendText(text); + } + } + + // --- Command processing --- + + private void HandleCommand(string sessionId, WebSocketSession session, string command, + string requestId, List parameters) + { + // Protocol commands available without auth + switch (command) + { + case "Authenticate": + HandleAuthenticate(sessionId, session, requestId, parameters); + return; + case "ChangeSessionId": + HandleChangeSessionId(sessionId, session, requestId, parameters); + return; + } + + if (!session.IsAuthenticated) + { + SendCommandResponse(sessionId, requestId, false, "Not authenticated"); + return; + } + + try + { + switch (command) + { + case "LogToConsole": + LogToConsole(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "LogDebugToConsole": + LogDebugToConsole(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "LogToConsoleTranslated": + LogToConsoleTranslated(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "LogDebugToConsoleTranslated": + LogDebugToConsoleTranslated(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "ReconnectToTheServer": + { + var extra = parameters.Count > 0 ? Convert.ToInt32(parameters[0]) : 3; + var delay = parameters.Count > 1 ? Convert.ToInt32(parameters[1]) : 0; + ReconnectToTheServer(extra, delay); + SendCommandResponse(sessionId, requestId, true); + break; + } + + case "DisconnectAndExit": + SendCommandResponse(sessionId, requestId, true); + DisconnectAndExit(); + break; + + case "SendPrivateMessage": + SendPrivateMessage(GetParam(parameters, 0), GetParam(parameters, 1)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "RunScript": + RunScript(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "GetTerrainEnabled": + SendCommandResponse(sessionId, requestId, true, SerializeData(new { enabled = GetTerrainEnabled() })); + break; + + case "SetTerrainEnabled": + SetTerrainEnabled(GetParam(parameters, 0)); + SendCommandResponse(sessionId, requestId, true); + break; + + case "GetEntityHandlingEnabled": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { enabled = GetEntityHandlingEnabled() })); + break; + + case "Sneak": + { + var on = GetParam(parameters, 0); + var result = Sneak(on); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "SendEntityAction": + { + var actionType = ParseEnum(parameters[0]); + var result = SendEntityAction(actionType); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "DigBlock": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + var direction = parameters.Count > 3 ? ParseEnum(parameters[3]) : Direction.Down; + var loc = new Location(x, y, z); + + if (!GetTerrainEnabled()) + { + SendCommandResponse(sessionId, requestId, false, "Terrain not enabled"); + break; + } + + var current = GetCurrentLocation(); + if (current.Distance(loc) > 6.0) + { + SendCommandResponse(sessionId, requestId, false, "Block too far away (max 6 blocks)"); + break; + } + + var world = GetWorld(); + var block = world.GetBlock(loc); + if (block.Type == Material.Air) + { + SendCommandResponse(sessionId, requestId, false, "Block is air"); + break; + } + + var digResult = DigBlock(loc, direction); + SendCommandResponse(sessionId, requestId, digResult); + break; + } + + case "SetSlot": + { + var slot = Convert.ToInt32(parameters[0]); + SetSlot(slot); + SendCommandResponse(sessionId, requestId, true); + break; + } + + case "GetWorld": + { + if (!GetTerrainEnabled()) + { + SendCommandResponse(sessionId, requestId, false, "Terrain not enabled"); + break; + } + // Return basic world info rather than full world data + SendCommandResponse(sessionId, requestId, true, SerializeData(new { available = true })); + break; + } + + case "GetEntities": + { + if (!GetEntityHandlingEnabled()) + { + SendCommandResponse(sessionId, requestId, false, "Entity handling not enabled"); + break; + } + var entities = GetEntities(); + SendCommandResponse(sessionId, requestId, true, SerializeData(entities)); + break; + } + + case "GetPlayersLatency": + { + var latency = GetPlayersLatency(); + SendCommandResponse(sessionId, requestId, true, SerializeData(latency)); + break; + } + + case "GetCurrentLocation": + SendCommandResponse(sessionId, requestId, true, SerializeData(GetCurrentLocation())); + break; + + case "MoveToLocation": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + var allowUnsafe = parameters.Count > 3 && GetParam(parameters, 3); + var allowDirectTp = parameters.Count > 4 && GetParam(parameters, 4); + var maxOffset = parameters.Count > 5 ? Convert.ToInt32(parameters[5]) : 0; + var minOffset = parameters.Count > 6 ? Convert.ToInt32(parameters[6]) : 0; + var result = MoveToLocation(new Location(x, y, z), allowUnsafe, allowDirectTp, maxOffset, minOffset); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "ClientIsMoving": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { moving = ClientIsMoving() })); + break; + + case "LookAtLocation": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + LookAtLocation(new Location(x, y, z)); + SendCommandResponse(sessionId, requestId, true); + break; + } + + case "GetTimestamp": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { timestamp = GetTimestamp() })); + break; + + case "GetServerPort": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { port = GetServerPort() })); + break; + + case "GetServerHost": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { host = GetServerHost() })); + break; + + case "GetUsername": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { username = GetUsername() })); + break; + + case "GetGamemode": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { gamemode = GetGamemode() })); + break; + + case "GetYaw": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { yaw = GetYaw() })); + break; + + case "GetPitch": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { pitch = GetPitch() })); + break; + + case "GetUserUUID": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { uuid = GetUserUUID() })); + break; + + case "GetOnlinePlayers": + SendCommandResponse(sessionId, requestId, true, + SerializeData(GetOnlinePlayers())); + break; + + case "GetOnlinePlayersWithUUID": + SendCommandResponse(sessionId, requestId, true, + SerializeData(GetOnlinePlayersWithUUID())); + break; + + case "GetServerTPS": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { tps = GetServerTPS() })); + break; + + case "InteractEntity": + { + var entityId = Convert.ToInt32(parameters[0]); + var interactType = ParseEnum(parameters[1]); + var hand = parameters.Count > 2 ? ParseEnum(parameters[2]) : Hand.MainHand; + var result = InteractEntity(entityId, interactType, hand); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "CreativeGive": + { + var slot = Convert.ToInt32(parameters[0]); + var itemType = ParseEnum(parameters[1]); + var count = Convert.ToInt32(parameters[2]); + var result = CreativeGive(slot, itemType, count); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "CreativeDelete": + { + var slot = Convert.ToInt32(parameters[0]); + var result = CreativeDelete(slot); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "SendAnimation": + { + var hand = parameters.Count > 0 ? ParseEnum(parameters[0]) : Hand.MainHand; + var result = SendAnimation(hand); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "SendPlaceBlock": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + var direction = ParseEnum(parameters[3]); + var hand = parameters.Count > 4 ? ParseEnum(parameters[4]) : Hand.MainHand; + var result = SendPlaceBlock(new Location(x, y, z), direction, hand); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "UseItemInHand": + { + var result = UseItemInHand(); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "GetInventoryEnabled": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { enabled = GetInventoryEnabled() })); + break; + + case "GetPlayerInventory": + { + if (!GetInventoryEnabled()) + { + SendCommandResponse(sessionId, requestId, false, "Inventory not enabled"); + break; + } + var inv = GetPlayerInventory(); + SendCommandResponse(sessionId, requestId, true, SerializeData(inv)); + break; + } + + case "GetInventories": + { + if (!GetInventoryEnabled()) + { + SendCommandResponse(sessionId, requestId, false, "Inventory not enabled"); + break; + } + var inventories = GetInventories(); + SendCommandResponse(sessionId, requestId, true, SerializeData(inventories)); + break; + } + + case "WindowAction": + { + var inventoryId = Convert.ToInt32(parameters[0]); + var slot = Convert.ToInt32(parameters[1]); + var actionType = ParseEnum(parameters[2]); + var result = WindowAction(inventoryId, slot, actionType); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "ChangeSlot": + { + var slot = Convert.ToInt16(parameters[0]); + var result = ChangeSlot(slot); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "GetCurrentSlot": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { slot = GetCurrentSlot() })); + break; + + case "ClearInventories": + { + var result = ClearInventories(); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "UpdateSign": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + var line1 = GetParam(parameters, 3); + var line2 = GetParam(parameters, 4); + var line3 = GetParam(parameters, 5); + var line4 = GetParam(parameters, 6); + var result = UpdateSign(new Location(x, y, z), line1, line2, line3, line4); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "SelectTrade": + { + var selectedSlot = Convert.ToInt32(parameters[0]); + var result = SelectTrade(selectedSlot); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "UpdateCommandBlock": + { + var x = Convert.ToDouble(parameters[0]); + var y = Convert.ToDouble(parameters[1]); + var z = Convert.ToDouble(parameters[2]); + var cmd = GetParam(parameters, 3); + var mode = ParseEnum(parameters[4]); + var flags = ParseEnum(parameters[5]); + var result = UpdateCommandBlock(new Location(x, y, z), cmd, mode, flags); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "CloseInventory": + { + var inventoryId = Convert.ToInt32(parameters[0]); + var result = CloseInventory(inventoryId); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "GetMaxChatMessageLength": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { length = GetMaxChatMessageLength() })); + break; + + case "Respawn": + { + var result = Respawn(); + SendCommandResponse(sessionId, requestId, result); + break; + } + + case "GetProtocolVersion": + SendCommandResponse(sessionId, requestId, true, + SerializeData(new { protocolVersion = GetProtocolVersion() })); + break; + + case "GetItemTypeMappings": + { + var mappings = new Dictionary(); + foreach (ItemType value in Enum.GetValues(typeof(ItemType))) + mappings[value.ToString()] = (int)value; + SendCommandResponse(sessionId, requestId, true, SerializeData(mappings)); + break; + } + + case "GetEntityTypeMappings": + { + var mappings = new Dictionary(); + foreach (EntityType value in Enum.GetValues(typeof(EntityType))) + mappings[value.ToString()] = (int)value; + SendCommandResponse(sessionId, requestId, true, SerializeData(mappings)); + break; + } + + default: + SendCommandResponse(sessionId, requestId, false, $"Unknown command: {command}"); + break; + } + } + catch (Exception ex) + { + SendCommandResponse(sessionId, requestId, false, $"Error: {ex.Message}"); + } + } + + private void HandleAuthenticate(string sessionId, WebSocketSession session, string requestId, + List parameters) + { + if (parameters.Count == 0) + { + SendCommandResponse(sessionId, requestId, false, "Invalid password"); + return; + } + + var provided = GetParam(parameters, 0); + var expected = _password; + + // Fixed-time comparison to prevent timing attacks + var diff = provided.Length ^ expected.Length; + for (int i = 0; i < expected.Length; i++) + diff |= expected[i] ^ (i < provided.Length ? provided[i] : 0xFF); + + if (diff != 0) + { + SendCommandResponse(sessionId, requestId, false, "Invalid password"); + return; + } + + session.IsAuthenticated = true; + SendCommandResponse(sessionId, requestId, true, "Authenticated"); + } + + private void HandleChangeSessionId(string sessionId, WebSocketSession session, string requestId, + List parameters) + { + if (parameters.Count == 0) + { + SendCommandResponse(sessionId, requestId, false, "New session ID required"); + return; + } + + var newId = GetParam(parameters, 0); + if (string.IsNullOrWhiteSpace(newId)) + { + SendCommandResponse(sessionId, requestId, false, "New session ID cannot be empty"); + return; + } + + if (_server == null) + { + SendCommandResponse(sessionId, requestId, false, "Server not initialized"); + return; + } + + if (_server.RenameSession(sessionId, newId)) + SendCommandResponse(newId, requestId, true, $"Session renamed to {newId}"); + else + SendCommandResponse(sessionId, requestId, false, $"Failed to rename session to {newId}"); + } + + // --- Response helpers --- + + private void SendCommandResponse(string sessionId, string requestId, bool success, string? message = null) + { + var response = new Dictionary + { + ["success"] = success, + ["requestId"] = requestId + }; + + if (message != null) + response["message"] = message; + + SendSessionEvent(sessionId, "OnWsCommandResponse", SerializeData(response)); + } + + // --- Parsing helpers --- + + private static T ParseEnum(object? param) where T : struct, Enum + { + if (param is string s) + { + if (Enum.TryParse(s, ignoreCase: true, out var parsed)) + return parsed; + } + + try + { + var numeric = Convert.ToInt32(param); + return (T)Enum.ToObject(typeof(T), numeric); + } + catch + { + throw new ArgumentException($"Cannot parse '{param}' as {typeof(T).Name}"); + } + } + + private static T GetParam(List parameters, int index) + { + if (index >= parameters.Count) + throw new ArgumentException($"Missing parameter at index {index}"); + + var val = parameters[index]; + + if (val is T typed) + return typed; + + if (typeof(T) == typeof(bool) && val != null) + return (T)(object)Convert.ToBoolean(val); + + if (typeof(T) == typeof(string)) + return (T)(object)(val?.ToString() ?? ""); + + return (T)Convert.ChangeType(val!, typeof(T)); + } +} diff --git a/MinecraftClient/config/sample-script-packet-capture.cs b/MinecraftClient/config/sample-script-packet-capture.cs new file mode 100644 index 00000000..aa8b15ff --- /dev/null +++ b/MinecraftClient/config/sample-script-packet-capture.cs @@ -0,0 +1,178 @@ +//MCCScript 1.0 + +MCC.LoadBot(new PacketCadenceCaptureBot()); + +//MCCScript Extensions + +public class PacketCadenceCaptureBot : ChatBot +{ + private const int CaptureDurationSeconds = 5; + private const int CaptureDurationTicks = CaptureDurationSeconds * 20; + + private readonly Lock _countsLock = new(); + private readonly Dictionary _counts = new() + { + { "PlayerMovement", 0 }, + { "PlayerPosition", 0 }, + { "PlayerPositionAndRotation", 0 }, + { "PlayerRotation", 0 } + }; + private readonly Dictionary _rawOutgoingCounts = new(); + + private bool _captureStarted; + private bool _captureSupported; + private bool _networkPacketEventEnabled; + private int _ticksRemaining; + private int _playerMovementPacketId = -1; + private int _playerPositionPacketId = -1; + private int _playerPositionAndRotationPacketId = -1; + private int _playerRotationPacketId = -1; + private string _profileName = "unsupported"; + + public override void AfterGameJoined() + { + int protocolVersion = GetProtocolVersion(); + _captureSupported = TryConfigureProfile(protocolVersion); + + LogToConsole($"Packet cadence profile: {_profileName} (protocol v{protocolVersion})"); + + if (!_captureSupported) + { + LogToConsole("Packet cadence capture does not know the outgoing movement IDs for this protocol."); + UnloadBot(); + return; + } + + SetNetworkPacketEventEnabled(true); + _networkPacketEventEnabled = true; + + _captureStarted = true; + _ticksRemaining = CaptureDurationTicks; + LogToConsole($"Capturing outgoing movement packets for {CaptureDurationSeconds} seconds."); + } + + public override void Update() + { + if (!_captureStarted) + return; + + if (--_ticksRemaining > 0) + return; + + FinishCapture(); + } + + private void FinishCapture() + { + if (!_captureStarted) + return; + + _captureStarted = false; + + int movementCount; + int positionCount; + int positionAndRotationCount; + int rotationCount; + + lock (_countsLock) + { + movementCount = _counts["PlayerMovement"]; + positionCount = _counts["PlayerPosition"]; + positionAndRotationCount = _counts["PlayerPositionAndRotation"]; + rotationCount = _counts["PlayerRotation"]; + } + + int totalPackets = movementCount + positionCount + positionAndRotationCount + rotationCount; + + LogToConsole($"Packet cadence summary ({_profileName}): total={totalPackets}, " + + $"movement={movementCount}, position={positionCount}, " + + $"posrot={positionAndRotationCount}, rotation={rotationCount}"); + + if (totalPackets == 0) + { + string rawSummary; + lock (_countsLock) + { + var entries = new List(); + foreach (var entry in _rawOutgoingCounts.OrderBy(entry => entry.Key)) + entries.Add($"0x{entry.Key:X2}={entry.Value}"); + rawSummary = entries.Count > 0 ? string.Join(", ", entries) : "none"; + } + + LogToConsole($"Packet cadence raw outbound IDs: {rawSummary}"); + } + + UnloadBot(); + } + + public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) + { + if (!_captureStarted || isLogin || isInbound) + return; + + lock (_countsLock) + { + _rawOutgoingCounts.TryGetValue(packetID, out int rawCount); + _rawOutgoingCounts[packetID] = rawCount + 1; + + if (packetID == _playerMovementPacketId) + _counts["PlayerMovement"]++; + else if (packetID == _playerPositionPacketId) + _counts["PlayerPosition"]++; + else if (packetID == _playerPositionAndRotationPacketId) + _counts["PlayerPositionAndRotation"]++; + else if (packetID == _playerRotationPacketId) + _counts["PlayerRotation"]++; + } + } + + public override void OnUnload() + { + if (!_networkPacketEventEnabled) + return; + + SetNetworkPacketEventEnabled(false); + _networkPacketEventEnabled = false; + } + + private bool TryConfigureProfile(int protocolVersion) + { + switch (protocolVersion) + { + case 47: + _profileName = "1.8/1.8.9"; + _playerMovementPacketId = 0x03; + _playerPositionPacketId = 0x04; + _playerPositionAndRotationPacketId = 0x06; + _playerRotationPacketId = 0x05; + return true; + + case 766: + case 767: + case 768: + case 769: + case 770: + case 771: + case 772: + _profileName = "1.20.6-1.21.8"; + _playerMovementPacketId = 0x1D; + _playerPositionPacketId = 0x1A; + _playerPositionAndRotationPacketId = 0x1B; + _playerRotationPacketId = 0x1C; + return true; + + case 773: + case 774: + case 775: + _profileName = "1.21.9+"; + _playerMovementPacketId = 0x20; + _playerPositionPacketId = 0x1D; + _playerPositionAndRotationPacketId = 0x1E; + _playerRotationPacketId = 0x1F; + return true; + + default: + return false; + } + } +} diff --git a/MinecraftClient/config/sample-script-tick-counter.cs b/MinecraftClient/config/sample-script-tick-counter.cs new file mode 100644 index 00000000..29dbb6da --- /dev/null +++ b/MinecraftClient/config/sample-script-tick-counter.cs @@ -0,0 +1,40 @@ +//MCCScript 1.0 + +MCC.LoadBot(new TickCounterBot()); + +//MCCScript Extensions + +public class TickCounterBot : ChatBot +{ + private const int CaptureDurationSeconds = 5; + + private DateTime _captureEndsAt = DateTime.MaxValue; + private bool _captureStarted; + private int _updateCount; + + public override void AfterGameJoined() + { + _captureEndsAt = DateTime.UtcNow.AddSeconds(CaptureDurationSeconds); + _captureStarted = true; + _updateCount = 0; + LogToConsole($"Counting MCC update ticks for {CaptureDurationSeconds} seconds."); + } + + public override void Update() + { + if (!_captureStarted) + return; + + _updateCount++; + + if (DateTime.UtcNow < _captureEndsAt) + return; + + _captureStarted = false; + + double ticksPerSecond = _updateCount / (double)CaptureDurationSeconds; + LogToConsole($"Tick counter summary: updates={_updateCount}, seconds={CaptureDurationSeconds}, tps={ticksPerSecond:F2}"); + + UnloadBot(); + } +} diff --git a/MinecraftClient/config/sample-script-with-http-request.cs b/MinecraftClient/config/sample-script-with-http-request.cs index 9b24fe34..60e6d27d 100644 --- a/MinecraftClient/config/sample-script-with-http-request.cs +++ b/MinecraftClient/config/sample-script-with-http-request.cs @@ -5,30 +5,19 @@ MCC.LogToConsole(mojangStatus); //MCCScript Extensions +private static readonly System.Net.Http.HttpClient s_httpClient = new(); + string PerformHttpRequest(string uri) { - var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri); - var response = (System.Net.HttpWebResponse)request.GetResponse(); - string responseString; - using (var stream = response.GetResponseStream()) - using (var reader = new StreamReader(stream)) - responseString = reader.ReadToEnd(); - return responseString; + return s_httpClient.GetStringAsync(uri).GetAwaiter().GetResult(); } void SendHttpPostAsync(string uri, string text) { new Thread(() => { - var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri); - request.ContentType = "text/plain"; - request.Method = "POST"; - using (var streamWriter = new StreamWriter(request.GetRequestStream())) - streamWriter.Write(text); - var response = (System.Net.HttpWebResponse)request.GetResponse(); - string responseString; - using (var stream = response.GetResponseStream()) - using (var reader = new StreamReader(stream)) - responseString = reader.ReadToEnd(); + using var content = new System.Net.Http.StringContent(text, System.Text.Encoding.UTF8, "text/plain"); + using var response = s_httpClient.PostAsync(uri, content).GetAwaiter().GetResult(); + string responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); //LogToConsole(responseString); }).Start(); } diff --git a/MinecraftClient/config/sample-script-with-task.cs b/MinecraftClient/config/sample-script-with-task.cs index 2e3e8c2e..c8a17be7 100644 --- a/MinecraftClient/config/sample-script-with-task.cs +++ b/MinecraftClient/config/sample-script-with-task.cs @@ -13,7 +13,7 @@ public class PeriodicTask : ChatBot private DateTime nextTaskRun = DateTime.Now; /// - /// Called on each MCC tick, around 10 times per second + /// Called on each MCC tick, around 20 times per second /// public override void Update() { diff --git a/MinecraftClientGUI/Form1.Designer.cs b/MinecraftClientGUI/Form1.Designer.cs index ba2c948a..e31ea78b 100644 --- a/MinecraftClientGUI/Form1.Designer.cs +++ b/MinecraftClientGUI/Form1.Designer.cs @@ -28,146 +28,20 @@ /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); - this.groupBox_Login = new System.Windows.Forms.GroupBox(); - this.btn_connect = new System.Windows.Forms.Button(); - this.box_ip = new System.Windows.Forms.TextBox(); - this.box_password = new System.Windows.Forms.TextBox(); - this.box_Login = new System.Windows.Forms.TextBox(); - this.box_output = new System.Windows.Forms.RichTextBox(); - this.box_input = new System.Windows.Forms.TextBox(); - this.btn_send = new System.Windows.Forms.Button(); - this.btn_about = new System.Windows.Forms.Button(); - this.groupBox_Login.SuspendLayout(); + this.components = new System.ComponentModel.Container(); this.SuspendLayout(); // - // groupBox_Login - // - this.groupBox_Login.BackColor = System.Drawing.Color.Transparent; - this.groupBox_Login.Controls.Add(this.btn_connect); - this.groupBox_Login.Controls.Add(this.box_ip); - this.groupBox_Login.Controls.Add(this.box_password); - this.groupBox_Login.Controls.Add(this.box_Login); - this.groupBox_Login.Location = new System.Drawing.Point(13, 11); - this.groupBox_Login.Name = "groupBox_Login"; - this.groupBox_Login.Size = new System.Drawing.Size(564, 46); - this.groupBox_Login.TabIndex = 0; - this.groupBox_Login.TabStop = false; - this.groupBox_Login.Text = " "; - // - // btn_connect - // - this.btn_connect.Location = new System.Drawing.Point(513, 15); - this.btn_connect.Name = "btn_connect"; - this.btn_connect.Size = new System.Drawing.Size(40, 23); - this.btn_connect.TabIndex = 6; - this.btn_connect.Text = "Go!"; - this.btn_connect.UseVisualStyleBackColor = true; - this.btn_connect.Click += new System.EventHandler(this.btn_connect_Click); - // - // box_ip - // - this.box_ip.Location = new System.Drawing.Point(400, 17); - this.box_ip.Name = "box_ip"; - this.box_ip.Size = new System.Drawing.Size(100, 20); - this.box_ip.TabIndex = 5; - this.box_ip.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_password - // - this.box_password.Location = new System.Drawing.Point(235, 17); - this.box_password.Name = "box_password"; - this.box_password.PasswordChar = '•'; - this.box_password.Size = new System.Drawing.Size(100, 20); - this.box_password.TabIndex = 3; - this.box_password.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_Login - // - this.box_Login.Location = new System.Drawing.Point(67, 17); - this.box_Login.Name = "box_Login"; - this.box_Login.Size = new System.Drawing.Size(100, 20); - this.box_Login.TabIndex = 1; - this.box_Login.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_output - // - this.box_output.Location = new System.Drawing.Point(13, 66); - this.box_output.Name = "box_output"; - this.box_output.ReadOnly = true; - this.box_output.Size = new System.Drawing.Size(564, 292); - this.box_output.TabIndex = 1; - this.box_output.Text = ""; - this.box_output.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(this.LinkClicked); - // - // box_input - // - this.box_input.AcceptsTab = true; - this.box_input.Location = new System.Drawing.Point(13, 365); - this.box_input.MaxLength = 100; - this.box_input.Multiline = true; - this.box_input.Name = "box_input"; - this.box_input.Size = new System.Drawing.Size(490, 20); - this.box_input.TabIndex = 2; - this.box_input.KeyDown += new System.Windows.Forms.KeyEventHandler(this.inputBox_KeyDown); - // - // btn_send - // - this.btn_send.Location = new System.Drawing.Point(509, 364); - this.btn_send.Name = "btn_send"; - this.btn_send.Size = new System.Drawing.Size(40, 22); - this.btn_send.TabIndex = 3; - this.btn_send.Text = "Send"; - this.btn_send.UseVisualStyleBackColor = true; - this.btn_send.Click += new System.EventHandler(this.btn_send_Click); - // - // btn_about - // - this.btn_about.Location = new System.Drawing.Point(555, 364); - this.btn_about.Name = "btn_about"; - this.btn_about.Size = new System.Drawing.Size(22, 22); - this.btn_about.TabIndex = 4; - this.btn_about.Text = "?"; - this.btn_about.UseVisualStyleBackColor = true; - this.btn_about.Click += new System.EventHandler(this.btn_about_Click); - // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.SystemColors.Control; - this.ClientSize = new System.Drawing.Size(589, 398); - this.Controls.Add(this.btn_about); - this.Controls.Add(this.btn_send); - this.Controls.Add(this.box_input); - this.Controls.Add(this.box_output); - this.Controls.Add(this.groupBox_Login); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; + this.ClientSize = new System.Drawing.Size(1100, 700); this.Name = "Form1"; + this.Text = "MCC Multibox Commander"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Minecraft Console Client GUI"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.onClose); - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox_Login.ResumeLayout(false); - this.groupBox_Login.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); - } #endregion - - private System.Windows.Forms.GroupBox groupBox_Login; - private System.Windows.Forms.Button btn_connect; - private System.Windows.Forms.TextBox box_ip; - private System.Windows.Forms.TextBox box_password; - private System.Windows.Forms.TextBox box_Login; - private System.Windows.Forms.RichTextBox box_output; - private System.Windows.Forms.TextBox box_input; - private System.Windows.Forms.Button btn_send; - private System.Windows.Forms.Button btn_about; } } - diff --git a/MinecraftClientGUI/Form1.cs b/MinecraftClientGUI/Form1.cs index 189cab59..60698982 100644 --- a/MinecraftClientGUI/Form1.cs +++ b/MinecraftClientGUI/Form1.cs @@ -1,337 +1,794 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Data; +using System.Diagnostics; using System.Drawing; +using System.IO; using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Runtime.InteropServices; using System.Threading; +using System.Windows.Forms; namespace MinecraftClientGUI { - /// - /// The main graphical user interface - /// + static class Theme + { + public static Color BgDark = Color.FromArgb(15, 15, 18); + public static Color BgPanel = Color.FromArgb(22, 22, 28); + public static Color BgHeader = Color.FromArgb(28, 28, 36); + public static Color BgCard = Color.FromArgb(32, 32, 42); + public static Color BgInput = Color.FromArgb(20, 20, 26); + public static Color TabActive = Color.FromArgb(38, 38, 52); + public static Color TabInactive = Color.FromArgb(22, 22, 28); + public static Color Accent = Color.FromArgb(82, 130, 255); + public static Color AccentHover = Color.FromArgb(110, 155, 255); + public static Color AccentRed = Color.FromArgb(220, 70, 70); + public static Color AccentGreen = Color.FromArgb(60, 200, 100); + public static Color Text = Color.FromArgb(220, 220, 230); + public static Color TextDim = Color.FromArgb(120, 120, 140); + public static Color TextMuted = Color.FromArgb(70, 70, 90); + public static Color Border = Color.FromArgb(40, 40, 55); + } + + class DarkComboBox : ComboBox + { + public DarkComboBox() + { + DrawMode = DrawMode.OwnerDrawFixed; + FlatStyle = FlatStyle.Flat; + BackColor = Theme.BgInput; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f); + } + protected override void OnDrawItem(DrawItemEventArgs e) + { + if (e.Index < 0) return; + e.Graphics.FillRectangle( + new SolidBrush((e.State & DrawItemState.Selected) != 0 ? Theme.TabActive : Theme.BgInput), + e.Bounds); + TextRenderer.DrawText(e.Graphics, Items[e.Index].ToString(), Font, e.Bounds, + Theme.Text, TextFormatFlags.VerticalCenter | TextFormatFlags.Left); + } + } + + class FlatBtn : Button + { + private Color _back, _hover; + public FlatBtn(string text, Color back, Color? hover = null) + { + Text = text; + _back = back; + _hover = hover ?? ControlPaint.Light(back, 0.2f); + FlatStyle = FlatStyle.Flat; + FlatAppearance.BorderSize = 0; + BackColor = _back; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f, FontStyle.Bold); + Cursor = Cursors.Hand; + MouseEnter += (s, e) => BackColor = _hover; + MouseLeave += (s, e) => BackColor = _back; + } + } public partial class Form1 : Form { - private LinkedList previous = new LinkedList(); - private MinecraftClient Client; - private Thread t_clientread; - - #region Aero Glass Low-level Windows API - - [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] - public struct MARGINS + private const string SettingsFile = "settings_v3.txt"; + private const string MacrosFile = "macros.txt"; + private static readonly string[] DefaultSettingsContent = new[] { "", "", "" }; + private static readonly string[] DefaultMacrosContent = new[] { - public int Left; - public int Right; - public int Top; - public int Bottom; - } + "Creative|/gamemode creative|Gold", + "Survival|/gamemode survival|Gray", + "Hello|Hello everyone!|Green", + "Login|/login password123|Purple", + "Spawn|/spawn|Blue" + }; + private string currentLang = "en"; - [System.Runtime.InteropServices.DllImport("dwmapi.dll")] - public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins); + private Panel tabBar, contentArea, topPanel, bottomPanel, rightPanel; + private Label lblLogin, lblPass, lblIP, lblActive; + private DarkComboBox cmbLogin, cmbIP; + private TextBox txtPassword; + private FlatBtn btnAddBot; + private TextBox boxGlobalInput; + private FlatBtn btnGlobalSend; + private CheckBox chkSendToAll; + private Label lblMacrosTitle; + private FlowLayoutPanel macroPanel; + private FlatBtn btnEditMacros, btnRefreshMacros, btnLangSwitch; - #endregion + private List historyLogins = new List(); + private List historyIPs = new List(); + private List tabs = new List(); + private ConsoleTab activeTab = null; public Form1(string[] args) { InitializeComponent(); - if (args.Length > 0) { initClient(new MinecraftClient(args)); } + BuildUI(); + EnsureRuntimeFiles(); + LoadSettings(); + LoadMacros(); + UpdateLanguage(); + if (args.Length > 0) AddNewTab("Auto-Bot", args); + this.FormClosing += (s, e) => { foreach (var t in tabs.ToList()) t.CloseTab(); }; } - /// - /// Define some element properties and init Aero Glass if using Vista or newer - /// - - private void Form1_Load(object sender, EventArgs e) + private static void EnsureRuntimeFiles() { - box_output.ScrollBars = RichTextBoxScrollBars.None; - box_output.Font = new Font("Consolas", 8); - box_output.BackColor = Color.White; + EnsureFileExists(SettingsFile, DefaultSettingsContent); + EnsureFileExists(MacrosFile, DefaultMacrosContent); + } - if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor == 1) + private static void EnsureFileExists(string path, string[] defaultContent) + { + if (!File.Exists(path)) { - this.BackColor = Color.DarkMagenta; this.TransparencyKey = Color.DarkMagenta; - MARGINS marg = new MARGINS() { Left = -1, Right = -1, Top = -1, Bottom = -1 }; - DwmExtendFrameIntoClientArea(this.Handle, ref marg); + File.WriteAllLines(path, defaultContent); } } - /// - /// Launch the Minecraft Client by clicking the "Go!" button. - /// If a client is already running, it will be closed. - /// - - private void btn_connect_Click(object sender, EventArgs e) + private void BuildUI() { - if (Client != null) + this.Text = "MCC Multibox Commander"; + this.Size = new Size(1200, 780); + this.MinimumSize = new Size(900, 600); + this.BackColor = Theme.BgDark; + this.ForeColor = Theme.Text; + this.Font = new Font("Segoe UI", 9f); + this.StartPosition = FormStartPosition.CenterScreen; + + // TOP PANEL + topPanel = new Panel { Dock = DockStyle.Top, Height = 60, BackColor = Theme.BgHeader, Padding = new Padding(12, 0, 12, 0) }; + topPanel.Paint += PaintBottomBorder; + this.Controls.Add(topPanel); + + int y = 17; + lblLogin = MkLabel("Username / Email:", 10, 2); topPanel.Controls.Add(lblLogin); + cmbLogin = new DarkComboBox { Location = new Point(10, y), Size = new Size(195, 26) }; topPanel.Controls.Add(cmbLogin); + + lblPass = MkLabel("Password:", 215, 2); topPanel.Controls.Add(lblPass); + txtPassword = new TextBox { Location = new Point(215, y), Size = new Size(155, 26), BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, UseSystemPasswordChar = true, Font = new Font("Segoe UI", 9f) }; + topPanel.Controls.Add(txtPassword); + + lblIP = MkLabel("Server IP:", 380, 2); topPanel.Controls.Add(lblIP); + cmbIP = new DarkComboBox { Location = new Point(380, y), Size = new Size(215, 26) }; topPanel.Controls.Add(cmbIP); + + btnAddBot = new FlatBtn("+ Add Account", Theme.Accent, Theme.AccentHover) { Location = new Point(608, y - 1), Size = new Size(148, 28) }; + btnAddBot.Click += BtnAddBot_Click; + topPanel.Controls.Add(btnAddBot); + + lblActive = new Label { Location = new Point(770, y), AutoSize = true, ForeColor = Theme.AccentGreen, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + topPanel.Controls.Add(lblActive); + + var timer = new System.Windows.Forms.Timer { Interval = 1000 }; + timer.Tick += (s, e) => lblActive.Text = (currentLang == "en" ? "Active accounts: " : "Aktywne konta: ") + tabs.Count; + timer.Start(); + + // RIGHT PANEL + rightPanel = new Panel { Dock = DockStyle.Right, Width = 185, BackColor = Theme.BgPanel }; + rightPanel.Paint += PaintLeftBorder; + this.Controls.Add(rightPanel); + + // Language toggle - large button at the top + btnLangSwitch = new FlatBtn("PL", Color.FromArgb(45, 75, 145), Color.FromArgb(60, 100, 185)) { - Client.Close(); - t_clientread.Abort(); - box_output.Text = ""; - } - string username = box_Login.Text; - string password = box_password.Text; - string serverip = box_ip.Text; - if (password == "") { password = "-"; } - if (username != "" && serverip != "") - { - initClient(new MinecraftClient(username, password, serverip)); - } + Dock = DockStyle.Top, + Height = 36, + Font = new Font("Segoe UI", 11f, FontStyle.Bold), + ForeColor = Color.White + }; + btnLangSwitch.Click += (s, e) => { currentLang = currentLang == "en" ? "pl" : "en"; UpdateLanguage(); }; + rightPanel.Controls.Add(btnLangSwitch); + + // Macro header + var macroHeader = new Panel { Dock = DockStyle.Top, Height = 52, BackColor = Theme.BgPanel }; + macroHeader.Paint += PaintBottomBorder; + rightPanel.Controls.Add(macroHeader); + + lblMacrosTitle = new Label { Text = "Quick Actions", Location = new Point(8, 8), Size = new Size(169, 18), ForeColor = Theme.Text, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + macroHeader.Controls.Add(lblMacrosTitle); + + btnEditMacros = new FlatBtn("Edit", Color.FromArgb(40, 40, 58)) { Location = new Point(8, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnEditMacros.Click += BtnEditMacros_Click; + macroHeader.Controls.Add(btnEditMacros); + + btnRefreshMacros = new FlatBtn("Reload", Color.FromArgb(40, 40, 58)) { Location = new Point(90, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnRefreshMacros.Click += (s, e) => LoadMacros(); + macroHeader.Controls.Add(btnRefreshMacros); + + macroPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.TopDown, WrapContents = false, AutoScroll = true, BackColor = Theme.BgPanel, Padding = new Padding(8, 8, 0, 8) }; + rightPanel.Controls.Add(macroPanel); + + rightPanel.Controls.SetChildIndex(macroPanel, 0); + rightPanel.Controls.SetChildIndex(macroHeader, 1); + rightPanel.Controls.SetChildIndex(btnLangSwitch, 2); + + // BOTTOM PANEL + bottomPanel = new Panel { Dock = DockStyle.Bottom, Height = 40, BackColor = Theme.BgHeader, Padding = new Padding(6, 6, 6, 0) }; + bottomPanel.Paint += PaintTopBorder; + this.Controls.Add(bottomPanel); + + btnGlobalSend = new FlatBtn("Send", Color.FromArgb(50, 90, 160), Theme.Accent) { Dock = DockStyle.Right, Width = 80 }; + btnGlobalSend.Click += BtnGlobalSend_Click; + bottomPanel.Controls.Add(btnGlobalSend); + + chkSendToAll = new CheckBox { Text = "Send to all", Dock = DockStyle.Right, Width = 120, ForeColor = Color.FromArgb(255, 160, 90), Padding = new Padding(8, 0, 0, 0), Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + bottomPanel.Controls.Add(chkSendToAll); + + boxGlobalInput = new TextBox { Dock = DockStyle.Fill, BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, Font = new Font("Consolas", 11f) }; + boxGlobalInput.KeyDown += (s, e) => { if (e.KeyCode == Keys.Enter) { BtnGlobalSend_Click(s, e); e.SuppressKeyPress = true; } }; + bottomPanel.Controls.Add(boxGlobalInput); + + // TAB BAR + tabBar = new Panel { Dock = DockStyle.Top, Height = 38, BackColor = Theme.BgPanel }; + tabBar.Paint += PaintBottomBorder; + this.Controls.Add(tabBar); + + // CONTENT AREA + contentArea = new Panel { Dock = DockStyle.Fill, BackColor = Theme.BgDark }; + this.Controls.Add(contentArea); + + this.Controls.SetChildIndex(contentArea, 0); + this.Controls.SetChildIndex(tabBar, 1); + this.Controls.SetChildIndex(bottomPanel, 2); + this.Controls.SetChildIndex(rightPanel, 3); + this.Controls.SetChildIndex(topPanel, 4); } - /// - /// Handle a new Minecraft Client - /// - /// Client to handle - - private void initClient(MinecraftClient client) + private void AddNewTab(string title, string[] args) { - Client = client; - t_clientread = new Thread(new ThreadStart(t_clientread_loop)); - t_clientread.Start(); - box_input.Select(); + var tab = new ConsoleTab(title, args, currentLang) { Dock = DockStyle.Fill }; + tabs.Add(tab); + contentArea.Controls.Add(tab); + RebuildTabBar(); + ActivateTab(tab); } - /// - /// Thread reading output from the Minecraft Client - /// - - private void t_clientread_loop() + private void ActivateTab(ConsoleTab tab) { - while (true && !Client.Disconnected) - { - printstring(Client.ReadLine()); - } + activeTab = tab; + foreach (Control c in contentArea.Controls) c.Visible = (c == tab); + RebuildTabBar(); } - /// - /// Print a Minecraft-Formatted string to the console area - /// - /// String to print - - private void printstring(string str) + private void RebuildTabBar() { - if (!String.IsNullOrEmpty(str)) + tabBar.Controls.Clear(); + int x = 4; + foreach (var tab in tabs) { - Color color = Color.Black; - FontStyle style = FontStyle.Regular; - string[] subs = str.Split('§'); - if (subs[0].Length > 0) { AppendTextBox(box_output, subs[0], Color.Black, FontStyle.Regular); } - for (int i = 1; i < subs.Length; i++) + var t = tab; + bool active = (t == activeTab); + + var btn = new Panel { Location = new Point(x, active ? 2 : 5), Size = new Size(148, active ? 32 : 27), BackColor = active ? Theme.TabActive : Theme.TabInactive, Cursor = Cursors.Hand }; + btn.Paint += (s, e) => { + if (t == activeTab) + e.Graphics.FillRectangle(new SolidBrush(Theme.Accent), 0, btn.Height - 2, btn.Width, 2); + }; + + var lbl = new Label { - if (subs[i].Length > 0) + Text = t.TabTitle, + Location = new Point(8, 0), + Size = new Size(108, 30), + ForeColor = active ? Theme.Text : Theme.TextDim, + Font = new Font("Segoe UI", 9f, active ? FontStyle.Bold : FontStyle.Regular), + TextAlign = ContentAlignment.MiddleLeft, + Cursor = Cursors.Hand + }; + lbl.Click += (s, e) => ActivateTab(t); + btn.Click += (s, e) => ActivateTab(t); + btn.Controls.Add(lbl); + + var btnX = new Label + { + Text = "x", + Location = new Point(120, 0), + Size = new Size(25, 30), + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f), + TextAlign = ContentAlignment.MiddleCenter, + Cursor = Cursors.Hand + }; + btnX.MouseEnter += (s, e) => btnX.ForeColor = Theme.AccentRed; + btnX.MouseLeave += (s, e) => btnX.ForeColor = Theme.TextMuted; + btnX.Click += (s, e) => { + t.CloseTab(); + tabs.Remove(t); + contentArea.Controls.Remove(t); + if (activeTab == t) { activeTab = tabs.LastOrDefault(); if (activeTab != null) ActivateTab(activeTab); } + RebuildTabBar(); + }; + btn.Controls.Add(btnX); + tabBar.Controls.Add(btn); + x += 152; + } + } + + private void UpdateLanguage() + { + bool en = currentLang == "en"; + btnLangSwitch.Text = en ? "Switch to PL" : "Switch to EN"; + lblLogin.Text = en ? "Username / Email:" : "Login / Email:"; + lblPass.Text = en ? "Password:" : "Haslo:"; + lblIP.Text = en ? "Server IP:" : "IP Serwera:"; + btnAddBot.Text = en ? "+ Add Account" : "+ Dodaj Konto"; + lblMacrosTitle.Text = en ? "Quick Actions" : "Szybkie Akcje"; + btnEditMacros.Text = en ? "Edit" : "Edytuj"; + btnRefreshMacros.Text = en ? "Reload" : "Odswiez"; + btnGlobalSend.Text = en ? "Send" : "Wyslij"; + chkSendToAll.Text = en ? "Send to all" : "Wyslij do wszystkich"; + foreach (var tab in tabs) tab.UpdateLang(currentLang); + } + + private void BtnEditMacros_Click(object sender, EventArgs e) + { + EnsureFileExists(MacrosFile, DefaultMacrosContent); + Process.Start("notepad.exe", MacrosFile); + } + + private void LoadMacros() + { + macroPanel.Controls.Clear(); + if (!File.Exists(MacrosFile)) return; + try + { + foreach (var line in File.ReadAllLines(MacrosFile)) + { + if (string.IsNullOrWhiteSpace(line)) continue; + var parts = line.Split('|'); + if (parts.Length >= 2) { - if (subs[i].Length > 1) - { - switch (subs[i][0]) - { - //Font colors - case '0': color = Color.Black; break; - case '1': color = Color.DarkBlue; break; - case '2': color = Color.DarkGreen; break; - case '3': color = Color.DarkCyan; break; - case '4': color = Color.DarkRed; break; - case '5': color = Color.DarkMagenta; break; - case '6': color = Color.DarkGoldenrod; break; - case '7': color = Color.DimGray; break; - case '8': color = Color.Gray; break; - case '9': color = Color.Blue; break; - case 'a': color = Color.Green; break; - case 'b': color = Color.CornflowerBlue; break; - case 'c': color = Color.Red; break; - case 'd': color = Color.Magenta; break; - case 'e': color = Color.Goldenrod; break; - - //White on white = invisible so use gray instead - case 'f': color = Color.DimGray; break; - - //Font styles. Can use several styles eg Bold + Underline - case 'l': style = style | FontStyle.Bold; break; - case 'm': style = style | FontStyle.Strikeout; break; - case 'n': style = style | FontStyle.Underline; break; - case 'o': style = style | FontStyle.Italic; break; - - //Reset font color & style - case 'r': color = Color.Black; style = FontStyle.Regular; break; - } - - AppendTextBox(box_output, subs[i].Substring(1, subs[i].Length - 1), color, style); - } + Color c = parts.Length > 2 ? Color.FromName(parts[2]) : Theme.Accent; + if (c.IsEmpty) c = Theme.Accent; + AddMacroBtn(parts[1], parts[0], c); } } - AppendTextBox(box_output, "\n", Color.Black, FontStyle.Regular); } - Console.ForegroundColor = ConsoleColor.Gray; + catch (Exception ex) { MessageBox.Show("Error loading macros: " + ex.Message); } } - /// - /// Append text to a RichTextBox with font customization - /// - /// Target RichTextBox - /// Text to add - /// Color of the text - /// Font style of the text - - private void AppendTextBox(RichTextBox box, string text, Color color, FontStyle style) + private void AddMacroBtn(string cmd, string label, Color color) { - if (InvokeRequired) + Color bg = Color.FromArgb(32, 32, 48); + Color hov = Color.FromArgb(44, 44, 64); + var btn = new Button { - this.Invoke(new Action(AppendTextBox), new object[] { box, text, color, style }); + Text = " " + label, + Width = macroPanel.Width - 22, + Height = 34, + FlatStyle = FlatStyle.Flat, + BackColor = bg, + ForeColor = Theme.Text, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand, + Margin = new Padding(0, 0, 0, 4), + TextAlign = ContentAlignment.MiddleLeft, + Tag = color + }; + btn.FlatAppearance.BorderSize = 0; + btn.MouseEnter += (s, e) => btn.BackColor = hov; + btn.MouseLeave += (s, e) => btn.BackColor = bg; + btn.Paint += (s, e) => e.Graphics.FillRectangle(new SolidBrush(color), 0, 0, 4, btn.Height); + btn.Click += (s, e) => { + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + }; + macroPanel.Controls.Add(btn); + } + + private void BtnAddBot_Click(object sender, EventArgs e) + { + string user = cmbLogin.Text.Trim(), pass = txtPassword.Text.Trim(), ip = cmbIP.Text.Trim(); + if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(ip)) + { + MessageBox.Show(currentLang == "en" ? "Please enter username and IP!" : "Podaj login i IP serwera!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + SaveSettings(user, ip); + string tabTitle = user.Contains("@") ? user.Split('@')[0] : user; + AddNewTab(tabTitle, new[] { user, pass, ip }); + } + + private void BtnGlobalSend_Click(object sender, EventArgs e) + { + string cmd = boxGlobalInput.Text.Trim(); + if (string.IsNullOrEmpty(cmd)) return; + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + boxGlobalInput.Clear(); + } + + private void LoadSettings() + { + try + { + if (!File.Exists(SettingsFile)) return; + var lines = File.ReadAllLines(SettingsFile).ToList(); + while (lines.Count < 3) + { + lines.Add(string.Empty); + } + if (lines.Count > 0) txtPassword.Text = lines[0]; + if (lines.Count > 1) { historyLogins = lines[1].Split('|').ToList(); cmbLogin.Items.AddRange(historyLogins.ToArray()); if (cmbLogin.Items.Count > 0) cmbLogin.SelectedIndex = 0; } + if (lines.Count > 2) { historyIPs = lines[2].Split('|').ToList(); cmbIP.Items.AddRange(historyIPs.ToArray()); if (cmbIP.Items.Count > 0) cmbIP.SelectedIndex = 0; } + } + catch { } + } + + private void SaveSettings(string user, string ip) + { + historyLogins.Remove(user); historyLogins.Insert(0, user); if (historyLogins.Count > 10) historyLogins.RemoveAt(10); + historyIPs.Remove(ip); historyIPs.Insert(0, ip); if (historyIPs.Count > 10) historyIPs.RemoveAt(10); + cmbLogin.Items.Clear(); cmbLogin.Items.AddRange(historyLogins.ToArray()); cmbLogin.Text = user; + cmbIP.Items.Clear(); cmbIP.Items.AddRange(historyIPs.ToArray()); cmbIP.Text = ip; + try { File.WriteAllLines(SettingsFile, new[] { txtPassword.Text, string.Join("|", historyLogins), string.Join("|", historyIPs) }); } catch { } + } + + private void PaintBottomBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, c.Height - 1, c.Width, c.Height - 1); } + private void PaintTopBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, c.Width, 0); } + private void PaintLeftBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, 0, c.Height); } + private Label MkLabel(string text, int x, int y) => new Label { Text = text, Location = new Point(x, y), AutoSize = true, ForeColor = Theme.TextDim, Font = new Font("Segoe UI", 8f) }; + } + + // ===================================================== + // LINE TYPES (for filtering) + // ===================================================== + enum LineType { Chat, System, Error } + + struct LogLine + { + public string Raw; + public string Display; + public LineType Type; + public DateTime Time; + } + + public class ConsoleTab : Panel + { + private MinecraftClient Client; + private Thread t_read; + private RichTextBox boxOutput; + private Button btnDisconnect; + private Label lblStatus; + private Label lblTimer; + private CheckBox chkAutoScroll; + private bool autoScroll = true; + + private Button btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError; + private LineType? activeFilter = null; + + private List allLines = new List(); + private object logLock = new object(); + + private StreamWriter logWriter; + private DateTime connectedAt; + private System.Windows.Forms.Timer timerClock; + private bool isConnected = false; + + public string TabTitle { get; set; } + + [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); + [DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] + private static extern int SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList); + private const int WM_VSCROLL = 0x115, SB_BOTTOM = 7; + + public ConsoleTab(string title, string[] args, string lang) + { + this.TabTitle = title; + this.BackColor = Theme.BgDark; + + InitLogFile(title); + + // Top bar + var topBar = new Panel { Dock = DockStyle.Top, Height = 36, BackColor = Theme.BgCard }; + topBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, topBar.Height - 1, topBar.Width, topBar.Height - 1); + + btnDisconnect = new Button + { + Text = lang == "en" ? "Disconnect" : "Rozlacz", + Dock = DockStyle.Right, + Width = 105, + FlatStyle = FlatStyle.Flat, + BackColor = Color.FromArgb(160, 45, 45), + ForeColor = Color.White, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btnDisconnect.FlatAppearance.BorderSize = 0; + btnDisconnect.Click += (s, e) => CloseTab(); + topBar.Controls.Add(btnDisconnect); + + chkAutoScroll = new CheckBox + { + Text = "Auto-scroll", + Dock = DockStyle.Right, + Width = 95, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f), + Checked = true, + Padding = new Padding(0, 0, 12, 0) + }; + chkAutoScroll.CheckedChanged += (s, e) => autoScroll = chkAutoScroll.Checked; + topBar.Controls.Add(chkAutoScroll); + + lblTimer = new Label + { + Text = "00:00:00", + Dock = DockStyle.Right, + Width = 70, + ForeColor = Theme.TextDim, + Font = new Font("Consolas", 8f), + TextAlign = ContentAlignment.MiddleCenter + }; + topBar.Controls.Add(lblTimer); + + lblStatus = new Label + { + Text = " ● " + title, + Dock = DockStyle.Fill, + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + TextAlign = ContentAlignment.MiddleLeft + }; + topBar.Controls.Add(lblStatus); + this.Controls.Add(topBar); + + // Filter bar + var filterBar = new Panel { Dock = DockStyle.Top, Height = 30, BackColor = Theme.BgPanel }; + filterBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, filterBar.Height - 1, filterBar.Width, filterBar.Height - 1); + + btnFilterAll = MakeFilterBtn("All", null, filterBar, 4); + btnFilterChat = MakeFilterBtn("Chat", LineType.Chat, filterBar, 54); + btnFilterSystem = MakeFilterBtn("System", LineType.System, filterBar, 118); + btnFilterError = MakeFilterBtn("Errors", LineType.Error, filterBar, 192); + SetFilterActive(btnFilterAll); + this.Controls.Add(filterBar); + + // Console + boxOutput = new RichTextBox + { + Dock = DockStyle.Fill, + BackColor = Theme.BgDark, + ForeColor = Color.FromArgb(200, 200, 215), + Font = new Font("Consolas", 10f), + BorderStyle = BorderStyle.None, + ReadOnly = true, + ScrollBars = RichTextBoxScrollBars.Vertical + }; + var ctx = new ContextMenuStrip { BackColor = Theme.BgCard, ForeColor = Theme.Text }; + ctx.Items.Add("Disconnect / Close", null, (s, e) => CloseTab()); + ctx.Items.Add("Clear Console", null, (s, e) => { boxOutput.Clear(); lock (logLock) allLines.Clear(); }); + boxOutput.ContextMenuStrip = ctx; + this.Controls.Add(boxOutput); + + try { SetWindowTheme(boxOutput.Handle, "DarkMode_Explorer", null); } catch { } + + timerClock = new System.Windows.Forms.Timer { Interval = 1000 }; + timerClock.Tick += (s, e) => { + if (isConnected && !lblTimer.IsDisposed) + { + var elapsed = DateTime.Now - connectedAt; + lblTimer.Text = elapsed.ToString(@"hh\:mm\:ss"); + } + }; + timerClock.Start(); + + PrintSystem("Initializing...", LineType.System); + if (args.Length == 3) new Thread(() => InitClient(new MinecraftClient(args[0], args[1], args[2]))).Start(); + else new Thread(() => InitClient(new MinecraftClient(args))).Start(); + } + + private void InitLogFile(string title) + { + try + { + string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); + Directory.CreateDirectory(dir); + string safeName = string.Concat(title.Split(Path.GetInvalidFileNameChars())); + string date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm"); + string path = Path.Combine(dir, safeName + "_" + date + ".txt"); + logWriter = new StreamWriter(path, append: true, encoding: System.Text.Encoding.UTF8) { AutoFlush = true }; + logWriter.WriteLine("=== Session started: " + DateTime.Now + " | Account: " + title + " ==="); + } + catch { } + } + + private void WriteLog(string text, LineType type) + { + try { logWriter?.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss") + "][" + type + "] " + text); } catch { } + } + + private Button MakeFilterBtn(string label, LineType? type, Panel parent, int x) + { + int w = label == "All" ? 44 : label == "System" ? 68 : 58; + var btn = new Button + { + Text = label, + Location = new Point(x, 4), + Size = new Size(w, 22), + FlatStyle = FlatStyle.Flat, + BackColor = Theme.BgCard, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btn.FlatAppearance.BorderSize = 1; + btn.FlatAppearance.BorderColor = Theme.Border; + btn.Click += (s, e) => { activeFilter = type; SetFilterActive(btn); RedrawFiltered(); }; + parent.Controls.Add(btn); + return btn; + } + + private void SetFilterActive(Button active) + { + foreach (var b in new[] { btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError }) + { + if (b == null) continue; + b.BackColor = b == active ? Theme.Accent : Theme.BgCard; + b.ForeColor = b == active ? Color.White : Theme.TextDim; + } + } + + private void RedrawFiltered() + { + InvokeUI(() => { + boxOutput.Clear(); + List snapshot; + lock (logLock) snapshot = new List(allLines); + foreach (var line in snapshot) + if (activeFilter == null || line.Type == activeFilter) + RenderLine(line); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + }); + } + + private void RenderLine(LogLine line) + { + if (line.Type == LineType.System || line.Type == LineType.Error) + { + boxOutput.SelectionColor = line.Type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105); + boxOutput.AppendText(line.Display + "\n"); } else { - box.SelectionStart = box.TextLength; - box.SelectionLength = 0; - box.SelectionColor = color; - box.SelectionFont = new Font(box.Font, style); - box.AppendText(text); - box.SelectionColor = box.ForeColor; - box.SelectionStart = box.Text.Length; - box.ScrollToCaret(); - } - } - - /// - /// Properly disconnect the client when clicking the [X] close button - /// - - protected void onClose(object sender, EventArgs e) - { - if (t_clientread != null) { t_clientread.Abort(); } - if (Client != null) { new Thread(new ThreadStart(Client.Close)).Start(); } - } - - /// - /// Allows an Enter keypress in "Login", "Password" or "Server IP" box to be considered as a click on the "Go!" button - /// - /// - /// - - public void loginBox_KeyUp(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - { - btn_connect_Click(sender, e); - e.Handled = true; - } - } - - /// - /// Handle special functions in the input box : send with Enter key, command history and tab-complete - /// - /// - /// - - public void inputBox_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - { - btn_send_Click(sender, e); - e.Handled = true; - } - else if (e.KeyCode == Keys.Down) - { - if (previous.Count > 0) + string[] subs = line.Raw.Split('\u00a7'); + boxOutput.SelectionColor = Color.FromArgb(200, 200, 215); + if (subs.Length > 0) boxOutput.AppendText(subs[0]); + for (int i = 1; i < subs.Length; i++) { - box_input.Text = previous.First.Value; - previous.AddLast(box_input.Text); - previous.RemoveFirst(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; - } - else if (e.KeyCode == Keys.Up) - { - if (previous.Count > 0) - { - box_input.Text = previous.Last.Value; - previous.AddFirst(box_input.Text); - previous.RemoveLast(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; - } - else if (e.KeyCode == Keys.Tab) - { - if (box_input.SelectionStart > 0) - { - string behind_cursor = box_input.Text.Substring(0, box_input.SelectionStart); - string after_cursor = box_input.Text.Substring(box_input.SelectionStart); - string[] behind_temp = behind_cursor.Split(' '); - string autocomplete = Client.tabAutoComplete(behind_temp[behind_temp.Length - 1]); - if (!String.IsNullOrEmpty(autocomplete)) + if (subs[i].Length > 1) { - behind_temp[behind_temp.Length - 1] = autocomplete; - behind_cursor = String.Join(" ", behind_temp); - box_input.Text = behind_cursor + after_cursor; - box_input.SelectionStart = behind_cursor.Length; + boxOutput.SelectionColor = GetColor(subs[i][0]); + boxOutput.SelectionFont = GetFont(subs[i][0], boxOutput.Font); + boxOutput.AppendText(subs[i].Substring(1)); } } - e.SuppressKeyPress = true; - e.Handled = true; + boxOutput.AppendText("\n"); } } - /// - /// Send the input in the input box, if any, by pressing the "Send" button. - /// Handle "/quit" command to properly disconnect and close the GUI. - /// + public void UpdateLang(string lang) { if (btnDisconnect != null) btnDisconnect.Text = lang == "en" ? "Disconnect" : "Rozlacz"; } - private void btn_send_Click(object sender, EventArgs e) + private void InitClient(MinecraftClient client) { - if (Client != null) + Client = client; + t_read = new Thread(ReadLoop) { IsBackground = true }; + t_read.Start(); + connectedAt = DateTime.Now; + isConnected = true; + InvokeUI(() => { + lblStatus.Text = " ● " + TabTitle; + lblStatus.ForeColor = Color.FromArgb(100, 210, 130); + PrintSystem("Connected.", LineType.System); + }); + } + + private void ReadLoop() + { + try { - if (box_input.Text.Trim().ToLower() == "/quit") + while (Client != null && !Client.Disconnected) { - Close(); - } - else - { - Client.SendText(box_input.Text); - previous.AddLast(box_input.Text); - box_input.Text = ""; + string line = Client.ReadLine(); + if (!string.IsNullOrEmpty(line)) PrintChat(line); } } - } - - /// - /// Draw text on glass pane without ClearType, only black pixels - /// - - protected override void OnPaint(PaintEventArgs e) - { - e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; - e.Graphics.DrawString("Login Details", this.Font, Brushes.Black, 20, 11); - e.Graphics.DrawString("Username:", this.Font, Brushes.Black, 20, 31); - e.Graphics.DrawString("Password:", this.Font, Brushes.Black, 191, 31); - e.Graphics.DrawString("Server IP:", this.Font, Brushes.Black, 355, 31); - } - - /// - /// Show the "About" message box, open the official topic in an internet browser if the user press OK. - /// - - private void btn_about_Click(object sender, EventArgs e) - { - if (MessageBox.Show("MCC GUI version 1.0 - (c) 2013 ORelio\nAllows to send commands to any Minecraft server\nand receive text messages in a fast and easy way.\n\nPress OK to visit the official topic on Minecraft Forums.", - "About Minecraft Console Client", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK) + catch (ThreadAbortException) { - System.Diagnostics.Process.Start("http://www.minecraftforum.net/topic/1314800-/"); + } + catch (Exception ex) { InvokeUI(() => PrintSystem("Error: " + ex.Message, LineType.Error)); } + finally + { + isConnected = false; + InvokeUI(() => { + PrintSystem("Disconnected.", LineType.Error); + if (lblStatus != null) { lblStatus.Text = " ● " + TabTitle; lblStatus.ForeColor = Color.FromArgb(220, 80, 80); } + }); } } - /// - /// Open a link located in the console window - /// - - private void LinkClicked(object sender, LinkClickedEventArgs e) + public void Send(string text) { - try { System.Diagnostics.Process.Start(e.LinkText); } - catch (Exception ex) { MessageBox.Show("An error occured while opening the link :\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } + if (Client != null && !Client.Disconnected) + { + Client.SendText(text); + InvokeUI(() => PrintSystem("> " + text, LineType.System, Color.FromArgb(100, 180, 255))); + } + } + + public void CloseTab() + { + try + { + isConnected = false; + timerClock?.Stop(); + logWriter?.WriteLine("=== Session ended: " + DateTime.Now + " ==="); + logWriter?.Close(); + if (Client != null) { Client.Close(); Client = null; } + if (t_read != null && t_read.IsAlive) { t_read.Abort(); t_read = null; } + } + catch { } + } + + private void PrintSystem(string text, LineType type, Color? color = null) + { + string prefix = type == LineType.Error ? "[ERR] " : "[SYS] "; + Color c = color ?? (type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105)); + var entry = new LogLine { Raw = prefix + text, Display = prefix + text, Type = type, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(text, type); + InvokeUI(() => { + if (activeFilter == null || activeFilter == type) + { + boxOutput.SelectionColor = c; + boxOutput.AppendText(entry.Display + "\n"); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + } + }); + } + + private void PrintChat(string raw) + { + string plain = System.Text.RegularExpressions.Regex.Replace(raw, @"§.", ""); + var entry = new LogLine { Raw = raw, Display = plain, Type = LineType.Chat, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(plain, LineType.Chat); + InvokeUI(() => { + if (activeFilter == null || activeFilter == LineType.Chat) + { + boxOutput.SuspendLayout(); + RenderLine(entry); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + boxOutput.ResumeLayout(); + } + }); + } + + private void InvokeUI(Action a) { if (!boxOutput.IsDisposed) { if (boxOutput.InvokeRequired) try { boxOutput.Invoke(a); } catch { } else a(); } } + private Font GetFont(char c, Font f) => c == 'l' ? new Font(f, FontStyle.Bold) : f; + private Color GetColor(char c) + { + switch (c) + { + case '0': return Color.FromArgb(20, 20, 20); + case '1': return Color.FromArgb(85, 85, 255); + case '2': return Color.FromArgb(85, 200, 85); + case '3': return Color.FromArgb(85, 220, 220); + case '4': return Color.FromArgb(220, 85, 85); + case '5': return Color.FromArgb(200, 85, 200); + case '6': return Color.FromArgb(255, 180, 30); + case '7': return Color.Silver; + case '8': return Color.FromArgb(120, 120, 140); + case '9': return Color.FromArgb(100, 130, 255); + case 'a': return Color.FromArgb(85, 255, 85); + case 'b': return Color.FromArgb(85, 255, 255); + case 'c': return Color.FromArgb(255, 85, 85); + case 'd': return Color.FromArgb(255, 130, 255); + case 'e': return Color.FromArgb(255, 255, 85); + case 'f': return Color.White; + default: return Color.FromArgb(200, 200, 215); + } } } } diff --git a/MinecraftClientGUI/MinecraftClient.cs b/MinecraftClientGUI/MinecraftClient.cs index abf9b4ef..fb070136 100644 --- a/MinecraftClientGUI/MinecraftClient.cs +++ b/MinecraftClientGUI/MinecraftClient.cs @@ -24,33 +24,18 @@ namespace MinecraftClientGUI private Process Client; private Thread Reader; - /// - /// Start a client using command-line arguments - /// - /// Arguments to pass - public MinecraftClient(string[] args) { initClient("\"" + String.Join("\" \"", args) + "\" BasicIO"); } - /// - /// Start the client using username, password and server IP - /// - /// Username or email - /// Password for the given username - /// Server IP to join - public MinecraftClient(string username, string password, string serverip) { + // If the password is empty, pass an empty string to support Microsoft/Browser login + if (password == null) password = ""; initClient('"' + username + "\" \"" + password + "\" \"" + serverip + "\" BasicIO"); } - /// - /// Inner function for launching the external console application - /// - /// Arguments to pass - private void initClient(string arguments) { if (File.Exists(ExePath)) @@ -59,7 +44,10 @@ namespace MinecraftClientGUI Client.StartInfo.FileName = ExePath; Client.StartInfo.Arguments = arguments; Client.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - Client.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage); + + // FIX: Forcing UTF-8 fixes Polish characters and colors + Client.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; + Client.StartInfo.UseShellExecute = false; Client.StartInfo.RedirectStandardOutput = true; Client.StartInfo.RedirectStandardInput = true; @@ -69,66 +57,58 @@ namespace MinecraftClientGUI Reader = new Thread(new ThreadStart(t_reader)); Reader.Start(); } - else throw new FileNotFoundException("Cannot find Minecraft Client Executable!", ExePath); + else throw new FileNotFoundException("Nie znaleziono pliku MinecraftClient.exe!", ExePath); } - /// - /// Thread for reading output and app messages from the console - /// - private void t_reader() { while (true) { try { - string line = ""; - while (line.Trim() == "") + if (Client.HasExited) { disconnected = true; break; } + + string line = Client.StandardOutput.ReadLine(); + if (line != null) { - line = Client.StandardOutput.ReadLine() + Client.MainWindowTitle; - if (line.Length > 0) + if (line.Trim() != "") { - if (line == "Server was successfuly joined.") { disconnected = false; } - if (line == "You have left the server.") { disconnected = true; } - if (line[0] == (char)0x00) + if (line.Contains("Server was successfuly joined")) { disconnected = false; } + if (line.Contains("You have left the server")) { disconnected = true; } + + if (line.Length > 0 && line[0] == (char)0x00) { - //App message from the console string[] command = line.Substring(1).Split((char)0x00); - switch (command[0].ToLower()) + if (command[0].ToLower() == "autocomplete") { - case "autocomplete": - if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } - else tabAutoCompleteBuffer.AddLast(""); - break; + if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } + else tabAutoCompleteBuffer.AddLast(""); } } - else OutputBuffer.AddLast(line); + else + { + OutputBuffer.AddLast(line); + } } } + else { Thread.Sleep(10); } // Small pause to avoid overloading the CPU } - catch (NullReferenceException) { break; } + catch (Exception) { break; } } } - /// - /// Get the first queuing output line to print - /// - /// - public string ReadLine() { - while (OutputBuffer.Count < 1) { } + while (OutputBuffer.Count < 1) + { + if (disconnected) return ""; + Thread.Sleep(10); // Save CPU while waiting for data + } string line = OutputBuffer.First.Value; OutputBuffer.RemoveFirst(); return line; } - /// - /// Complete a playername or a command, usually by pressing the TAB key - /// - /// Text to complete - /// Returns an autocompletion for the provided text - public string tabAutoComplete(string text_behindcursor) { tabAutoCompleteBuffer.Clear(); @@ -136,7 +116,12 @@ namespace MinecraftClientGUI { text_behindcursor = text_behindcursor.Trim(); SendText((char)0x00 + "autocomplete" + (char)0x00 + text_behindcursor); - int maxwait = 30; while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) { Thread.Sleep(100); maxwait--; } + int maxwait = 30; + while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) + { + Thread.Sleep(100); + maxwait--; + } if (tabAutoCompleteBuffer.Count > 0) { string text_completed = tabAutoCompleteBuffer.First.Value; @@ -148,14 +133,9 @@ namespace MinecraftClientGUI else return ""; } - /// - /// Send a message or a command to the server - /// - /// Text to send - public void SendText(string text) { - if (text != null) + if (text != null && !Client.HasExited) { text = text.Replace("\t", ""); text = text.Replace("\r", ""); @@ -168,17 +148,16 @@ namespace MinecraftClientGUI } } - /// - /// Properly disconnect from the server and dispose the client - /// - public void Close() { - Client.StandardInput.WriteLine("/quit"); - if (Reader.IsAlive) { Reader.Abort(); } - if (!Client.WaitForExit(3000)) + if (!Client.HasExited) { - try { Client.Kill(); } catch { } + Client.StandardInput.WriteLine("/quit"); + if (Reader.IsAlive) { Reader.Abort(); } + if (!Client.WaitForExit(2000)) + { + try { Client.Kill(); } catch { } + } } } } diff --git a/MinecraftClientGUI/MinecraftClientGUI.csproj b/MinecraftClientGUI/MinecraftClientGUI.csproj index ce8642c4..1066164e 100644 --- a/MinecraftClientGUI/MinecraftClientGUI.csproj +++ b/MinecraftClientGUI/MinecraftClientGUI.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,9 +10,26 @@ Properties MinecraftClientGUI MinecraftClientGUI - v4.0 - Client + v4.8 + + 512 + false + C:\Users\Admin\Desktop\publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 3 + 1.0.0.%2a + false + true + true x86 @@ -23,6 +40,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -32,10 +50,23 @@ TRACE prompt 4 + false AppIcon.ico + + 4C76A63F11DB91E010AF3039521927BEB537A320 + + + MinecraftClientGUI_TemporaryKey.pfx + + + true + + + true + @@ -69,7 +100,10 @@ True Resources.resx + True + + SettingsSingleFileGenerator Settings.Designer.cs @@ -83,6 +117,18 @@ + + + False + Microsoft .NET Framework 4.8 %28x86 i x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + - \ No newline at end of file + diff --git a/MinecraftClientGUI/Program.cs b/MinecraftClientGUI/Program.cs index 45876950..dd080198 100644 --- a/MinecraftClientGUI/Program.cs +++ b/MinecraftClientGUI/Program.cs @@ -1,12 +1,13 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; using System.Windows.Forms; namespace MinecraftClientGUI { static class Program { + private const string ReleasesUrl = "https://github.com/MCCTeam/Minecraft-Console-Client/releases"; + /// /// Minecraft Console Client GUI by ORelio (c) 2013. /// Allows to use Minecraft Console Client in a more user friendly interface @@ -18,7 +19,26 @@ namespace MinecraftClientGUI { if (!System.IO.File.Exists(MinecraftClient.ExePath)) { - MessageBox.Show("File not found: " + MinecraftClient.ExePath, "Minecraft client not found", MessageBoxButtons.OK, MessageBoxIcon.Error); + DialogResult result = MessageBox.Show( + "File not found: " + MinecraftClient.ExePath + Environment.NewLine + Environment.NewLine + + "Place MinecraftClient.exe in the same folder as MinecraftClientGUI.exe." + Environment.NewLine + Environment.NewLine + + "Download MinecraftClient.exe from:" + Environment.NewLine + + ReleasesUrl + Environment.NewLine + Environment.NewLine + + "Open the releases page now?", + "Minecraft client not found", + MessageBoxButtons.YesNo, + MessageBoxIcon.Error); + + if (result == DialogResult.Yes) + { + try + { + Process.Start(new ProcessStartInfo(ReleasesUrl) { UseShellExecute = true }); + } + catch + { + } + } } else { diff --git a/MinecraftClientGUI/Properties/Resources.Designer.cs b/MinecraftClientGUI/Properties/Resources.Designer.cs index 9b33fcd6..a32a90af 100644 --- a/MinecraftClientGUI/Properties/Resources.Designer.cs +++ b/MinecraftClientGUI/Properties/Resources.Designer.cs @@ -1,17 +1,17 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18046 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace MinecraftClientGUI.Properties -{ - - +namespace MinecraftClientGUI.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace MinecraftClientGUI.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClientGUI.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/MinecraftClientGUI/Properties/Settings.Designer.cs b/MinecraftClientGUI/Properties/Settings.Designer.cs index 52f889e8..e41b8583 100644 --- a/MinecraftClientGUI/Properties/Settings.Designer.cs +++ b/MinecraftClientGUI/Properties/Settings.Designer.cs @@ -1,28 +1,24 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18046 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace MinecraftClientGUI.Properties -{ - - +namespace MinecraftClientGUI.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/MinecraftClientGUI/app.config b/MinecraftClientGUI/app.config new file mode 100644 index 00000000..3e0e37cf --- /dev/null +++ b/MinecraftClientGUI/app.config @@ -0,0 +1,3 @@ + + + diff --git a/MinecraftClientGUI/screenshot.png b/MinecraftClientGUI/screenshot.png new file mode 100644 index 00000000..6684c443 Binary files /dev/null and b/MinecraftClientGUI/screenshot.png differ diff --git a/README.md b/README.md index fb690dd9..af4569d4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,25 @@ ## Download -Get development builds from the [Releases section](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest) +Get the latest release from the [Releases section](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest). + +## Quick Install ⚡ + +Open a terminal in the folder where you want MCC and run: + +Linux / macOS: + +```bash +curl -fsSL https://mccteam.github.io/install.sh | sh +``` + +Windows (PowerShell): + +```powershell +iwr -useb https://mccteam.github.io/install.ps1 | iex +``` + +The script detects your architecture and downloads the right binary. For more options (including `wget` and manual downloads), see the [installation guide](https://mccteam.github.io/guide/installation.html). ## How to use 📚 @@ -57,7 +75,7 @@ If you'd like to contribute to Minecraft Console Client, great, just fork the re ## Translating Minecraft Console Client 🌍 -To improve translations for MCC, please visit: [Crowdin - Minecraft Console Client](https://crwd.in/minecraft-console-client). +To improve translations for MCC, please visit: [Crowdin - Minecraft Console Client](https://crowdin.com/project/minecraft-console-client). ## Building from the source 🏗️ diff --git a/crowdin.yml b/crowdin.yml index 5e80b8bb..9d6bbe3a 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,6 +1,6 @@ "project_id_env": "CROWDIN_PROJECT_ID" "api_token_env": "CROWDIN_PERSONAL_TOKEN" -"base_path": "/" +"base_path": "./" "preserve_hierarchy": true "base_url": "https://api.crowdin.com" @@ -32,5 +32,9 @@ { "source": "/docs/guide/*.md", "translation": "/docs/l10n/%osx_code%/guide/%original_file_name%" + }, + { + "source": "/docs/guide/websocket/*.md", + "translation": "/docs/l10n/%osx_code%/guide/websocket/%original_file_name%" } ] diff --git a/docs/.vuepress/client.ts b/docs/.vuepress/client.ts new file mode 100644 index 00000000..4cbef77c --- /dev/null +++ b/docs/.vuepress/client.ts @@ -0,0 +1,66 @@ +import { computed, defineComponent, h } from 'vue' +import { + defineClientConfig, + resolveRouteFullPath, + useRoute, + useRouter, +} from 'vuepress/client' + +const guardEvent = (event: MouseEvent): boolean => { + if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return false + if (event.defaultPrevented) return false + if (event.button !== undefined && event.button !== 0) return false + + if (event.currentTarget instanceof Element) { + const target = event.currentTarget.getAttribute('target') + if (target?.match(/\b_blank\b/i)) return false + } + + event.preventDefault() + return true +} + +const SafeRouteLink = defineComponent({ + name: 'RouteLink', + props: { + to: { + type: String, + required: true, + }, + active: Boolean, + activeClass: { + type: String, + default: 'route-link-active', + }, + }, + setup(props, { slots }) { + const router = useRouter() + const route = useRoute() + const path = computed(() => + props.to.startsWith('#') || props.to.startsWith('?') + ? props.to + : `${__VUEPRESS_BASE__}${resolveRouteFullPath(props.to, route.path).substring(1)}`, + ) + + return () => + h( + 'a', + { + class: ['route-link', { [props.activeClass]: props.active }], + href: path.value, + onClick: (event: MouseEvent) => { + if (guardEvent(event)) { + void router.push(props.to).catch(() => {}) + } + }, + }, + slots.default?.() ?? [], + ) + }, +}) + +export default defineClientConfig({ + enhance({ app }) { + app.component('RouteLink', SafeRouteLink) + }, +}) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 574c87d8..d03ff77a 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -1,23 +1,84 @@ import process from 'node:process' + import { viteBundler } from '@vuepress/bundler-vite' import { webpackBundler } from '@vuepress/bundler-webpack' -import { defineUserConfig } from '@vuepress/cli' +import { markdownChartPlugin } from '@vuepress/plugin-markdown-chart' +import { redirectPlugin } from '@vuepress/plugin-redirect' +import { searchPlugin } from '@vuepress/plugin-search' import { shikiPlugin } from '@vuepress/plugin-shiki' import { defaultTheme } from '@vuepress/theme-default' -import { getDirname, path } from '@vuepress/utils' -import { searchPlugin } from "@vuepress/plugin-search"; -import { redirectPlugin } from "vuepress-plugin-redirect"; +import { defineUserConfig } from 'vuepress' + +import type { Plugin } from 'vite' import { headConfig } from './configs/head.js' import { mainConfig, defaultThemeConfig } from './configs/locales_config.js' -const __dirname = getDirname(import.meta.url) const isProd = process.env.NODE_ENV === 'production' +function vueTemplateTolerantPlugin(): Plugin { + let compilerSfc: typeof import('@vue/compiler-sfc') | undefined + return { + name: 'vue-template-tolerant', + enforce: 'pre', + async transform(code, id) { + if (!id.endsWith('.html.vue') || !id.includes('/l10n/')) return + compilerSfc ??= await import('@vue/compiler-sfc') + const { errors } = compilerSfc.parse(code, { filename: id }) + if (errors.length === 0) return + + const lines = code.split('\n') + const mdPath = id + .replace(/\.vuepress\/\.temp\/pages\//, '') + .replace(/\.html\.vue$/, '.md') + + const yellow = '\x1b[33m' + const red = '\x1b[31m' + const dim = '\x1b[2m' + const cyan = '\x1b[36m' + const reset = '\x1b[0m' + + let output = `${yellow}[vue-template-tolerant]${reset} ${errors.length} error(s) in translation page (replaced with placeholder)\n` + output += ` ${dim}source:${reset} ${cyan}${mdPath}${reset}\n` + + for (const err of errors as any[]) { + const msg = err.message ?? String(err) + const loc = err.loc as { start: { line: number; column: number }; end: { line: number; column: number }; source?: string } | undefined + if (loc) { + output += `\n ${red}error${reset} ${msg}\n` + output += ` ${dim}at ${id}:${loc.start.line}:${loc.start.column}${reset}\n` + const startLine = Math.max(0, loc.start.line - 3) + const endLine = Math.min(lines.length, loc.start.line + 2) + for (let i = startLine; i < endLine; i++) { + const lineNum = String(i + 1).padStart(5) + const marker = i + 1 === loc.start.line ? `${red}>${reset}` : ' ' + const lineContent = lines[i].length > 200 ? lines[i].slice(0, 200) + '...' : lines[i] + output += ` ${marker} ${dim}${lineNum}${reset} | ${lineContent}\n` + if (i + 1 === loc.start.line) { + const col = loc.start.column + output += ` ${' '.repeat(5)} | ${' '.repeat(col)}${red}^${reset}\n` + } + } + } else { + output += `\n ${red}error${reset} ${msg}\n` + } + } + + console.warn(output) + return { + code: '', + map: null, + } + }, + } +} + export default defineUserConfig({ // set site base to default value base: '/', + pagePatterns: ['**/*.md', '!.vuepress', '!node_modules', '!superpowers'], + // extra tags in `` head: headConfig, @@ -25,12 +86,20 @@ export default defineUserConfig({ locales: mainConfig, // specify bundler via environment variable - bundler: process.env.DOCS_BUNDLER === 'webpack' ? webpackBundler() : viteBundler(), + bundler: + process.env.DOCS_BUNDLER === 'webpack' + ? webpackBundler() + : viteBundler({ + viteOptions: { + plugins: [vueTemplateTolerantPlugin()], + }, + }), // configure default theme theme: defaultTheme({ - logo: "/images/MCC_logo.png", - repo: "MCCTeam/Minecraft-Console-Client", + hostname: 'https://mccteam.github.io', + logo: '/images/MCC_logo.png', + repo: 'MCCTeam/Minecraft-Console-Client', docsBranch: 'master', docsDir: 'docs', @@ -42,55 +111,58 @@ export default defineUserConfig({ git: isProd, // use shiki plugin in production mode instead prismjs: !isProd, + seo: isProd + ? { + canonical: 'https://mccteam.github.io/', + } + : false, + sitemap: isProd + ? { + changefreq: 'weekly', + } + : false, }, }), - // configure markdown - markdown: { - importCode: { - handleImportPath: (str) => - str.replace(/^@vuepress/, path.resolve(__dirname, '../../ecosystem')), - }, - }, - // use plugins plugins: [ redirectPlugin({ - hostname: "https://mccteam.github.io", + hostname: 'https://mccteam.github.io', config: { - "/r/entity.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/EntityType.cs", - "/r/entity/index.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/EntityType.cs", + '/r/entity.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/EntityType.cs', + '/r/entity/index.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/EntityType.cs', - "/r/item.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs", - "/r/item/index.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs", + '/r/item.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs', + '/r/item/index.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs', - "/r/block.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs", - "/r/block/index.html": "https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs", + '/r/block.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs', + '/r/block/index.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs', - "/r/l-code.html": "https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239#discussion-4447461", - "/r/l-code/index.html": "https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239#discussion-4447461", + '/r/l-code.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239#discussion-4447461', + '/r/l-code/index.html': 'https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239#discussion-4447461', - "/r/dc-fmt.html": "https://www.writebots.com/discord-text-formatting/", - "/r/dc-fmt/index.html": "https://www.writebots.com/discord-text-formatting/", + '/r/dc-fmt.html': 'https://www.writebots.com/discord-text-formatting/', + '/r/dc-fmt/index.html': 'https://www.writebots.com/discord-text-formatting/', - "/r/tg-fmt.html": "https://sendpulse.com/blog/telegram-text-formatting", - "/r/tg-fmt/index.html": "https://sendpulse.com/blog/telegram-text-formatting", + '/r/tg-fmt.html': 'https://sendpulse.com/blog/telegram-text-formatting', + '/r/tg-fmt/index.html': 'https://sendpulse.com/blog/telegram-text-formatting', }, }), - // only enable shiki plugin in production mode - isProd ? shikiPlugin({ theme: 'dark-plus' }) : [], + ...(isProd ? [shikiPlugin({ theme: 'dark-plus' })] : []), searchPlugin({ - maxSuggestions: 15, - hotKeys: ["s", "/"], - locales: { - "/": { - placeholder: "Search", - }, + maxSuggestions: 15, + hotKeys: ['s', '/'], + locales: { + '/': { + placeholder: 'Search', }, + }, }), - 'vuepress-plugin-mermaidjs' + markdownChartPlugin({ + mermaid: true, + }), ], }) diff --git a/docs/.vuepress/configs/l10n_configs/af.ts b/docs/.vuepress/configs/l10n_configs/af.ts index b956e7f8..2b11ba60 100644 --- a/docs/.vuepress/configs/l10n_configs/af.ts +++ b/docs/.vuepress/configs/l10n_configs/af.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_af: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ar.ts b/docs/.vuepress/configs/l10n_configs/ar.ts index a91a460f..a08352b7 100644 --- a/docs/.vuepress/configs/l10n_configs/ar.ts +++ b/docs/.vuepress/configs/l10n_configs/ar.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ar: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ca.ts b/docs/.vuepress/configs/l10n_configs/ca.ts index 1ca77152..90ab4b2c 100644 --- a/docs/.vuepress/configs/l10n_configs/ca.ts +++ b/docs/.vuepress/configs/l10n_configs/ca.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ca: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/config_templete.ts b/docs/.vuepress/configs/l10n_configs/config_templete.ts index 3b0568ba..3d817b54 100644 --- a/docs/.vuepress/configs/l10n_configs/config_templete.ts +++ b/docs/.vuepress/configs/l10n_configs/config_templete.ts @@ -49,7 +49,7 @@ export const defaultThemeConfig_$LanguageCodeEscaped$: DefaultThemeLocaleData = { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/cs.ts b/docs/.vuepress/configs/l10n_configs/cs.ts index bb45f7de..14bb6893 100644 --- a/docs/.vuepress/configs/l10n_configs/cs.ts +++ b/docs/.vuepress/configs/l10n_configs/cs.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_cs: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/da.ts b/docs/.vuepress/configs/l10n_configs/da.ts index 4771c49e..185a1b5c 100644 --- a/docs/.vuepress/configs/l10n_configs/da.ts +++ b/docs/.vuepress/configs/l10n_configs/da.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_da: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/de.ts b/docs/.vuepress/configs/l10n_configs/de.ts index 509be3aa..c151bcb8 100644 --- a/docs/.vuepress/configs/l10n_configs/de.ts +++ b/docs/.vuepress/configs/l10n_configs/de.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_de: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/el.ts b/docs/.vuepress/configs/l10n_configs/el.ts index 95f1a97b..1885b04b 100644 --- a/docs/.vuepress/configs/l10n_configs/el.ts +++ b/docs/.vuepress/configs/l10n_configs/el.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_el: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/en.ts b/docs/.vuepress/configs/l10n_configs/en.ts index fa0c6533..d3ed87f6 100644 --- a/docs/.vuepress/configs/l10n_configs/en.ts +++ b/docs/.vuepress/configs/l10n_configs/en.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_en: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], @@ -62,6 +62,16 @@ export const defaultThemeConfig_en: DefaultThemeLocaleData = { "/guide/creating-text-script.md", "/guide/chat-bots.md", "/guide/creating-bots.md", + { + text: "WebSocket Bot", + collapsible: true, + children: [ + "/guide/websocket/README.md", + "/guide/websocket/Commands.md", + "/guide/websocket/Events.md", + ], + }, + "/guide/ai-assisted-development.md", "/guide/contibuting.md" ], diff --git a/docs/.vuepress/configs/l10n_configs/es.ts b/docs/.vuepress/configs/l10n_configs/es.ts index 81c88e3c..3bfe0adc 100644 --- a/docs/.vuepress/configs/l10n_configs/es.ts +++ b/docs/.vuepress/configs/l10n_configs/es.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_es: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/fi.ts b/docs/.vuepress/configs/l10n_configs/fi.ts index 6494cf75..55f63c65 100644 --- a/docs/.vuepress/configs/l10n_configs/fi.ts +++ b/docs/.vuepress/configs/l10n_configs/fi.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_fi: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/fr.ts b/docs/.vuepress/configs/l10n_configs/fr.ts index d73eab9e..daa39ecd 100644 --- a/docs/.vuepress/configs/l10n_configs/fr.ts +++ b/docs/.vuepress/configs/l10n_configs/fr.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_fr: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/he.ts b/docs/.vuepress/configs/l10n_configs/he.ts index 80253c88..1211da36 100644 --- a/docs/.vuepress/configs/l10n_configs/he.ts +++ b/docs/.vuepress/configs/l10n_configs/he.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_he: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/hu.ts b/docs/.vuepress/configs/l10n_configs/hu.ts index 43c6fde5..a5085801 100644 --- a/docs/.vuepress/configs/l10n_configs/hu.ts +++ b/docs/.vuepress/configs/l10n_configs/hu.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_hu: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/it.ts b/docs/.vuepress/configs/l10n_configs/it.ts index 42a03629..1fb0788e 100644 --- a/docs/.vuepress/configs/l10n_configs/it.ts +++ b/docs/.vuepress/configs/l10n_configs/it.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_it: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ja.ts b/docs/.vuepress/configs/l10n_configs/ja.ts index 5b5f4970..9e014260 100644 --- a/docs/.vuepress/configs/l10n_configs/ja.ts +++ b/docs/.vuepress/configs/l10n_configs/ja.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ja: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ko.ts b/docs/.vuepress/configs/l10n_configs/ko.ts index 2b2b0618..fcb27fb0 100644 --- a/docs/.vuepress/configs/l10n_configs/ko.ts +++ b/docs/.vuepress/configs/l10n_configs/ko.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ko: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/lv.ts b/docs/.vuepress/configs/l10n_configs/lv.ts index dbb74316..8448d1a8 100644 --- a/docs/.vuepress/configs/l10n_configs/lv.ts +++ b/docs/.vuepress/configs/l10n_configs/lv.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_lv: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/nl.ts b/docs/.vuepress/configs/l10n_configs/nl.ts index 992371c5..d1c0dc24 100644 --- a/docs/.vuepress/configs/l10n_configs/nl.ts +++ b/docs/.vuepress/configs/l10n_configs/nl.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_nl: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/no.ts b/docs/.vuepress/configs/l10n_configs/no.ts index d223eac1..2b92c00f 100644 --- a/docs/.vuepress/configs/l10n_configs/no.ts +++ b/docs/.vuepress/configs/l10n_configs/no.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_no: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/pl.ts b/docs/.vuepress/configs/l10n_configs/pl.ts index 70b06e98..573ac16b 100644 --- a/docs/.vuepress/configs/l10n_configs/pl.ts +++ b/docs/.vuepress/configs/l10n_configs/pl.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_pl: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/pt-BR.ts b/docs/.vuepress/configs/l10n_configs/pt-BR.ts index 3601fff0..3d423d44 100644 --- a/docs/.vuepress/configs/l10n_configs/pt-BR.ts +++ b/docs/.vuepress/configs/l10n_configs/pt-BR.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_pt_BR: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/pt.ts b/docs/.vuepress/configs/l10n_configs/pt.ts index d50297b8..09d9bc46 100644 --- a/docs/.vuepress/configs/l10n_configs/pt.ts +++ b/docs/.vuepress/configs/l10n_configs/pt.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_pt: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ro.ts b/docs/.vuepress/configs/l10n_configs/ro.ts index d8293b94..47d7ee1a 100644 --- a/docs/.vuepress/configs/l10n_configs/ro.ts +++ b/docs/.vuepress/configs/l10n_configs/ro.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ro: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/ru.ts b/docs/.vuepress/configs/l10n_configs/ru.ts index fc8d1148..e085b7f8 100644 --- a/docs/.vuepress/configs/l10n_configs/ru.ts +++ b/docs/.vuepress/configs/l10n_configs/ru.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_ru: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/sr-Cyrl.ts b/docs/.vuepress/configs/l10n_configs/sr-Cyrl.ts index cb86e0e0..0c4399e6 100644 --- a/docs/.vuepress/configs/l10n_configs/sr-Cyrl.ts +++ b/docs/.vuepress/configs/l10n_configs/sr-Cyrl.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_sr_Cyrl: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/sv.ts b/docs/.vuepress/configs/l10n_configs/sv.ts index b4c0a278..97764bb0 100644 --- a/docs/.vuepress/configs/l10n_configs/sv.ts +++ b/docs/.vuepress/configs/l10n_configs/sv.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_sv: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/tr.ts b/docs/.vuepress/configs/l10n_configs/tr.ts index fb9eb6ec..b1be3c5e 100644 --- a/docs/.vuepress/configs/l10n_configs/tr.ts +++ b/docs/.vuepress/configs/l10n_configs/tr.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_tr: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/uk.ts b/docs/.vuepress/configs/l10n_configs/uk.ts index a15a896e..c2fce2a7 100644 --- a/docs/.vuepress/configs/l10n_configs/uk.ts +++ b/docs/.vuepress/configs/l10n_configs/uk.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_uk: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/vi.ts b/docs/.vuepress/configs/l10n_configs/vi.ts index 50c554c7..8e34c396 100644 --- a/docs/.vuepress/configs/l10n_configs/vi.ts +++ b/docs/.vuepress/configs/l10n_configs/vi.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_vi: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/zh-Hans.ts b/docs/.vuepress/configs/l10n_configs/zh-Hans.ts index 146cf830..2de8559b 100644 --- a/docs/.vuepress/configs/l10n_configs/zh-Hans.ts +++ b/docs/.vuepress/configs/l10n_configs/zh-Hans.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_zh_Hans: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/configs/l10n_configs/zh-Hant.ts b/docs/.vuepress/configs/l10n_configs/zh-Hant.ts index d2577220..b13d6053 100644 --- a/docs/.vuepress/configs/l10n_configs/zh-Hant.ts +++ b/docs/.vuepress/configs/l10n_configs/zh-Hant.ts @@ -50,7 +50,7 @@ export const defaultThemeConfig_zh_Hant: DefaultThemeLocaleData = { { text: Translation.helpUsTranslate, - link: "https://crwd.in/minecraft-console-client", + link: "https://crowdin.com/project/minecraft-console-client", }, ], diff --git a/docs/.vuepress/public/install.ps1 b/docs/.vuepress/public/install.ps1 new file mode 100644 index 00000000..31f2e324 --- /dev/null +++ b/docs/.vuepress/public/install.ps1 @@ -0,0 +1,85 @@ +# Minecraft Console Client - Installer for Windows +# Downloads the latest MinecraftClient binary for your Windows architecture. +# Usage (PowerShell): iwr -useb https://mccteam.github.io/install.ps1 | iex + +$ErrorActionPreference = 'Stop' + +$REPO = "MCCTeam/Minecraft-Console-Client" +$OUTPUT = "MinecraftClient.exe" + +# --- Detect CPU architecture --- +$arch = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture +$archId = switch ($arch) { + 'X64' { 'x64' } + 'X86' { 'x86' } + 'Arm64' { 'arm64' } + default { + Write-Error "Unsupported CPU architecture: $arch" + exit 1 + } +} + +$suffix = "win-$archId" + +# --- Fetch latest release metadata from GitHub API --- +$apiUrl = "https://api.github.com/repos/$REPO/releases/latest" +Write-Host "Fetching latest release information..." +$release = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing + +# --- Locate the correct asset --- +$asset = $release.assets | Where-Object { $_.name -match "^MinecraftClient-.*-$([regex]::Escape($suffix))\.exe$" } | Select-Object -First 1 + +if (-not $asset) { + Write-Error "Could not find a release asset for '$suffix'." + exit 1 +} + +$downloadUrl = $asset.browser_download_url +$tag = $release.tag_name + +Write-Host "Downloading MinecraftClient $tag ($suffix)..." + +# Download with a built-in ASCII progress bar (no external tools required). +# HttpWebRequest streams the body on the main thread so we can update the +# progress bar inline without any Runspace or thread-safety concerns. +$outPath = Join-Path (Get-Location).Path $OUTPUT +$request = [System.Net.HttpWebRequest]::Create($downloadUrl) +$response = $request.GetResponse() +$totalBytes = $response.ContentLength + +$responseStream = $response.GetResponseStream() +$fileStream = [System.IO.File]::Create($outPath) +$buffer = New-Object byte[] 32768 +$totalRead = 0 + +try { + while ($true) { + $read = $responseStream.Read($buffer, 0, $buffer.Length) + if ($read -le 0) { break } + $fileStream.Write($buffer, 0, $read) + $totalRead += $read + if ($totalBytes -gt 0) { + $pct = [int]($totalRead * 100 / $totalBytes) + $filled = '=' * [int]($pct / 2) + $bar = $filled.PadRight(50) + $recv = [math]::Round($totalRead / 1MB, 1) + $total = [math]::Round($totalBytes / 1MB, 1) + # Use [Console]::Write with an explicit \r so the cursor returns to + # column 0 and overwrites the previous bar. Write-Host -NoNewline + # does not reliably reposition the cursor when the script is run + # via iex (pipe mode), producing multiple bars on one line. + $line = "`r[{0}] {1,3}% {2,6:N1} / {3,6:N1} MB" -f $bar, $pct, $recv, $total + [Console]::Write($line) + } + } +} finally { + $fileStream.Close() + $responseStream.Close() + $response.Close() +} + +[Console]::WriteLine() # end the progress line + +Write-Host "" +Write-Host "Downloaded: .\$OUTPUT" +Write-Host "Run with: .\$OUTPUT --help" diff --git a/docs/.vuepress/public/install.sh b/docs/.vuepress/public/install.sh new file mode 100644 index 00000000..d3e74d3d --- /dev/null +++ b/docs/.vuepress/public/install.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# Minecraft Console Client - Installer +# Downloads the latest MinecraftClient binary for your Linux or macOS platform. +# Usage: curl -fsSL https://mccteam.github.io/install.sh | sh +# or: wget -qO- https://mccteam.github.io/install.sh | sh + +set -e + +REPO="MCCTeam/Minecraft-Console-Client" +OUTPUT="MinecraftClient" + +# --- Detect OS --- +OS=$(uname -s) +case "$OS" in + Linux) PLATFORM="linux" ;; + Darwin) PLATFORM="osx" ;; + *) + echo "Error: Unsupported OS '$OS'. This script supports Linux and macOS." >&2 + exit 1 + ;; +esac + +# --- Detect CPU architecture --- +ARCH=$(uname -m) +case "$ARCH" in + x86_64|amd64) ARCH_ID="x64" ;; + aarch64|arm64) ARCH_ID="arm64" ;; + armv7l|armv8l|armhf) ARCH_ID="arm" ;; + arm*) ARCH_ID="arm" ;; + *) + echo "Error: Unsupported CPU architecture '$ARCH'." >&2 + exit 1 + ;; +esac + +# macOS does not have an arm (32-bit) build +if [ "$PLATFORM" = "osx" ] && [ "$ARCH_ID" = "arm" ]; then + echo "Error: 32-bit ARM is not supported on macOS." >&2 + exit 1 +fi + +SUFFIX="${PLATFORM}-${ARCH_ID}" + +# --- Download helpers: prefer curl, fall back to wget --- +_download_stdout() { + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$1" + elif command -v wget >/dev/null 2>&1; then + wget -qO- "$1" + else + echo "Error: Neither 'curl' nor 'wget' is available. Please install one and retry." >&2 + exit 1 + fi +} + +_download_file() { + if command -v curl >/dev/null 2>&1; then + curl -fL --progress-bar -o "$2" "$1" + elif command -v wget >/dev/null 2>&1; then + # --show-progress forces the progress bar even when stdout is not a TTY. + # Fall back silently to default output if the flag is not supported + # (older wget versions, e.g. BusyBox wget). + if wget --show-progress -O "$2" "$1" 2>/dev/null; then + return 0 + fi + wget -O "$2" "$1" + else + echo "Error: Neither 'curl' nor 'wget' is available. Please install one and retry." >&2 + exit 1 + fi +} + +# --- Fetch latest release metadata from GitHub API --- +API_URL="https://api.github.com/repos/${REPO}/releases/latest" +echo "Fetching latest release information..." +RELEASE_JSON=$(_download_stdout "$API_URL") + +# --- Parse asset download URL (no external tools required) --- +# The JSON key "browser_download_url" appears once per asset. +# We match the key followed by the URL, anchoring on the platform-arch suffix +# and the closing quote so that e.g. "linux-arm" does not match "linux-arm64". +# The ' *: *' pattern handles optional spaces around the colon (GitHub API adds spaces). +ASSET_URL=$(printf '%s' "$RELEASE_JSON" \ + | grep -o '"browser_download_url" *: *"[^"]*-'"${SUFFIX}"'"' \ + | grep -o 'https://[^"]*' \ + | head -1) + +if [ -z "$ASSET_URL" ]; then + echo "Error: Could not find a release asset for platform '${SUFFIX}'." >&2 + exit 1 +fi + +# --- Extract tag name for display --- +TAG=$(printf '%s' "$RELEASE_JSON" \ + | grep -o '"tag_name" *: *"[^"]*"' \ + | head -1 \ + | grep -o '"[^"]*"$' \ + | tr -d '"') + +echo "Downloading MinecraftClient ${TAG} (${SUFFIX})..." +_download_file "$ASSET_URL" "$OUTPUT" +chmod +x "$OUTPUT" + +echo "" +echo "Downloaded: ./${OUTPUT}" +echo "Run with: ./${OUTPUT} --help" diff --git a/docs/.vuepress/styles/index.scss b/docs/.vuepress/styles/index.scss new file mode 100644 index 00000000..3be2b2ca --- /dev/null +++ b/docs/.vuepress/styles/index.scss @@ -0,0 +1,163 @@ +.custom-container { + --custom-container-accent: var(--vp-c-accent-bg); + --custom-container-title: var(--vp-c-accent-text); + --custom-container-soft: var(--vp-c-accent-soft); + + margin: 0.75rem 0; + padding: 0.85rem 1rem; + border-inline-start: 0.35rem solid var(--custom-container-accent); + border-radius: 0.75rem; + background: var(--custom-container-soft); + color: inherit; + font-size: var(--hint-font-size, 0.92rem); + transition: + background var(--vp-t-color), + color var(--vp-t-color), + border-color var(--vp-t-color); +} + +.custom-container > .custom-container-title { + margin: 0 0 0.45rem; + color: var(--custom-container-title); + font-weight: 700; + line-height: 1.25; +} + +.custom-container > :last-child { + margin-bottom: 0; +} + +.custom-container > :not(.custom-container-title):first-child { + margin-top: 0; +} + +.custom-container a { + color: var(--vp-c-accent); +} + +.custom-container :not(pre) > code { + background: var(--vp-c-control); +} + +.custom-container.tip { + --custom-container-accent: var(--tip-c-accent, var(--vp-c-green-bg)); + --custom-container-title: var(--tip-c-text, var(--vp-c-green-text)); + --custom-container-soft: var(--tip-c-soft, var(--vp-c-green-soft)); +} + +.custom-container.info { + --custom-container-accent: var(--info-c-accent, var(--vp-c-blue-bg)); + --custom-container-title: var(--info-c-text, var(--vp-c-blue-text)); + --custom-container-soft: var(--info-c-soft, var(--vp-c-blue-soft)); +} + +.custom-container.note { + --custom-container-accent: var(--note-c-accent, var(--vp-c-grey-bg)); + --custom-container-title: var(--note-c-text, var(--vp-c-grey-text)); + --custom-container-soft: var(--note-c-soft, var(--vp-c-grey-soft)); +} + +.custom-container.important { + --custom-container-accent: var(--important-c-accent, var(--vp-c-purple-bg)); + --custom-container-title: var(--important-c-text, var(--vp-c-purple-text)); + --custom-container-soft: var(--important-c-soft, var(--vp-c-purple-soft)); +} + +.custom-container.warning { + --custom-container-accent: var(--warning-c-accent, var(--vp-c-yellow-bg)); + --custom-container-title: var(--warning-c-text, var(--vp-c-yellow-text)); + --custom-container-soft: var(--warning-c-soft, var(--vp-c-yellow-soft)); +} + +.custom-container.danger, +.custom-container.caution { + --custom-container-accent: var(--caution-c-accent, var(--vp-c-red-bg)); + --custom-container-title: var(--caution-c-text, var(--vp-c-red-text)); + --custom-container-soft: var(--caution-c-soft, var(--vp-c-red-soft)); +} + +@media (max-width: 719px) { + .custom-container { + margin-inline: -0.75rem; + border-radius: 0.5rem; + } +} + +/* Collapsible
sections */ +details { + margin: 1rem 0; + padding: 0; + border: 1px solid var(--vp-c-divider); + border-radius: 0.5rem; + transition: + background var(--vp-t-color), + border-color var(--vp-t-color); + + > summary { + display: flex; + align-items: center; + gap: 0.5em; + padding: 0.75rem 1.15rem; + font-weight: 600; + cursor: pointer; + user-select: none; + list-style: none; + border-radius: 0.5rem; + background: var(--vp-c-bg-soft); + transition: background var(--vp-t-color); + + &::before { + content: '▶'; + display: inline-block; + font-size: 0.55em; + color: var(--vp-c-text-2); + transition: transform 0.2s ease; + flex-shrink: 0; + } + + /* Hide the default marker in all browsers */ + &::-webkit-details-marker { + display: none; + } + + &::marker { + content: none; + } + + &:hover { + background: var(--vp-c-bg-mute); + } + + > code { + font-size: 0.95em; + font-weight: 700; + color: var(--vp-c-accent); + background: var(--vp-c-control); + padding: 0.15em 0.45em; + border-radius: 0.25rem; + } + } + + &[open] > summary { + border-bottom: 1px solid var(--vp-c-divider); + border-radius: 0.5rem 0.5rem 0 0; + margin-bottom: 0; + + &::before { + transform: rotate(90deg); + } + } + + &[open] > :not(summary) { + margin-left: 1.25rem; + margin-right: 1.25rem; + } + + &[open] > :nth-child(2) { + margin-top: 1rem; + } + + &[open] > :last-child { + margin-bottom: 1rem; + } +} diff --git a/docs/README.md b/docs/README.md index ce8fc3b4..36bc8c0e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,6 @@ features: - title: Automation details: Create bots to do automated tasks - title: Supported Versions - details: 1.4 - 1.20.4 + details: 1.4.6 - 26.1 footer: Made by MCC Team with ❤️ --- diff --git a/docs/guide/README.md b/docs/guide/README.md index 3c527887..a5fd572d 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -4,135 +4,176 @@ title: About & Features # Introduction -- [About](#about) -- [Quick Intro (YouTube Videos)](#quick-intro) -- [Features](#features) -- [Why Minecraft Console Client?](#why-minecraft-console-client) -- [Getting Help](#getting-help) -- [Submitting a bug report or an idea/feature-request](#bugs-ideas-feature-requests) -- [Important notes on some features](#notes-on-some-features) -- [Credits](#credits) -- [Disclaimer](#disclaimer) -- [License](#license) +- [Introduction](#introduction) + - [About](#about) + - [Features](#features) + - [Why Minecraft Console Client?](#why-minecraft-console-client) + - [Quick Intro](#quick-intro) + - [The list of the tutorials:](#the-list-of-the-tutorials) + - [Getting Help](#getting-help) + - [Before getting help](#before-getting-help) + - [Bugs, Ideas, Feature Requests](#bugs-ideas-feature-requests) + - [Before submitting](#before-submitting) + - [AI-Assisted Development](#ai-assisted-development) + - [Notes on some features](#notes-on-some-features) + - [Inventory, Terrain and Entity Handling](#inventory-terrain-and-entity-handling) + - [Path-Finding and Physics](#path-finding-and-physics) + - [Credits](#credits) + - [Disclaimer](#disclaimer) + - [License](#license) ## About -**Minecraft Console Client (MCC)** is a lightweight cross-platform open-source **Minecraft** TUI client for **Java edition** that allows you to connect to any Minecraft Java server, send commands and receive text messages in a fast and easy way without having to open the main Minecraft game. +**Minecraft Console Client (MCC)** is a lightweight, cross-platform, open-source **Minecraft** TUI client for **Java Edition**. It lets you connect to Minecraft Java servers, send commands, and receive text messages without launching the main game. -It also provides various automations that you can enable for administration and other purposes, as well as extensible C# API for creating Bots. +It also includes built-in automation for administration and utility work, plus an extensible C# API for creating bots and runtime scripts. It was originally made by [ORelio](https://github.com/ORelio) in 2012 on the [Minecraft Forum](http://www.minecraftforum.net/topic/1314800-/), now it's maintained by him and many other contributors from the community. ## Features -- Chat +- Chat - - Send and receive chat messages - - [Log chat history](chat-bots.md#chat-log) - - [Get alerted on certain keywords](chat-bots.md#alerts) - - [Auto Respond](chat-bots.md#auto-respond) + - Send and receive chat messages + - [Log chat history](chat-bots.md#chat-log) + - [Get alerted on certain keywords](chat-bots.md#alerts) + - [Auto Respond](chat-bots.md#auto-respond) -- [Anti AFK](chat-bots.md#anti-afk) -- [Auto Relog](chat-bots.md#auto-relog) -- [Script Scheduler](chat-bots.md#script-scheduler) -- [Remote Control](chat-bots.md#remote-control) -- [Auto Respond](chat-bots.md#auto-respond) -- [Auto Attack](chat-bots.md#auto-attack) -- [Auto Fishing](chat-bots.md#auto-fishing) -- [Auto Eat](chat-bots.md#auto-eat) -- [Auto Craft](chat-bots.md#auto-craft) -- [Mailer Bot](chat-bots.md#mailer) -- [Auto Drop](chat-bots.md#auto-drop) -- [Replay Mod](chat-bots.md#replay-mod) -- [API for creating Bots in C#](creating-bots.md#creating-chat-bots) -- [Docker Support](installation.md#using-docker) -- [Inventory Handling](usage.md#inventory) -- [Terrain Traversing](usage.md#move) -- Entity Handling +- Microsoft account authentication with 2FA support (OAuth 2.0 device code flow) -_NOTE: Some of mentioned features are disabled by default and you will have to turn them on in the configuration file and some may require additional configuration on your part for your specific usage._ +- [Anti AFK](chat-bots.md#anti-afk) + +- [Auto Relog](chat-bots.md#auto-relog) + +- [Script Scheduler](chat-bots.md#script-scheduler) + +- [Remote Control](chat-bots.md#remote-control) + +- [Auto Respond](chat-bots.md#auto-respond) + +- [Auto Attack](chat-bots.md#auto-attack) + +- [Auto Fishing](chat-bots.md#auto-fishing) + +- [Auto Eat](chat-bots.md#auto-eat) + +- [Auto Craft](chat-bots.md#auto-craft) + +- [Mailer Bot](chat-bots.md#mailer) + +- [Auto Drop](chat-bots.md#auto-drop) + +- [Replay Mod](chat-bots.md#replay-mod) + +- [API for creating Bots in C#](creating-bots.md#creating-chat-bots) + +- [Docker Support](installation.md#using-docker) + +- [Inventory Handling](usage.md#inventory) + +- [Book Support](usage.md#book) + +- [Terrain Traversing](usage.md#move) + +- Entity Handling + +_Note: Some of these features are disabled by default. You need to enable them in the configuration file, and some also require additional setup._ ## Why Minecraft Console Client? -- Easy to use -- Helpful community -- Open-Source -- Fast performance -- Easy Scripting/Automation -- Cross-Platform -- Docker Support -- 10 years of continuous development -- Active contributors -- Widely used +- Easy to use +- Helpful community +- Open-Source +- Fast performance +- Easy Scripting/Automation +- Cross-Platform +- Docker Support +- 10 years of continuous development +- Active contributors +- Widely used ## Quick Intro -Don't have time to read through the documentation, we got you, our community has made some simple introduction videos about the **Minecraft Console Client**. +If you do not want to read through the documentation right away, the community has made a few short introduction videos for **Minecraft Console Client**. ### The list of the tutorials: Installation: -- [Installation on Windows by Daenges](https://www.youtube.com/watch?v=BkCqOCa2uQw) -- [Installation on Windows + Auto AFK and More by Dexter113](https://www.youtube.com/watch?v=FxJ0KFIHDrY) +- [Installation on Windows by Daenges](https://www.youtube.com/watch?v=BkCqOCa2uQw) +- [Installation on Windows + Auto AFK and More by Dexter113](https://www.youtube.com/watch?v=FxJ0KFIHDrY) Using Commands, Scripts and other features: -- [Minecraft Console Client | Tutorial | Commands, Scripts, AppVars, Matches, Tasks and C# Scripts by Daenges](https://youtu.be/JbDpwwETEnU) -- [Console Client Tutorial - Scripting by Zixxter](https://www.youtube.com/watch?v=XE7rYBFJxn0) +- [Minecraft Console Client | Tutorial | Commands, Scripts, AppVars, Matches, Tasks and C# Scripts by Daenges](https://youtu.be/JbDpwwETEnU) +- [Console Client Tutorial - Scripting by Zixxter](https://www.youtube.com/watch?v=XE7rYBFJxn0) ## Getting Help -MCC has a community that is willing to help, we have a Discussions section in out Git Hub repository. +MCC has an active community, and the GitHub Discussions section is the best place to ask for help. Click [here](https://github.com/MCCTeam/Minecraft-Console-Client/discussions) to access it. ### Before getting help -- **Please use the search option here or in the discussion section and read the documentation so we avoid duplicate questions. Thank you!** -- **Please be kind and patient, respect others as they're the ones using their time to help you** +- **Please use the search option here or in the discussion section and read the documentation so we avoid duplicate questions. Thank you!** +- **Please be kind and patient, respect others as they're the ones using their time to help you** ## Bugs, Ideas, Feature Requests -Bug reporting, idea submitting or feature requesting are done in the [Issues](https://github.com/MCCTeam/Minecraft-Console-Client/issues) section of our [Github repository]([here](https://github.com/MCCTeam/Minecraft-Console-Client)). +Bug reports, ideas, and feature requests all go through the [Issues](https://github.com/MCCTeam/Minecraft-Console-Client/issues) section of our [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client). -Navigate to the Issues section, search for a bug, idea or a feature using the search option here in the documentation and in the `Issues` section on Git Hub before making your own. +Before opening a new issue, search both the documentation and the `Issues` section to avoid duplicates. -If you haven't found anything similar, go ahead and click on the `New issue` button, then choose what you want to do. +If you do not find anything similar, click `New issue` and choose the appropriate template. -If you're reporting a bug, please be descriptive as much as possible, try to explain how to re-create the bug, attack screenshots and logs, make sure that you have [`debugmessages`](configuration.me#debugmessages) set to `true` before sending a bug report or taking a screenshot. +If you are reporting a bug, be as specific as possible. Explain how to reproduce it, attach screenshots and logs, and make sure debug logging is enabled before collecting them. ### Before submitting -- **Please use the search option here or in the `Issues` section and read the documentation so we avoid duplicate questions/ideas/reports. Thank you!** -- **Please be kind, patient and respect others. Thank you!** +- **Please use the search option here or in the `Issues` section and read the documentation so we avoid duplicate questions/ideas/reports. Thank you!** +- **Please be kind, patient and respect others. Thank you!** + +## AI-Assisted Development + +If you want the repeatable agent workflow used by maintainers, start with [AI-Assisted Development](ai-assisted-development.md). ## Notes on some features ### Inventory, Terrain and Entity Handling -Inventory handling is currently not supported in versions: `1.4.6 - 1.9` (*The inventory handling code is in the place, but we're missing Item Palettes, on which we're working.*) +MCC currently supports Minecraft versions `1.4.6` through `26.1`. -Terrain handling is currently not supported in versions: `1.4.6 - 1.6` +Feature support still depends on protocol version: -Entity handling is currently not supported in versions: `1.4.6 - 1.7` +- Inventory handling is supported on `1.8+`. +- Terrain handling is supported on `1.7.2+`. +- Entity handling is supported on `1.8+`. -There features might not always be implemented in the latest version of the game, since they're often subjected to major changes by Mojang, and we need some time to figure out what has changed and to implement the required changes. +These features may lag behind brand-new Minecraft releases when Mojang changes the protocol or registries in a major way. If there was a major game update, and the MCC hasn't been updated to support these features, if you're a programmer, feel free to contribute to the project. ### Path-Finding and Physics -Currently the path-finding and physics have some limitations, those are: -- Path finding under slabs is not supported (currently being worked on, partialy complete but not avaliable in the main branch) -- Swimming is not supported yet -- Jumping is not supported yet -- Knockback is not supported yet +MCC now uses A\* path-finding together with a physics-based movement system for movement and collision handling. What is supported and works: -- Terrain navigation (path-finding with A* algorithm and walking) -- Climbing up and down the ladders and all types of vines -- Gravity + +- Terrain navigation with A\* path-finding and physics-driven movement +- Collision-aware movement using real block shapes +- Automatic jumping when the path requires moving up +- Step-up movement for slabs and similar low obstacles +- Sneaking and sprinting +- Movement physics in water and lava +- Climbing up and down ladders and all types of vines +- Gravity, friction, and block speed modifiers such as ice, soul sand, soul soil, and honey blocks + +Current limitations: + +- Path-finding is still block-based, so very complex terrain can still fail +- Automatic route planning still avoids underwater routes by default, so this is not a full swimming path-finder yet +- Knockback and other external velocity effects are not simulated yet ## Credits @@ -186,7 +227,7 @@ We remind you that **you may get banned** by your server for using this program. Minecraft Console Client is a totally free of charge, open source project. -The source code is available at [Github Repository](https://github.com/MCCTeam/Minecraft-Console-Client) +The source code is available at the [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client) Unless specifically stated, source code is from the MCC Team or Contributors, and available under CDDL-1.0. diff --git a/docs/guide/ai-assisted-development.md b/docs/guide/ai-assisted-development.md new file mode 100644 index 00000000..11686d64 --- /dev/null +++ b/docs/guide/ai-assisted-development.md @@ -0,0 +1,771 @@ +--- +title: AI-Assisted Development +--- + +# AI-Assisted Development + +This guide documents the MCC AI-assisted development workflow as a real working loop, not a patch generator running on guesses. The goal is to give the agent an environment it can drive on its own: build MCC, start a local server, send commands, inspect logs, and repeat. Once that loop is in place, iteration is faster and regressions are easier to catch. + +If you are looking for the broader contributor entry point first, start with [Contributing](contibuting.md) and then come back here for the agent workflow. + +The practical goal is a closed loop: + +```mermaid +flowchart LR + edit[Edit] --> build[Build] + build --> run[Run] + run --> test[Test] + test --> inspect[Inspect] + inspect --> repeat[Repeat] + repeat --> edit +``` + +

Warning

+ +If you develop on Windows, use WSL2. This workflow is built around Unix-style shells, `tmux`, `python3`, and shell helper functions. Do not try to run the full AI workflow from plain PowerShell or CMD. + +
+ +## Index + +- [What This Workflow Covers](#what-this-workflow-covers) +- [Setup](#setup) +- [How The Harness Works](#how-the-harness-works) +- [Repository Tools](#repository-tools) +- [Skills](#skills) +- [Standard Development Loop](#standard-development-loop) +- [Testing And Validation](#testing-and-validation) +- [Version Adaptation Notes](#version-adaptation-notes) +- [Example Workflows](#example-workflows) + +## What This Workflow Covers + +This is the workflow for: + +- local MCC development +- local offline server testing +- AI-assisted debugging +- bot authoring +- protocol and version adaptation work +- documentation work that should still follow the same disciplined loop + +It is built around two layers: + +- repo tools in `tools/`, which do the actual work +- AI skills in `.skills/`, which tell the agent when and how to use those tools + +For agent-driven local development, prefer the `mcc-*` wrappers after `source tools/mcc-env.sh`. They preserve session isolation, temp configs, and optional tmpfs build routing. Do not default to raw `dotnet build` or `dotnet run` for the normal MCC debug loop. + +## Setup + +You only do most of this once. + +
+Windows: install WSL2 first + +Open PowerShell as Administrator and run: + +```powershell +wsl --install +``` + +If WSL is already enabled and you specifically want Ubuntu, use: + +```powershell +wsl --install -d Ubuntu +``` + +If the install stalls at `0.0%`, use: + +```powershell +wsl --install --web-download -d Ubuntu +``` + +Restart if Windows asks for it, then open the Ubuntu shell and finish the Linux user setup there. + +From this point on, do MCC development inside WSL. That includes cloning the repo, building, running servers, and using AI agent tooling. + +Reference: [Microsoft WSL installation guide](https://learn.microsoft.com/windows/wsl/install) + +
+ +
+Linux and macOS: use Bash or Zsh + +Bash and Zsh both work with MCC's helper scripts. + +Check your current shell: + +```bash +echo $SHELL +``` + +Notes: + +- Bash is the normal baseline on Linux. +- Zsh is the default interactive shell on modern macOS. +- The helper script `tools/mcc-env.sh` can be sourced from either `~/.bashrc` or `~/.zshrc`. + +
+ +
+Install Git + +Ubuntu, Debian, and derivatives: + +```bash +sudo apt update +sudo apt install git +``` + +Arch Linux: + +```bash +sudo pacman -S git +``` + +macOS with Homebrew: + +```bash +brew install git +``` + +Verify: + +```bash +git --version +``` + +Reference: [Git downloads](https://git-scm.com/downloads) + +
+ +
+Install .NET SDK 10 + +MCC currently builds on `.NET 10`. You need the SDK, not just the runtime. + +Supported Ubuntu releases and Ubuntu-based distros with the correct feed enabled: + +```bash +sudo apt-get update && sudo apt-get install -y dotnet-sdk-10.0 +``` + +Debian 12: + +```bash +wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +sudo dpkg -i packages-microsoft-prod.deb +rm packages-microsoft-prod.deb +sudo apt-get update && sudo apt-get install -y dotnet-sdk-10.0 +``` + +Debian 13: + +```bash +wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +sudo dpkg -i packages-microsoft-prod.deb +rm packages-microsoft-prod.deb +sudo apt-get update && sudo apt-get install -y dotnet-sdk-10.0 +``` + +Arch Linux: + +```bash +sudo pacman -S dotnet-sdk +``` + +macOS with Homebrew: + +```bash +brew install --cask dotnet-sdk +``` + +Verify: + +```bash +dotnet --version +``` + +References: + +- [Install .NET on Ubuntu](https://learn.microsoft.com/dotnet/core/install/linux-ubuntu) +- [Install .NET on Debian](https://learn.microsoft.com/dotnet/core/install/linux-debian) +- [Homebrew `dotnet-sdk` cask](https://formulae.brew.sh/cask/dotnet-sdk) + +
+ +
+Install Java 21 + +The local server harness uses `java` directly, so Java 21 needs to be on your `PATH`. + +Ubuntu and Ubuntu-based distros: + +```bash +sudo apt update +sudo apt install openjdk-21-jdk +``` + +Debian: + +Package availability varies by Debian release. If `openjdk-21-jdk` is not available in your configured repositories, install a current JDK 21 build from your preferred vendor instead of forcing a stale package name. + +Arch Linux: + +```bash +sudo pacman -S jdk21-openjdk +``` + +macOS with Homebrew: + +```bash +brew install openjdk@21 +sudo ln -sfn "$(brew --prefix openjdk@21)/libexec/openjdk.jdk" /Library/Java/JavaVirtualMachines/openjdk-21.jdk +``` + +Homebrew marks `openjdk@21` as keg-only, which is why the symlink step matters. + +Verify: + +```bash +java -version +``` + +References: + +- [Ubuntu `openjdk-21-jdk` package](https://packages.ubuntu.com/noble/openjdk-21-jdk) +- [Arch `jdk21-openjdk` package](https://archlinux.org/packages/extra/x86_64/jdk21-openjdk/) +- [Homebrew `openjdk@21` formula](https://formulae.brew.sh/formula/openjdk@21) + +
+ +
+Install Python 3 + +Python 3 is required for the RCON helper and the version-adaptation tools. + +Ubuntu, Debian, and derivatives: + +```bash +sudo apt update +sudo apt install python3 +``` + +Arch Linux: + +```bash +sudo pacman -S python +``` + +macOS with Homebrew: + +```bash +brew install python@3.14 +``` + +Homebrew currently provides Python 3 through the `python@3.14` formula, and aliases it as `python` and `python3`. + +Verify: + +```bash +python3 --version +``` + +References: + +- [Ubuntu `python3` package](https://packages.ubuntu.com/noble/python/python3) +- [Arch `python` package](https://archlinux.org/packages/core/x86_64/python/) +- [Homebrew Python formula](https://formulae.brew.sh/formula/python@3.14) + +
+ +
+Install tmux + +The local Minecraft server runs in a `tmux` session so it can keep running while the agent builds and restarts MCC. + +Ubuntu, Debian, and derivatives: + +```bash +sudo apt update +sudo apt install tmux +``` + +Arch Linux: + +```bash +sudo pacman -S tmux +``` + +macOS with Homebrew: + +```bash +brew install tmux +``` + +Verify: + +```bash +tmux -V +``` + +
+ +
+Clone the repo and initialize submodules + +Clone with submodules in one step: + +```bash +git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive +``` + +If you already cloned it without submodules: + +```bash +git submodule update --init --recursive +``` + +
+ +
+Prepare a server version and decompiled source + +From the repo root, use the decompiler helper to download the official server jar and create the decompiled source tree: + +```bash +tools/decompile.sh --version 1.20.6 +``` + +That creates the paths used by the harness and the version-adaptation workflow: + +- `$MCC_SERVERS/1.20.6/server.jar` +- `MinecraftOfficial/1.20.6-decompiled/` + +If you are doing protocol work, this step is not optional. + +
+ +
+Load the MCC shell helpers in Bash + +Add this line to `~/.bashrc`: + +```bash +source "$HOME/Minecraft/Minecraft-Console-Client/tools/mcc-env.sh" +``` + +Reload the shell: + +```bash +source ~/.bashrc +``` + +This gives you the helper functions used by the workflow: + +- `mc-start` +- `mc-stop` +- `mc-cmd` +- `mc-log` +- `mc-rcon` +- `mc-reset-test-env` +- `mcc-build` +- `mcc-build-clean` +- `mcc-run` +- `mcc-tui` +- `mcc-cmd` +- `mcc-log-mcc` +- `mcc-state` +- `mcc-kill` +- `mcc-debug` +- `mcc-reload` + +
+ +
+Load the MCC shell helpers in Zsh + +Add this line to `~/.zshrc`: + +```bash +source "$HOME/Minecraft/Minecraft-Console-Client/tools/mcc-env.sh" +``` + +Reload the shell: + +```bash +source ~/.zshrc +``` + +If your clone lives somewhere else, update the path in the `source` line. + +
+ +
+Verify the environment + +Run these checks: + +```bash +git --version +dotnet --version +java -version +python3 --version +tmux -V +``` + +Then make sure the helper functions are loaded: + +```bash +type mc-start +type mcc-build +type mcc-debug +``` + +
+ +## How The Harness Works + +AI agents do not get a rich interactive terminal in the same way a human does. That is why this workflow uses a harness instead of relying on live keyboard input. + +The moving parts are: + +- a local Minecraft server running in `tmux` +- `mc-rcon` for server-side commands such as `/op`, `/give`, `/summon`, or gamerule setup +- MCC started with `MCC_FILE_INPUT=1` +- `FileInputBot`, which watches the session input file under `${TMPDIR:-/tmp}/mcc-debug//mcc_input.txt` +- logs from MCC and the local server, which the agent can inspect between runs + +The result is simple: the agent can change code, rebuild, start the app, inject commands, and read the result without waiting for a human to sit in the terminal. + +## Shared Server, Isolated MCC Sessions + +The harness separates shared server state from MCC client state. + +- `mc-*` commands operate on the shared local Minecraft server. +- `mcc-*` commands operate on one MCC client session. +- The default `session` is the current worktree name. +- The default username is derived from `session`, so two worktrees can join the same shared server without kicking each other. +- Session files live under `${TMPDIR:-/tmp}/mcc-debug//`. +- `MCC_SERVERS` stays the shared server-root override. + +Keep shared servers running by default. Do not stop or reset them unless the user explicitly asks for that, or you need to switch server versions. + +Two worktrees can share one local server like this: + +```bash +# worktree A +cd ~/Minecraft/Minecraft-Console-Client +source tools/mcc-env.sh +mc-start 1.21.11 +mcc-debug -v 1.21.11 --file-input + +# worktree B +cd ~/Minecraft/Minecraft-Console-Client-foo +source tools/mcc-env.sh +mcc-debug -v 1.21.11 --file-input + +# from each worktree, mcc-* targets that worktree's default session +mcc-state +``` + +If you need two MCC sessions from one worktree, pass `--session NAME` explicitly. + +### tmpfs build mode + +```bash +source tools/mcc-env.sh +export MCC_BUILD_MODE=tmpfs +mcc-build +mcc-build-clean +``` + +When `MCC_BUILD_MODE=tmpfs`, build output goes to `/dev/shm/mcc-build//` on Linux, or `${TMPDIR:-/tmp}/mcc-build//` when `/dev/shm` is unavailable. + +## Repository Tools + +These are the repo-level tools that make the workflow practical. + +| Path | Purpose | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `tools/mcc-env.sh` | Loads the shell helper functions used for the normal loop. | +| `tools/mcc-debug.sh` | Starts a session-scoped MCC debug run with generated config, log, pid, and tmux state. | +| `tools/start-server.sh` | Starts a local Minecraft server in a named `tmux` session with a FIFO for stdin. | +| `tools/mc-rcon.sh` | Sends RCON commands to the local server using `python3`. | +| `tools/decompile.sh` | Downloads `MinecraftDecompiler.jar` if needed, decompiles the requested Minecraft version, and fetches `server.jar` for server-side work. | +| `tools/diff_registries.py` | Compares registries between two Minecraft versions to show which palettes need updates. | +| `tools/gen_item_palette.py` | Generates item palette source from decompiled or reported registry data. | +| `tools/gen_block_palette.py` | Generates block palette source from authoritative block reports. | +| `tools/gen_entity_palette.py` | Generates entity palette source from registry reports. | +| `tools/gen_entity_metadata_palette.py` | Generates entity metadata palette source from serializer registration order. | +| `tools/gen_command_argument_registry.py` | Helps update modern declare-commands registry order. | +| `tools/gen_block_shapes.py` | Downloads and compacts collision shape data for physics support. | + +There is one more piece worth calling out: + +- `MinecraftClient/ChatBots/FileInputBot.cs` is what makes file-driven command injection possible. +- It is loaded when `MCC_FILE_INPUT=1` is set. +- `mcc-debug --file-input` and `mcc-run` already set that flag for you. + +## Skills + +The tools above do the work. The skills in `.skills/` tell the AI when to use them and what good output looks like. + +| Skill | What it is for | Notes | +| ------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `mcc-dev-workflow` | The default build, run, debug, and local server loop. | This is the skill to use for most day-to-day MCC debugging. It assumes WSL, `tmux`, Java, and the local harness. | +| `mcc-integration-testing` | Repeatable end-to-end testing against a local offline server. | This skill bundles its own scripts under `.skills/mcc-integration-testing/scripts/`. Those are skill resources, not top-level repo scripts. | +| `mcc-version-adaptation` | Protocol and palette updates for new Minecraft versions. | Use this when routing, registries, metadata, palettes, or structured components change. | +| `mcc-chatbot-authoring` | Authoring or repairing built-in bots and standalone `/script` bots. | This skill bundles references and templates under `.skills/mcc-chatbot-authoring/`. It defaults to standalone `/script` bots unless built-in wiring is requested. | +| `csharp-best-practices` | C# 14 / .NET 10 coding guidance for this repo. | Use it whenever the change touches MCC runtime code. | +| `humanizer` | Documentation and prose cleanup. | Use it for docs, guides, release notes, and anything that starts sounding machine-written. | +| `mcc-prompt-engineer` | Generating structured prompts for MCC development tasks. | Manually triggered. Interviews the user, explores the codebase, and produces a self-contained prompt with reasoning framework, skill references, and sub-agent directives. | +| `skill-creator` | Creating or evolving skills themselves. | This is for improving the AI workflow, not for normal MCC feature work. | + +The important distinction is this: + +- repo tools are executable scripts and source files +- skills are instructions, references, templates, and workflow constraints for the AI + +Some skills also carry their own bundled resources: + +- `mcc-integration-testing` bundles scripts and a command matrix reference +- `mcc-chatbot-authoring` bundles references and bot templates +- `skill-creator` bundles scripts, eval tooling, and reviewer assets + +## Standard Development Loop + +This is the core loop you should expect an agent to follow. + +### 1. Start the local server and resolve the MCC identity + +```bash +source tools/mcc-env.sh +SESSION="smoke-a" +USERNAME="$(_mcc_resolve_username "$SESSION")" +mc-start 1.20.6 +``` + +Check the recent server output: + +```bash +mc-log 1.20.6 +``` + +### 2. Build MCC + +```bash +mcc-build +``` + +### 3. Run MCC with file input enabled + +```bash +mcc-debug -v 1.20.6 --file-input --session "$SESSION" --no-build +``` + +### 4. Set up server state through RCON + +Examples: + +```bash +mc-rcon "op $USERNAME" +mc-rcon "gamerule sendCommandFeedback true" +mc-rcon "give $USERNAME diamond_sword 1" +mc-rcon "summon minecraft:armor_stand ~ ~ ~" +``` + +### 5. Drive MCC through the session input file + +Examples: + +```bash +mcc-cmd --session "$SESSION" "inventory player list" +mcc-cmd --session "$SESSION" "entity" +mcc-cmd --session "$SESSION" "/gamemode creative" +``` + +Behavior: + +- lines starting with `/` are sent as server commands or chat +- lines without `/` are treated as MCC internal commands first +- if a line is not an MCC internal command, it falls back to normal chat sending + +### 6. Inspect the result + +Read the MCC output and the server log, decide what changed, and either keep iterating or stop. + +### 7. Rebuild and restart fast + +```bash +mcc-reload +``` + +That is the usual tight loop for regression work. + +## Testing And Validation + +There are two main testing styles in this workflow. + +### Manual validation + +This is enough for smaller changes: + +- join the local server +- grant operator privileges with `mc-rcon` +- run internal MCC commands through `mcc-cmd` +- trigger gameplay or server state changes through `mc-rcon` +- inspect logs for parsing errors, disconnects, or wrong output + +Typical manual checks: + +- inventory listing and creative item injection +- entity tracking after `summon` +- terrain and chunk handling after join +- chat and command flow +- explosion, particle, and sound events + +### Scripted full-spectrum testing + +The `mcc-integration-testing` skill goes further. It bundles its own scripts under `.skills/mcc-integration-testing/scripts/` and expects the shell helpers from `~/.zshrc`. + +Treat those scripts as skill-owned resources. Read the skill before running them directly, and do not assume they behave like top-level repo tools. + +That skill is designed for repeatable offline validation of: + +- chat +- slash commands +- MCC internal commands +- inventory handling +- entity handling +- particles and sounds +- TNT and explosion handling + +Server settings that matter for AI-driven offline testing: + +- `eula=true` +- `online-mode=false` +- `enforce-secure-profile=false` +- `enable-rcon=true` +- `rcon.password=test123` + +If those are wrong, the loop gets noisy fast. + +## Version Adaptation Notes + +Version work needs a stricter process than normal bug fixing. + +The important rule is simple: + +- for newer versions, especially `1.21.9+`, use server data reports as the authority for items and blocks +- use decompiled source for implementation details, field order, codecs, and serializer logic +- do not stop at a palette diff; finish with a build and a live server test + +The usual order is: + +1. `tools/decompile.sh --version ` +2. generate server reports from `server.jar` +3. run `tools/diff_registries.py` +4. regenerate the palettes that actually changed +5. update version routing and packet handling +6. build MCC +7. test against the real target version + +That is exactly the sort of work `mcc-version-adaptation` is meant to guide. + +## Example Workflows + +These are four common patterns this guide is meant to support. + +### Example 1: Debug a runtime regression + +Use skills: + +- `mcc-dev-workflow` +- `csharp-best-practices` + +Typical loop: + +```bash +source tools/mcc-env.sh +SESSION="smoke-a" +USERNAME="$(_mcc_resolve_username "$SESSION")" +mc-start 1.20.6 +mcc-build +mcc-debug -v 1.20.6 --file-input --session "$SESSION" --no-build +mc-rcon "op $USERNAME" +mcc-cmd --session "$SESSION" "inventory player list" +mcc-cmd --session "$SESSION" "entity" +``` + +Then inspect the MCC output, patch the code, and use: + +```bash +mcc-reload +``` + +### Example 2: Build or repair a bot + +Use skills: + +- `mcc-chatbot-authoring` +- `csharp-best-practices` +- `mcc-dev-workflow` + +Typical flow: + +1. Decide whether this should be a standalone `/script` bot or a built-in bot. +2. Use the authoring skill's references and templates. +3. Build MCC. + Use `mcc-build` instead of raw `dotnet build` so worktree-local temp build output still applies. +4. Start a local server and join it. +5. Test the bot behavior through live commands, chat, or event-driven actions. +6. Make sure cleanup paths such as `OnUnload()` are correct. + +For standalone script work, the skill defaults to `/script` unless built-in repo wiring is explicitly needed. + +### Example 3: Adapt MCC to a new Minecraft version + +Use skills: + +- `mcc-version-adaptation` +- `mcc-dev-workflow` +- `mcc-integration-testing` + +Typical flow: + +```bash +tools/decompile.sh --version 26.1 +``` + +Generate server reports: + +```bash +cd /tmp +java -DbundlerMainClass=net.minecraft.data.Main \ + -jar "$MCC_SERVERS/26.1/server.jar" \ + --reports --output /tmp/mc_reports +``` + +Run the registry diff: + +```bash +python3 tools/diff_registries.py 1.21.10 26.1 --registry /tmp/mc_reports/reports/registries.json +``` + +Then regenerate the palettes that changed, update routing, build MCC, start a local server for the target version, and run live validation before calling the work done. + +### Example 4: Write or update documentation for the workflow itself + +Use skills: + +- `humanizer` +- `skill-creator`, if you are changing the skills rather than just the docs + +Typical flow: + +1. Re-read the relevant skill files and repo tools. +2. Update the guide so the written process matches the real process. +3. Keep the instructions concrete enough that another contributor can follow them without guessing. +4. If the workflow itself changed, update the relevant skill too instead of leaving the docs ahead of the automation. diff --git a/docs/guide/chat-bots.md b/docs/guide/chat-bots.md index 90fbee06..23658d54 100644 --- a/docs/guide/chat-bots.md +++ b/docs/guide/chat-bots.md @@ -1,27 +1,21 @@ --- title: Chat Bots redirectFrom: - - "/g/bots/index.html" - - "/g/bots.html" + - /g/bots/index.html + - /g/bots.html --- # Chat Bots -- [About](#about) -- [List of built-in Chat Bots](#list-of-built-in-chat-bots) -- [Creating your own](creating-bots.md) +- [About](#about) +- [List of built-in Chat Bots](#list-of-built-in-chat-bots) +- [Creating your own](creating-bots.md) ## About **Minecraft Console Client** has a number of default built in Chat Bots (Scripts/Plugins) which allow for various types of automation. -

Warning

- -**Recently we have changed the configuration format from INI to TOML, this part of the documentation has only been partially updated, it's work in progress, for the time being please refer to the `MinecraftClient.ini` for setting names, the descriptions and options should be up to date in most cases, but not guaranteed.** - -
- -

Tip

+

Note

**Settings refer to settings in the [configuration file](configuration.md)** @@ -29,1513 +23,1961 @@ redirectFrom: ## List of built-in Chat Bots -- [Alerts](#alerts) -- [Anti AFK](#anti-afk) -- [Auto Attack](#auto-attack) -- [Auto Craft](#auto-craft) -- [Auto Dig](#auto-dig) -- [Auto Drop](#auto-drop) -- [Auto Eat](#auto-eat) -- [Auto Fishing](#auto-fishing) -- [Auto Relog](#auto-relog) -- [Auto Respond](#auto-respond) -- [Chat Log](#chat-log) -- [Discord Bridge](#discord-bridge) -- [Farmer](#farmer) -- [Follow Player](#follow-player) -- [Hangman](#hangman) -- [Mailer](#mailer) -- [Map](#map) -- [PlayerList Logger](#playerlist-logger) -- [Remote Control](#remote-control) -- [Replay Mod](#replay-mod) -- [Script Scheduler](#script-scheduler) -- [Telegram Bridge](#telegram-bridge) -- [Items Collector](#items-collector) -- [WebSocket](#websocket-chat-bot) +- [Chat Bots](#chat-bots) + - [About](#about) + - [List of built-in Chat Bots](#list-of-built-in-chat-bots) + - [Alerts](#alerts) + - [Anti AFK](#anti-afk) + - [Auto Attack](#auto-attack) + - [Auto Craft](#auto-craft) + - [Auto Dig](#auto-dig) + - [Auto Drop](#auto-drop) + - [Auto Eat](#auto-eat) + - [Auto Fishing](#auto-fishing) + - [Auto Relog](#auto-relog) + - [Auto Respond](#auto-respond) + - [Chat Log](#chat-log) + - [Discord Bridge](#discord-bridge) + - [Discord RPC](#discord-rpc) + - [Farmer](#farmer) + - [Follow player](#follow-player) + - [Hangman](#hangman) + - [Mailer](#mailer) + - [MCP Server](#mcp-server) + - [Map](#map) + - [PlayerList Logger](#playerlist-logger) + - [Remote Control](#remote-control) + - [Replay Capture](#replay-capture) + - [Script Scheduler](#script-scheduler) + - [Telegram Bridge](#telegram-bridge) + - [Items Collector](#items-collector) ## Alerts -- **Description:** +- **Description:** - Get alerted when specified words are detected in the chat + Get alerted when specified words are detected in the chat - Useful for moderating your server or detecting when someone is talking to you. + Useful for moderating your server or detecting when someone is talking to you. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.Alerts`** + **Section:** **`ChatBot.Alerts`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Alerts Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Alerts Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Beep_Enabled` + - **Default:** `false` -

Tip

+ #### `Beep_Enabled` - **This might not work depending on your system or a console (terminal emulator).** +

Note

-
+ **This might not work depending on your system or a console (terminal emulator).** - - **Description:** +
- This setting specifies if you want to hear a beep when you get an alert. + - **Description:** - - **Type:** `boolean` + This setting specifies if you want to hear a beep when you get an alert. - - **Default:** `true` + - **Type:** `boolean` - #### `Trigger_By_Words` + - **Default:** `true` - - **Description:** + #### `Trigger_By_Words` - Triggers an alert after receiving a specified keyword. + - **Description:** - - **Available values:** `true` and `false`. + Triggers an alert after receiving a specified keyword. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Trigger_By_Rain` + - **Default:** `false` - - **Description:** + #### `Trigger_By_Rain` - Trigger alerts when it rains and when it stops. + - **Description:** - - **Available values:** `true` and `false`. + Trigger alerts when it rains and when it stops. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Trigger_By_Thunderstorm` + - **Default:** `false` - - **Description:** + #### `Trigger_By_Thunderstorm` - Triggers alerts at the beginning and end of thunderstorms. + - **Description:** - - **Available values:** `true` and `false`. + Triggers alerts at the beginning and end of thunderstorms. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Log_To_File` + - **Default:** `false` - - **Description:** + #### `Log_To_File` - Should the Alerts Chat Bot log alerts into a file. + - **Description:** - - **Available values:** `true` and `false`. + Should the Alerts Chat Bot log alerts into a file. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Log_File` + - **Default:** `false` - - **Description:** + #### `Log_File` - A path to the file where alerts will be logged if `Log_To_File` is set to `true`. + - **Description:** - - **Type:** `string` + A path to the file where alerts will be logged if `Log_To_File` is set to `true`. - - **Default:** `"alerts-log.txt"` + - **Type:** `string` - #### `Matches` + - **Default:** `"alerts-log.txt"` - - **Description:** + #### `Matches` - List of words/strings to alert you on. + - **Description:** - - **Type:** `array of strings` + List of words/strings to alert you on. - - **Example**: + - **Type:** `array of strings` - ```toml - Matches = [ "Yourname", " whispers ", "-> me", "admin", ".com", ] - ``` + - **Example**: - #### `Excludes` + ```toml + Matches = [ "Yourname", " whispers ", "-> me", "admin", ".com", ] + ``` - - **Description:** + #### `Excludes` - List of words/strings to NOT alert you on. + - **Description:** - - **Type:** `array of strings` + List of words/strings to NOT alert you on. - - **Example**: + - **Type:** `array of strings` - ```toml - Excludes = [ "myserver.com", "Yourname>:", "Player Yourname", "Yourname joined", "Yourname left", "[Lockette] (Admin)", " Yourname:", "Yourname is", ] - ``` + - **Example**: + + ```toml + Excludes = [ "myserver.com", "Yourname>:", "Player Yourname", "Yourname joined", "Yourname left", "[Lockette] (Admin)", " Yourname:", "Yourname is", ] + ``` + +
## Anti AFK -- **Description:** +- **Description:** - Send a command and sneak on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection. + Send a command and sneak on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AntiAFK`** + **Section:** **`ChatBot.AntiAFK`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Anti AFK Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Anti AFK Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Delay` + - **Default:** `false` - - **Description:** + #### `Delay` - The time interval for execution in seconds. + - **Description:** - If the `min` and `max` are the same, the time interval will be consistent. However if they are not the same, the plugin will choose a random number between `min` and `max`, this is useful if you want to have a random interval to trick anti afk plugins. + The time interval for execution in seconds. - - **Format:** `{ min = , max = }` + If the `min` and `max` are the same, the time interval will be consistent. However if they are not the same, the plugin will choose a random number between `min` and `max`, this is useful if you want to have a random interval to trick anti afk plugins. - - **Type:** `inline table with min and max fields which have type of double` + - **Format:** `{ min = , max = }` - - **Default:** `{ min = 60.0, max = 60.0 }` + - **Type:** `inline table with min and max fields which have type of double` - #### `Command` + - **Default:** `{ min = 60.0, max = 60.0 }` - - **Description:** + #### `Command` - Command to be sent. + - **Description:** - - **Type:** `string` + Command to be sent. - - **Default:** `/ping` + - **Type:** `string` - #### `Use_Sneak` + - **Default:** `/ping` - - **Description:** + #### `Use_Sneak` - Sometimes you can trick plugins with sneaking or command might not be enough, enable it if you need it. + - **Description:** - - **Type:** `boolean` + Sometimes you can trick plugins with sneaking or command might not be enough, enable it if you need it. - - **Default:** `false` + - **Type:** `boolean` - #### `Use_Terrain_Handling` + - **Default:** `false` -

Tip

+ #### `Use_Terrain_Handling` - **You need to enable [Terrain Handling](configuration.md#terrainandmovements) in the settings and it's recommended to put the bot into an enclosure not to wander off. (Recommended size 5x5x5)** +

Note

-
+ **You need to enable [Terrain Handling](configuration.md#terrainandmovements) in the settings and it's recommended to put the bot into an enclosure not to wander off. (Recommended size 5x5x5)** - - **Description:** +
- Should the bot use [Terrain Handling](configuration.md#terrainandmovements) instead of the command method. + - **Description:** - This will enable your bot to randomly move about, thus a better anti afk effect. + Should the bot use [Terrain Handling](configuration.md#terrainandmovements) instead of the command method. - - **Available values:** `true` and `false`. + This will enable your bot to randomly move about, thus a better anti afk effect. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Walk_Range` + - **Default:** `false` - - **Description:** + #### `Walk_Range` - The range which bot will use to walk around (-X to +X and -Z to +Z, Y is not used). + - **Description:** - The bigger the slower the bot might be at calculating the path, recommended 2-5. + The range which bot will use to walk around (-X to +X and -Z to +Z, Y is not used). - - **Default:** `5` + The bigger the slower the bot might be at calculating the path, recommended 2-5. - #### `Walk_Retries` + - **Default:** `5` -

Tip

+ #### `Walk_Retries` - **This happens on each trigger of the task, so it does not permanently switch to alternative method.** +

Note

-
+ **This happens on each trigger of the task, so it does not permanently switch to alternative method.** - - **Description:** +
- This is the number of times the bot will try to pathfind, if he can't find a valid path for 20 times, he will use the command method. + - **Description:** - - **Default:** `20` + This is the number of times the bot will try to pathfind, if he can't find a valid path for 20 times, he will use the command method. + + - **Default:** `20` + +
## Auto Attack -

Tip

+

Note

**You need to have [inventoryhandling](configuration.md#inventoryhandling) and [entityhandling](configuration.md#entityhandling) enabled in order for this bot to work.**
-- **Description:** +- **Description:** - Automatically attacks mobs around you, you can configure it to attack both hostile and passive mobs and only certain mobs or all mobs. + Automatically attacks mobs around you, you can configure it to attack both hostile and passive mobs and only certain mobs or all mobs. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoAttack`** + **Section:** **`ChatBot.AutoAttack`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Attack Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Attack Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Mode` + - **Default:** `false` - - **Description:** + #### `Mode` - Available values: + - **Description:** - - `single` + Available values: - Target one mob per attack. + - `single` - - `multi` + Target one mob per attack. - Target all mobs in range per attack. + - `multi` - - **Type:** `string` + Target all mobs in range per attack. - - **Default:** `single` + - **Type:** `string` - #### `Priority` + - **Default:** `single` - - **Description:** + #### `Priority` - Available values: + - **Description:** - - `health` (prioritize targeting mobs with lower health) - - `distance` (prioritize targeting mobs closer to you) + Available values: - - **Type:** `string` + - `health` (prioritize targeting mobs with lower health) + - `distance` (prioritize targeting mobs closer to you) - - **Default:** `distance` + - **Type:** `string` - #### `Cooldown_Time` + - **Default:** `distance` - - **Description:** + #### `Cooldown_Time` - How long to wait between each attack in seconds. + - **Description:** - To enable it, set `Custom` (boolean) to `true` and change `value` (double) to your preferred value (eg. `1.5`). + Controls the delay between attacks. By default, MCC calculates this based on server TPS. Set `Custom` to `true` to specify your own values: - By the default, this is disabled and the MCC calculates it based on the server TPS. + - `Min` — minimum cooldown in seconds + - `Max` — maximum cooldown in seconds + - `RandomMode` — if enabled, picks a random cooldown between `Min` and `Max` for each attack - - **Format:** `Cooldown_Time = { Custom = , value = }` + - **Format:** `Cooldown_Time = { Custom = , RandomMode = , Min = , Max = }` - - **Type:** `inline table` + - **Type:** `inline table` - - **Example:** `Cooldown_Time = { Custom = true, value = 1.5 }` + - **Example:** `Cooldown_Time = { Custom = true, RandomMode = true, Min = 1.0, Max = 2.0 }` - - **Default:** `{ Custom = false, value = 1.0 }` + - **Default:** `{ Custom = false, RandomMode = false, Min = 1.5, Max = 2.5 }` - #### `Interaction` + #### `Interaction` - - **Description:** + - **Description:** - Available values: + Available values: - - `Attack` + - `Attack` - Just attack a mob. (Default) + Just attack a mob. (Default) - - `Interact` + - `Interact` - Just interact with a mob. + Just interact with a mob. - - `InteractAt` + - `InteractAt` - Interact with and attack a mob. + Interact with and attack a mob. - - **Type:** `string` + - **Type:** `string` - - **Default:** `Attack` + - **Default:** `Attack` - #### `Attack_Hostile` + #### `Attack_Hostile` - - **Description:** + - **Description:** - This setting specifies if the Auto Attack Chat Bot should attack hostile mobs. + This setting specifies if the Auto Attack Chat Bot should attack hostile mobs. - - **Available values:** `true` and `false`. + - **Available values:** `true` and `false`. - - **Type:** `boolean` + - **Type:** `boolean` - - **Default:** `true` + - **Default:** `true` - #### `Attack_Passive` + #### `Attack_Passive` - - **Description:** + - **Description:** - This setting specifies if the Auto Attack Chat Bot should attack passive mobs. + This setting specifies if the Auto Attack Chat Bot should attack passive mobs. - - **Available values:** `true` and `false`. + - **Available values:** `true` and `false`. - - **Type:** `boolean` + - **Type:** `boolean` - - **Default:** `false` + - **Default:** `false` - #### `List_Mode` + #### `List_Mode` - - **Description:** + - **Description:** - This setting specifies which mode of the list should Auto Attack Chat Bot use for `Entites_List` setting. + This setting specifies which mode of the list should Auto Attack Chat Bot use for `Entites_List` setting. - - **Available values:** `whitelist` (only attack specified mobs) and `blacklist` (do not attack specified mobs). + - **Available values:** `whitelist` (only attack specified mobs) and `blacklist` (do not attack specified mobs). - - **Type:** `string` + - **Type:** `string` - - **Default:** `whitelist` + - **Default:** `whitelist` - #### `Entites_List` + #### `Entites_List` - - **Description:** + - **Description:** - A list of mobs which are either whitelisted or blacklisted, the mode is set in `List_Mode` setting. + A list of mobs which are either whitelisted or blacklisted, the mode is set in `List_Mode` setting. - You can find the full list of mobs [here](https://mccteam.github.io/r/entity/#L15). + You can find the full list of mobs [here](https://mccteam.github.io/r/entity/#L15). - - **Format:** `["", "", ...]` + - **Format:** `["", "", ...]` - - **Type:** `array of strings` + - **Type:** `array of strings` - - **Example:** `[ "Spider", "Skeleton", "Pig", ]` + - **Example:** `[ "Spider", "Skeleton", "Pig", ]` - - **Default:** `[ "Zombie", "Cow", ]` + - **Default:** `[ "Zombie", "Cow", ]` + +
## Auto Craft -

Tip

+

Note

**You need to have [inventoryhandling](configuration.md#inventoryhandling) enabled in order for basic crafting in the inventory to work, in addition if you want to use a crafting table, you need to enable [terrainandmovements](configuration.md#terrainandmovements) in order for bot to be able to reach the crafting table.**
-- **Description:** +- **Description:** - Automatically craft items in your inventory or in a crafting table. + Automatically craft items in your inventory or in a crafting table. -- **Commands:** +- **Commands:** - - `/autocraft list` + - `/autocraft list` - List all loaded recipes. + List all loaded recipes. - - `/autocraft start ` + - `/autocraft start ` - Start the crafting process with the given recipe name you had defined. + Start the crafting process with the given recipe name you had defined. - - `/autocraft stop` + - `/autocraft stop` - Stop the crafting process. + Stop the crafting process. - - `/autocraft help` + - `/autocraft help` - In-game help command. + In-game help command. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoCraft`** + **Section:** **`ChatBot.AutoCraft`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Craft Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Craft Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `CraftingTable` + - **Default:** `false` - - **Description:** + #### `CraftingTable` - This setting specifies the location of the crafting table. + - **Description:** - - **Type/Format:** + This setting specifies the location of the crafting table. - This setting is an of an `inline table` type that has the following sub-options/settings; + - **Type/Format:** - - `x` - X coordinate, the type is `double` (eg. `123.0`) + This setting is an of an `inline table` type that has the following sub-options/settings; - - `y` - Y coordinate, the type is `double` (eg. `64.0`) + - `x` - X coordinate, the type is `double` (eg. `123.0`) - - `z` - Z coordinate, the type is `double` (eg. `456.0`) + - `y` - Y coordinate, the type is `double` (eg. `64.0`) - - **Example:** + - `z` - Z coordinate, the type is `double` (eg. `456.0`) - ```toml - CraftingTable = { X = 123.0, Y = 65.0, Z = 456.0 } - ``` - - #### `OnFailure` - - - **Description:** - - This setting specifies what the Auto Craft Chat Bot should do on failure. - - Failure can happen when there are no materials available or when a crafting table can't be reached. - - - **Available values:** `abort` and `wait`. - - - **Type:** `string` - - - **Default:** `abort` - - ### Defining a recipe - -

Tip

- - **If you're using `table` you need to set the `CraftingTable` setting.** - -
- - The recipes are defines as a separate new sub-section `[[ChatBot.AutoCraft.Recipes]]` of the `[ChatBot.AutoCraft]` section. - - The `[[ChatBot.AutoCraft.Recipes]]` section needs to contain the following settings: - - - `Name` - - The name of your recipe, can be whatever you like. - - **Type**: `string` - - - `Type` - - **Available values:** `player` and `table` - - - `Result` - - This is the type of resulting item. - - **Type:** `string` - - **Example:** `"StoneBricks"` - - - `Slots` - - This setting is an array/list of material names (strings) that go into an each slot (max 9 elements). Empty slots should be marked with `"Null"` - - **Type:** `array of strings` - - **Format:** - - ```toml - Slots = [ "", "", ... ] - ``` - - - **Slots are indexed as following:** - - **`2x2` (Player)** - - ```cs - ╔═══╦═══╗ - ║ 1 ║ 2 ║ - ╠═══╬═══╣ - ║ 3 ║ 4 ║ - ╚═══╩═══╝ - ``` - - **`3x3` (Crafting Table)** - - ```cs - ╔═══╦═══╦═══╗ - ║ 1 ║ 2 ║ 3 ║ - ╠═══╬═══╬═══╣ - ║ 4 ║ 5 ║ 6 ║ - ╠═══╬═══╬═══╣ - ║ 7 ║ 8 ║ 9 ║ - ╚═══╩═══╩═══╝ - ``` - - **Full Examples:** + - **Example:** ```toml - # Stone Bricks using the player inventory - [[ChatBot.AutoCraft.Recipes]] - Name = "Recipe-Name-1" - Type = "player" - Result = "StoneBricks" - Slots = [ "Stone", "Stone", "Stone", "Stone", ] - - # Stone Bricks using a crafting table - [[ChatBot.AutoCraft.Recipes]] - Name = "Recipe-Name-2" - Type = "table" - Result = "StoneBricks" - Slots = [ "Stone", "Stone", "Null", "Stone", "Stone", "Null", "Null", "Null", "Null", ] + CraftingTable = { X = 123.0, Y = 65.0, Z = 456.0 } ``` -

Tip

+ #### `OnFailure` - **If you have a case where you have to leave some fields empty, use `"Null"` to mark them as empty. Example for stone bricks: `Slots = [ "Stone", "Stone", "Null", "Stone", "Stone", "Null", "Null", "Null", "Null", ]`** + - **Description:** - **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** + This setting specifies what the Auto Craft Chat Bot should do on failure. - **Make sure to provide materials for your bot by placing them in inventory first.** + Failure can happen when there are no materials available or when a crafting table can't be reached. -
+ - **Available values:** `abort` and `wait`. + + - **Type:** `string` + + - **Default:** `abort` + + ### Defining a recipe + +

Note

+ + **If you're using `table` you need to set the `CraftingTable` setting.** + +
+ + The recipes are defines as a separate new sub-section `[[ChatBot.AutoCraft.Recipes]]` of the `[ChatBot.AutoCraft]` section. + + The `[[ChatBot.AutoCraft.Recipes]]` section needs to contain the following settings: + + - `Name` + + The name of your recipe, can be whatever you like. + + **Type**: `string` + + - `Type` + + **Available values:** `player` and `table` + + - `Result` + + This is the type of resulting item. + + **Type:** `string` + + **Example:** `"StoneBricks"` + + - `Slots` + + This setting is an array/list of material names (strings) that go into an each slot (max 9 elements). Empty slots should be marked with `"Null"` + + **Type:** `array of strings` + + **Format:** + + ```toml + Slots = [ "", "", ... ] + ``` + + **Slots are indexed as following:** + + **`2x2` (Player)** + + ```cs + ╔═══╦═══╗ + ║ 1 ║ 2 ║ + ╠═══╬═══╣ + ║ 3 ║ 4 ║ + ╚═══╩═══╝ + ``` + + **`3x3` (Crafting Table)** + + ```cs + ╔═══╦═══╦═══╗ + ║ 1 ║ 2 ║ 3 ║ + ╠═══╬═══╬═══╣ + ║ 4 ║ 5 ║ 6 ║ + ╠═══╬═══╬═══╣ + ║ 7 ║ 8 ║ 9 ║ + ╚═══╩═══╩═══╝ + ``` + + **Full Examples:** + + ```toml + # Stone Bricks using the player inventory + [[ChatBot.AutoCraft.Recipes]] + Name = "Recipe-Name-1" + Type = "player" + Result = "StoneBricks" + Slots = [ "Stone", "Stone", "Stone", "Stone", ] + + # Stone Bricks using a crafting table + [[ChatBot.AutoCraft.Recipes]] + Name = "Recipe-Name-2" + Type = "table" + Result = "StoneBricks" + Slots = [ "Stone", "Stone", "Null", "Stone", "Stone", "Null", "Null", "Null", "Null", ] + ``` + +

Tip

+ + **If you have a case where you have to leave some fields empty, use `"Null"` to mark them as empty. Example for stone bricks: `Slots = [ "Stone", "Stone", "Null", "Stone", "Stone", "Null", "Null", "Null", "Null", ]`** + + **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** + + **Make sure to provide materials for your bot by placing them in inventory first.** + +
+ +
## Auto Dig -- **Description:** +- **Description:** - Automatically digs block on specified locations. + Automatically digs block on specified locations. -

Tip

+

Note

- **You need to have [inventoryhandling](configuration.md#inventoryhandling) and [terrainandmovements](configuration.md#terrainandmovements) enabled in order for this bot to work.** + **You need to have [inventoryhandling](configuration.md#inventoryhandling) and [terrainandmovements](configuration.md#terrainandmovements) enabled in order for this bot to work.** -
+
-

Tip

+

Note

- **Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead.** + **Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead.** -
+
-- **Commands:** +- **Commands:** - - `/digbot start` - Starts the digging + - `/digbot start` - Starts the digging - - `/digbot stop` - Stops the digging + - `/digbot stop` - Stops the digging -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoDig`** + **Section:** **`ChatBot.AutoDig`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Dig Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Dig Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Mode` + - **Default:** `false` - - **Description:** + #### `Mode` - This setting specifies in which mode the Auto Dig Chat Bot will operate. + - **Description:** - - **Available values:** + This setting specifies in which mode the Auto Dig Chat Bot will operate. - - `lookat` + - **Available values:** - Digs the block that the bot is looking at. + - `lookat` - - `fixedpos` + Digs the block that the bot is looking at. - Digs the block in a fixed location/position/coordinate. + - `fixedpos` - - `both` + Digs the block in a fixed location/position/coordinate. - Dig only when the block you are looking at is in the "Locations" list. + - `both` - - **Type:** `string` + Dig only when the block you are looking at is in the "Locations" list. - - **Default:** `lookat` + - **Type:** `string` - #### `Locations` + - **Default:** `lookat` - - **Description:** + #### `Locations` - This setting specifies an array/list of locations which the bot will dig out. + - **Description:** - - **Type/Format:** + This setting specifies an array/list of locations which the bot will dig out. - The type of this setting is an array of inline table which has the following sub-options/settings: + - **Type/Format:** - - `x` - X coordinate, the type is `double` (eg. `123.45`) + The type of this setting is an array of inline table which has the following sub-options/settings: - - `y` - Y coordinate, the type is `double` (eg. `64.0`) + - `x` - X coordinate, the type is `double` (eg. `123.45`) - - `z` - Z coordinate, the type is `double` (eg. `234.5`) + - `y` - Y coordinate, the type is `double` (eg. `64.0`) - - **Full example:** + - `z` - Z coordinate, the type is `double` (eg. `234.5`) - ```toml - Locations = [ - { x = 123.5, y = 64.0, z = 234.5 }, - { x = 124.5, y = 63.0, z = 235.5 }, - ] - ``` + - **Full example:** - #### `Location_Order` + ```toml + Locations = [ + { x = 123.5, y = 64.0, z = 234.5 }, + { x = 124.5, y = 63.0, z = 235.5 }, + ] + ``` - - **Description:** + #### `Location_Order` - This setting specifies in which order the Auto Dig Chat Bot will dig blocks. + - **Description:** - - **Available values:** + This setting specifies in which order the Auto Dig Chat Bot will dig blocks. - - `distance` + - **Available values:** - Digs the block closest to the bot. + - `distance` - - `index` + Digs the block closest to the bot. - Digs blocks in the list order. + - `index` - - **Type:** `string` + Digs blocks in the list order. - - **Default:** `distance` + - **Type:** `string` - #### `Auto_Start_Delay` + - **Default:** `distance` - - **Description:** + #### `Auto_Start_Delay` - How many seconds to wait after entering the game to start digging automatically. + - **Description:** - Set to `-1` to disable the automatic start. + How many seconds to wait after entering the game to start digging automatically. - - **Type:** `float` + Set to `-1` to disable the automatic start. - - **Default:** `3.0` + - **Type:** `float` - #### `Dig_Timeout` + - **Default:** `3.0` - - **Description:** + #### `Auto_Tool_Switch` - If mining a block takes longer than this value, a new attempt will be made to find a block to mine. + - **Description:** - - **Type:** `float` + Automatically switch to a more suitable tool from your inventory before digging. - - **Default:** `60.0` + When `Durability_Limit` is above zero, tools below that durability threshold are skipped. - #### `Log_Block_Dig` + - **Available values:** `true` and `false` - - **Description:** + - **Type:** `boolean` - This setting specifies whether to output logs in to the console when digging blocks. + - **Default:** `false` - - **Available values:** `true` and `false`. + #### `Apply_Efficiency_Enchantments` - - **Type:** `boolean` + - **Description:** - - **Default:** `true` + Include Efficiency enchantments when Auto Dig calculates how long it should wait before finishing a block break. - #### `List_Type` + Disable this if a server's anti-cheat expects slower mining timing. This only changes MCC's timing calculation; it does not remove the enchantment from your tool. - - **Description:** + - **Available values:** `true` and `false` - This setting specifies the mode at which the `Blocks` setting is operating. + - **Type:** `boolean` - - **Available values:** `whitelist` (only dig specified blocks) and `blacklist` (do not dig specified blocks). + - **Default:** `true` - - **Type:** `string` + #### `Apply_Haste_Effects` - - **Default:** `whitelist` + - **Description:** - #### `Blocks` + Include Haste and Conduit Power effects when Auto Dig calculates how long it should wait before finishing a block break. - - **Description:** + Disable this if a server's anti-cheat does not allow the faster timing. This only changes MCC's timing calculation; it does not remove the effect from your player. - This setting specifies the list of blocks which either should not should not be dug out. + - **Available values:** `true` and `false` - **The list of block types can be found [here](https://mccteam.github.io/r/block/#L15).** + - **Type:** `boolean` - - **Format:** `[ "", "", ...]` + - **Default:** `true` - - **Type:** `array of strings` + #### `Durability_Limit` - - **Example:** `Blocks = [ "DiamondOre", "RedstoneOre", "EmeraldOre", "RedstoneBlock" ]` + - **Description:** - - **Default:** `[ "Cobblestone", "Stone", ]` + Will not use tools with less durability than this. + + Set to `0` to disable this durability check. + + - **Type:** `integer` + + - **Default:** `2` + + #### `Drop_Low_Durability_Tools` + + - **Description:** + + Drop the replaced tool if its remaining durability is below `Durability_Limit`. + + This setting is only useful when `Auto_Tool_Switch` is enabled. + + - **Available values:** `true` and `false` + + - **Type:** `boolean` + + - **Default:** `false` + + #### `Dig_Timeout` + + - **Description:** + + If mining a block takes longer than this value, a new attempt will be made to find a block to mine. + + - **Type:** `float` + + - **Default:** `60.0` + + #### `Log_Block_Dig` + + - **Description:** + + This setting specifies whether to output logs in to the console when digging blocks. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `List_Type` + + - **Description:** + + This setting specifies the mode at which the `Blocks` setting is operating. + + - **Available values:** `whitelist` (only dig specified blocks) and `blacklist` (do not dig specified blocks). + + - **Type:** `string` + + - **Default:** `whitelist` + + #### `Blocks` + + - **Description:** + + This setting specifies the list of blocks which either should not should not be dug out. + + **The list of block types can be found [here](https://mccteam.github.io/r/block/#L15).** + + - **Format:** `[ "", "", ...]` + + - **Type:** `array of strings` + + - **Example:** `Blocks = [ "DiamondOre", "RedstoneOre", "EmeraldOre", "RedstoneBlock" ]` + + - **Default:** `[ "Cobblestone", "Stone", ]` + +
## Auto Drop -- **Description:** +- **Description:** - Automatically drop items you don't need from the inventory. + Automatically drop items you don't need from the inventory. -

Tip

+

Note

- **You need to have [inventoryhandling](configuration.md#inventoryhandling) enabled in order for this bot to work** + **You need to have [inventoryhandling](configuration.md#inventoryhandling) enabled in order for this bot to work** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoDrop`** + **Section:** **`ChatBot.AutoDrop`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Drop Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Drop Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Mode` + - **Default:** `false` - - **Description:** + #### `Mode` - This setting specifies the mode of the auto dropping. + - **Description:** - Available values: + This setting specifies the mode of the auto dropping. - - `include` + Available values: - This mode will drop any items specified in the list in the `Items` setting. + - `include` - - `exclude` + This mode will drop any items specified in the list in the `Items` setting. - This mode will drop any other items than specified in the list in the `Items` setting. + - `exclude` - So it would keep the items specified in the list. + This mode will drop any other items than specified in the list in the `Items` setting. - - `everything` + So it would keep the items specified in the list. - Drop any item regardless of the items listed in the `Items` setting. + - `everything` - - **Type:** `string` + Drop any item regardless of the items listed in the `Items` setting. - - **Default:** `include` + - **Type:** `string` - #### `Items` + - **Default:** `include` -

Tip

+ #### `Items` - **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** +

Note

-
+ **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** - - **Description:** +
- This setting is where you can specify the list of items which you want to drop, or keep. + - **Description:** + This setting is where you can specify the list of items which you want to drop, or keep. - - **Format:** `[ "", "", ...]` + - **Format:** `[ "", "", ...]` - - **Type:** `array of strings` + - **Type:** `array of strings` - - **Example:** `[ "Totem", "GlassBottle", ]` + - **Example:** `[ "Totem", "GlassBottle", ]` - - **Default:** `[ "Cobblestone", "Dirt", ]` + - **Default:** `[ "Cobblestone", "Dirt", ]` + +
## Auto Eat -- **Description:** +- **Description:** - Automatically eat food when your Hunger value is low. + Automatically eat food when your Hunger value is low. -

Tip

+

Note

- **You need to have [inventoryhandling](configuration.md#inventoryhandling) enabled in order for this bot to work** + **You need to have [inventoryhandling](configuration.md#inventoryhandling) enabled in order for this bot to work** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoEat`** + **Section:** **`ChatBot.AutoEat`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Eat Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Eat Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Threshold` + - **Default:** `false` - - **Description:** + #### `Threshold` - Threshold bellow which the bot will auto eat. + - **Description:** - - **Type:** `integer` + Threshold bellow which the bot will auto eat. - - **Default:** `6` + - **Type:** `integer` + + - **Default:** `6` + +
## Auto Fishing -- **Description:** +- **Description:** - Automatically catch fish using a fishing rod. + Automatically catch fish using a fishing rod. + Bite detection combines bobber movement, bobber velocity, and splash sounds. -

Tip

+

Note

- **You need to have [entityhandling](configuration.md#entityhandling) enabled in order for this bot to work.** + **You need to have [entityhandling](configuration.md#entityhandling) enabled in order for this bot to work.** -
+
-

Tip

+

Note

- **To use the automatic rod switching and durability check feature, you need to enable [inventoryhandling](configuration.md#inventoryhandling).** + **To use the automatic rod switching and durability check feature, you need to enable [inventoryhandling](configuration.md#inventoryhandling).** -
+
-

Tip

+

Note

- **Note: To adjust the position or angle after catching a fish, you need to enable [terrainandmovements](configuration.md#terrainandmovements).** + **Note: To adjust the position or angle after catching a fish, you need to enable [terrainandmovements](configuration.md#terrainandmovements).** -
+
-

Tip

+

Tip

- **A fishing rod with **Mending enchantment** is strongly recommended.** + **A fishing rod with **Mending enchantment** is strongly recommended.** -
+
- **Steps for using this bot (with the default setting)** + **Steps for using this bot (with the default setting)** - 1. Hold a fishing rod and aim towards the sea before login with MCC - 2. Make sure `AutoFish` is `enabled` in config file - 3. Login with MCC - 4. You will be able to see the log "Fishing will start in 3.0 second(s).". + 1. Hold a fishing rod and aim towards the sea before login with MCC + 2. Make sure `AutoFish` is `enabled` in config file + 3. Login with MCC + 4. You will be able to see the log "Fishing will start in 3.0 second(s).". -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoFishing`** + **Section:** **`ChatBot.AutoFishing`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Fishing Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Fishing Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Antidespawn` + - **Default:** `false` - - **Description:** + #### `Antidespawn` - This option may be used in some special cases, so if it has not been modified before, leave the default value. + - **Description:** - - **Available values:** `true` and `false`. + This option may be used in some special cases, so if it has not been modified before, leave the default value. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Mainhand` + - **Default:** `false` - - **Description:** + #### `Mainhand` - Whether to use the main hand or off hand to hold the rod. + - **Description:** - - **Available values:** + Whether to use the main hand or off hand to hold the rod. - - `true` (Main Hand) - - `false` (Off Hand) + - **Available values:** - - **Type:** `boolean` + - `true` (Main Hand) + - `false` (Off Hand) - - **Default:** `true` + - **Type:** `boolean` - #### `Auto_Start` + - **Default:** `true` - - **Description:** + #### `Auto_Start` - Whether to start fishing automatically after joining the game or switching worlds. + - **Description:** - - **Available values:** `true` and `false`. + Whether to start fishing automatically after joining the game or switching worlds. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `true` + - **Type:** `boolean` - #### `Cast_Delay` + - **Default:** `true` - - **Description:** + #### `Cast_Delay` - Wait how many seconds after successfully catching a fish before recasting the rod. + - **Description:** - - **Type:** `float` + Wait how many seconds after successfully catching a fish before recasting the rod. - - **Default:** `0.4` + - **Type:** `float` - #### `Fishing_Delay` + - **Default:** `0.4` - - **Description:** + #### `Fishing_Delay` - Effective only when `auto_start = true`. + - **Description:** - After joining the game or switching worlds, wait how many seconds before starting to fish automatically. + Effective only when `auto_start = true`. - - **Type:** `float` + After joining the game or switching worlds, wait how many seconds before starting to fish automatically. - - **Default:** `3.0` + - **Type:** `float` - #### `Fishing_Timeout` + - **Default:** `3.0` - - **Description:** + #### `Fishing_Timeout` - How long the fish bite is not detected is considered a timeout. It will re-cast after the timeout. + - **Description:** - - **Type:** `float` + How long the fish bite is not detected is considered a timeout. It will re-cast after the timeout. - - **Default:** `300.0` + - **Type:** `float` - #### `Durability_Limit` + - **Default:** `300.0` - - **Description:** + #### `Durability_Limit` - Will not use rods with less durability than this (full durability is 64). + - **Description:** - Set to zero to disable this feature. + Will not use rods with less durability than this (full durability is 64). - **Type/Available values:** An integer number from `0` to `64`. + Set to zero to disable this feature. - - **Default:** `2` + **Type/Available values:** An integer number from `0` to `64`. - #### `Auto_Rod_Switch` + - **Default:** `2` - - **Description:** + #### `Auto_Rod_Switch` - Switch to a new rod from inventory after the current rod is unavailable. + - **Description:** - - **Available values:** `true` and `false`. + Switch to a new rod from inventory after the current rod is unavailable. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `true` + - **Type:** `boolean` - #### `Stationary_Threshold` + - **Default:** `true` - - **Description:** + #### `Stationary_Threshold` - For each movement of the fishhook entity (entity movement packet), if the distance on both X and Z axes is below this threshold it will be considered as stationary. + - **Description:** - This is to avoid being detected as a bite during the casting of the hook. + For each movement of the fishhook entity (entity movement packet), if the distance on both X and Z axes is below this threshold it will be considered as stationary. - **If set too high, it will cause the rod to be reeled in while casting.** + This is to avoid being detected as a bite during the casting of the hook. - **If set too low, it will result in not detecting a bite.** + **If set too high, it will cause the rod to be reeled in while casting.** - - **Type:** `float` + **If set too low, it will result in not detecting a bite.** - - **Default:** `0.001` + - **Type:** `float` - #### `Hook_Threshold` + - **Default:** `0.001` - - **Description:** + #### `Hook_Threshold` - For each movement of the fishhook entity (entity movement packet), if it is stationary (check `stationary_threshold`) and its movement on the Y-axis is greater than this threshold, it will be considered to have caught a fish. + - **Description:** - If it is set too high, it will cause normal bites to be ignored. + For each movement of the fishhook entity (entity movement packet), if it is stationary (check `stationary_threshold`) and its movement on the Y-axis is greater than this threshold, it will be considered to have caught a fish. - If set too low, it can cause small fluctuations in the hook to be recognized as bites. + If it is set too high, it will cause normal bites to be ignored. - - **Type:** `float` + If set too low, it can cause small fluctuations in the hook to be recognized as bites. - - **Default:** `0.2` + - **Type:** `float` - #### `Log_Fish_Bobber` + - **Default:** `0.2` - - **Description:** + #### `Enable_Velocity_Detection` - When turned on it will be print a log every time a fishhook entity movement packet is received. + - **Description:** - If auto-fishing does not work as expected, turn this option on to adjust `stationary_threshold` and `hook_threshold`, or create an issue and attach these logs. + Enables bite detection using the fishing bobber velocity packet. - - **Available values:** `true` and `false`. + This improves reliability when bobber X/Z movement is constrained (for example by blocks near the water surface). - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Enable_Move` + - **Default:** `true` - - **Description:** + #### `Velocity_Hook_Threshold` - Some plugins do not allow the player to fish in one place for a long time. This setting allows the player to change position/angle after each catch. + - **Description:** - Each position is added as a new `[[ChatBot.AutoFishing.Movements]]` subsection, more on that bellow. + Velocity Y threshold in blocks/tick for velocity-based bite detection. - - **Available values:** `true` and `false`. + Values below this threshold are considered a bite. Keep this value negative. - - **Type:** `boolean` + - **Type:** `float` - - **Default:** `false` + - **Default:** `-0.2` - ### Adding a position/movement + #### `Enable_Sound_Detection` - Each position/movement is added as a new `[[ChatBot.AutoFishing.Movements]]` subsection of `[ChatBot.AutoFishing]`. + - **Description:** - **Available settings/options:** + Enables bite detection using nearby splash sounds (`entity.fishing_bobber.splash`). - - `XYZ` + - **Available values:** `true` and `false`. - This setting specifies at location the bot should move to. + - **Type:** `boolean` - The type of this setting is `inline table`, that has the following sub-settings/options: + - **Default:** `true` - - `x` - X coordinate, the type is `double` (eg. `123.0`) + #### `Sound_Distance` - - `y` - Y coordinate, the type is `double` (eg. `64.0`) + - **Description:** - - `z` - Z coordinate, the type is `double` (eg. `-654.0`) + Maximum distance in blocks between a splash sound and the tracked bobber to treat it as a bite. - **Example**: + - **Type:** `float` - ```toml - XYZ = { x = 123.0, y = 64.0, z = -654.0 } - ``` + - **Default:** `5.0` - - `facing` + #### `Detection_Warmup` - This setting specifies at which angle the bot will look at when he arrives to this position/location. + - **Description:** - The type of this setting is `inline table`, that has the following sub-settings/options: + Delay in seconds after bobber spawn before bite detection starts. - - `yaw` - The type is `double` (eg. `12.34`) + This helps ignore the initial cast-entry splash/motion. - - `pitch` - The type is `double` (eg. `-23.45`) + - **Type:** `float` - **Example**: + - **Default:** `1.0` - ```toml - facing = { yaw = 12.34, pitch = -23.45 } - ``` + #### `Log_Fish_Bobber` - #### Full example + - **Description:** + + When turned on it will be print a log every time a fishhook entity movement packet is received. + + If auto-fishing does not work as expected, turn this option on to adjust `stationary_threshold` and `hook_threshold`, or create an issue and attach these logs. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `false` + + #### `Enable_Move` + + - **Description:** + + Some plugins do not allow the player to fish in one place for a long time. This setting allows the player to change position/angle after each catch. + + Each position is added as a new `[[ChatBot.AutoFishing.Movements]]` subsection, more on that bellow. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `false` + + ### Adding a position/movement + + Each position/movement is added as a new `[[ChatBot.AutoFishing.Movements]]` subsection of `[ChatBot.AutoFishing]`. + + **Available settings/options:** + + - `XYZ` + + This setting specifies at location the bot should move to. + + The type of this setting is `inline table`, that has the following sub-settings/options: + + - `x` - X coordinate, the type is `double` (eg. `123.0`) + + - `y` - Y coordinate, the type is `double` (eg. `64.0`) + + - `z` - Z coordinate, the type is `double` (eg. `-654.0`) + + **Example**: ```toml - [[ChatBot.AutoFishing.Movements]] - facing = { yaw = 12.34, pitch = -23.45 } - - [[ChatBot.AutoFishing.Movements]] - XYZ = { x = 123.45, y = 64.0, z = -654.32 } - facing = { yaw = -25.14, pitch = 36.25 } + XYZ = { x = 123.0, y = 64.0, z = -654.0 } ``` + - `facing` + + This setting specifies at which angle the bot will look at when he arrives to this position/location. + + The type of this setting is `inline table`, that has the following sub-settings/options: + + - `yaw` - The type is `double` (eg. `12.34`) + + - `pitch` - The type is `double` (eg. `-23.45`) + + **Example**: + + ```toml + facing = { yaw = 12.34, pitch = -23.45 } + ``` + + #### Full example + + ```toml + [[ChatBot.AutoFishing.Movements]] + facing = { yaw = 12.34, pitch = -23.45 } + + [[ChatBot.AutoFishing.Movements]] + XYZ = { x = 123.45, y = 64.0, z = -654.32 } + facing = { yaw = -25.14, pitch = 36.25 } + ``` + +
+ ## Auto Relog -- **Description:** +- **Description:** - Make MCC automatically relog when disconnected by the server, for example because the server is restating. + Make MCC automatically relog when disconnected by the server, for example because the server is restating. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoRelog`** + **Section:** **`ChatBot.AutoRelog`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Relog Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Relog Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Delay` + - **Default:** `false` - - **Description:** + #### `Delay` - The delay time before joining the server. + - **Description:** - If the `min` and `max` are the same, the time will be consistent, however, if you want a random time, you can set `min` and `max` to different values to get a random time. The time format is in seconds, and the type is double. (eg. `37.0`) + The delay time before joining the server. - - **Format:** `{ min = , max = }` + If the `min` and `max` are the same, the time will be consistent, however, if you want a random time, you can set `min` and `max` to different values to get a random time. The time format is in seconds, and the type is double. (eg. `37.0`) - - **Type:** `inline table` + - **Format:** `{ min = , max = }` - - **Example:** `{ min = 8.0, max = 60.0 }` + - **Type:** `inline table` - - **Default:** `{ min = 3.0, max = 3.0 }` + - **Example:** `{ min = 8.0, max = 60.0 }` - #### `Retries` + - **Default:** `{ min = 3.0, max = 3.0 }` -

Tip

+ #### `Retries` - **This might get you banned by the server owners.** +

Note

-
+ **This might get you banned by the server owners.** - - **Description:** +
- Number of retries. + - **Description:** - Use `-1` for infinite retries. + Number of retries. - - **Default:** `-1` + Use `-1` for infinite retries. - #### `Ignore_Kick_Message` + - **Default:** `-1` - - **Description:** + #### `Ignore_Kick_Message` - This settings specifies if the `Kick_Messages` setting will be ignored, if set to `true` it will auto relog regardless of the kick messages. + - **Description:** - - **Type:** `boolean` + This settings specifies if the `Kick_Messages` setting will be ignored, if set to `true` it will auto relog regardless of the kick messages. - - **Default:** `false` + - **Type:** `boolean` - #### `Kick_Messages` + - **Default:** `false` - - **Description:** + #### `Kick_Messages` - A list of words which should trigger the Auto Reconnect Chat Bot. + - **Description:** - - **Format:** `[ "", "", ... ]` + A list of words which should trigger the Auto Reconnect Chat Bot. - - **Type:** `array of strings` + - **Format:** `[ "", "", ... ]` - - **Default:** `[ "Connection has been lost", "Server is restarting", "Server is full", "Too Many people", ]` + - **Type:** `array of strings` + + - **Default:** `[ "Connection has been lost", "Server is restarting", "Server is full", "Too Many people", ]` + +
## Auto Respond -- **Description:** +- **Description:** - Run commands or send messages automatically when a specified pattern is detected in the chat. + Run commands or send messages automatically when a specified pattern is detected in the chat. -

Warning

+

Warning

- **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `AutoRespond` only if you trust server admins.** + **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `AutoRespond` only if you trust server admins.** -
+
-

Warning

+

Warning

- **This bot may get spammy depending on your rules, although the global [messagecooldown](configuration.md#messagecooldown) setting can help you avoiding accidental spam.** + **This bot may get spammy depending on your rules, although the global [messagecooldown](configuration.md#messagecooldown) setting can help you avoiding accidental spam.** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.AutoRespond`** + **Section:** **`ChatBot.AutoRespond`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Auto Respond Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Auto Respond Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Matches_File` + - **Default:** `false` -

Tip

+ #### `Matches_File` - **This file is not created by default, we recommend making a clone of the [`sample-matches.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-matches.ini) and changing it according to your needs.** +

Note

-
+ **This file is not created by default, we recommend making a clone of the [`sample-matches.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-matches.ini) and changing it according to your needs.** -

Warning

+
- **If you want to use variables from this chat bot in scripts, currently that does not work. You will have to use a C# script in that case. We are working on getting this functionality back.** +

Warning

-
+ **If you want to use variables from this chat bot in scripts, currently that does not work. You will have to use a C# script in that case. We are working on getting this functionality back.** - - **Description:** +
- This setting specifies the path to the file which contains the list of rules for detecting of keywords and responding on them. + - **Description:** - To find out how to configure the rules, take a look at the [`sample-matches.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-matches.ini) which has very detailed examples and a lot of comments. + This setting specifies the path to the file which contains the list of rules for detecting of keywords and responding on them. - _PS: In the future we will document the rules here with examples too._ + To find out how to configure the rules, take a look at the [`sample-matches.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-matches.ini) which has very detailed examples and a lot of comments. - - **Type:** `string` + _PS: In the future we will document the rules here with examples too._ - - **Default:** `matches.ini` + - **Type:** `string` - #### `Match_Colors` + - **Default:** `matches.ini` -

Tip

+ #### `Match_Colors` - **This feature uses the `§` symbol for color matching** +

Note

-
+ **This feature uses the `§` symbol for color matching** - - **Description:** +
- This setting specifies if the Auto Respond Chat Bot should keep the color formatting send by the server. + - **Description:** - You can use this when you need to match text by colors. + This setting specifies if the Auto Respond Chat Bot should keep the color formatting send by the server. - List of all color codes: [here](https://minecraft.tools/en/color-code.php) + You can use this when you need to match text by colors. - - **Type:** `boolean` + List of all color codes: [here](https://minecraft.tools/en/color-code.php) - - **Default:** `false` + - **Type:** `boolean` + + - **Default:** `false` + +
## Chat Log -- **Description:** +- **Description:** - Make MCC log chat messages into a file. + Make MCC log chat messages into a file. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.ChatLog`** + **Section:** **`ChatBot.ChatLog`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Chat Log Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Chat Log Chat Bot is enabled. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `Add_DateTime` + - **Default:** `false` - - **Description:** + #### `Add_DateTime` - This setting specifies if the Chat Log should prepend timestamps to the logged messages. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Chat Log should prepend timestamps to the logged messages. - - **Default:** `true` + - **Available values:** `true` and `false`. - #### `Log_File` + - **Default:** `true` - - **Description:** + #### `Log_File` - This setting specifies the name of the Chat Log file that will be created. + - **Description:** - - **Default:** `chatlog-%username%-%serverip%.txt` + This setting specifies the name of the Chat Log file that will be created. - #### `Filter` + - **Default:** `chatlog-%username%-%serverip%.txt` - - **Description:** + #### `Filter` - Type of messages to be logged into the file. + - **Description:** - Available values: + Type of messages to be logged into the file. - - `all` + Available values: - All text from the console + - `all` - - `messages` + All text from the console - All messages, including system, plugin channel, player and server. + - `messages` - - `chat` + All messages, including system, plugin channel, player and server. - Only chat messages. + - `chat` - - `private` + Only chat messages. - Only private messages. + - `private` - - `internal` + Only private messages. - Only internal messages and commands. + - `internal` - - **Default:** `messages` + Only internal messages and commands. + + - **Default:** `messages` + +
## Discord Bridge -- **Description:** +- **Description:** - This Chat Bot allows you to send and receive messages and MCC commands via a Discord channel. + This Chat Bot allows you to send and receive messages and MCC commands via a Discord channel. -- **Setup:** +- **Setup:** - In order for this to work you must create a Discord bot on the [Discord Developers portal](https://discord.com/developers/applications/). + In order for this to work you must create a Discord bot on the [Discord Developers portal](https://discord.com/developers/applications/). - First go to [Discord Developers portal](https://discord.com/developers/applications/), click on **New Application**, fill out the name of your bot and confirm the terms of service and click **Create**. + First go to [Discord Developers portal](https://discord.com/developers/applications/), click on **New Application**, fill out the name of your bot and confirm the terms of service and click **Create**. - ![Image](/images/guide/Discord_Create_Application.png) + ![Image](/images/guide/Discord_Create_Application.png) - Copy the **Application ID** and save it somewhere. + Copy the **Application ID** and save it somewhere. - Click on the **Bot** tab in the left menu. + Click on the **Bot** tab in the left menu. - Click on **Add Bot** + Click on **Add Bot** - ![Image](/images/guide/Discord_Add_Bot.png) + ![Image](/images/guide/Discord_Add_Bot.png) - Click on the **Reset Token** button and copy the generated token, then paste it in the `Token` field in the MCC configuration. + Click on the **Reset Token** button and copy the generated token, then paste it in the `Token` field in the MCC configuration. - Enable `Message Content Intent`, `Server Members Intent` and `Presence Intent`. + Enable `Message Content Intent`, `Server Members Intent` and `Presence Intent`. - ![Image](/images/guide/Discord_Reset_Token.png) - ![Image](https://i.pics.rs/AAhyx.png) + ![Image](/images/guide/Discord_Reset_Token.png) + ![Image](https://i.pics.rs/AAhyx.png) -

Warning

+

Warning

- **Token is what gives you access to the Bot, do not share it with anyone and keep it safe!** + **Token is what gives you access to the Bot, do not share it with anyone and keep it safe!** -
+
-

Warning

+

Warning

- **You must Enable `Message Content Intent`, `Server Members Intent` and `Presence Intent` for the bot to work!** + **You must Enable `Message Content Intent`, `Server Members Intent` and `Presence Intent` for the bot to work!** -
+
- Then go to [Discord Permissions Calculator](https://discordapi.com/permissions.html). - Paste the **Application Id** that you've copied into the **Client ID** field, then Check/Enable the **Administrator** field in General Permissions section. - Finally click on the **Link** down bellow and invite the Bot on to a server you want to interact with the MCC on. + Then go to [Discord Permissions Calculator](https://discordapi.com/permissions.html). + Paste the **Application Id** that you've copied into the **Client ID** field, then Check/Enable the **Administrator** field in General Permissions section. + Finally click on the **Link** down bellow and invite the Bot on to a server you want to interact with the MCC on. - ![Image](/images/guide/Discord_Permissions.png) + ![Image](/images/guide/Discord_Permissions.png) - Go to your Discord Client and go to **Settings -> Advanced**, Enable **Developer Mode**. + Go to your Discord Client and go to **Settings -> Advanced**, Enable **Developer Mode**. - Then **right click** on a server where you invited the bot to in the server list and click on **Copy ID**, paste the copied id in `GuildId` in your MCC configuration. + Then **right click** on a server where you invited the bot to in the server list and click on **Copy ID**, paste the copied id in `GuildId` in your MCC configuration. - Then **right click** on a channel where you want to interact with the bot and click on **Copy ID**, paste the copied id in `ChannelId` in your MCC configuration. + Then **right click** on a channel where you want to interact with the bot and click on **Copy ID**, paste the copied id in `ChannelId` in your MCC configuration. - Send a message in that channel and **right click** on your nick and click **Copy ID** and paste the copied id in `OwnersIds` list setting in your MCC configuration. + Send a message in that channel and **right click** on your nick and click **Copy ID** and paste the copied id in `OwnersIds` list setting in your MCC configuration. - Enable the bot by setting `Enabled` to `true` in your MCC configuration and start the MCC. + Enable the bot by setting `Enabled` to `true` in your MCC configuration and start the MCC. -- **Usage:** +- **Usage:** - To send a message simply type it out in the Discord channel and press enter. + To send a message simply type it out in the Discord channel and press enter. - To execute a MCC command, you must prefix it with a dot (`.`). - Example: `.move 145 64 832` + To execute a MCC command, you must prefix it with a dot (`.`). + Example: `.move 145 64 832` -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.DiscordBrdige`** + **Section:** **`ChatBot.DiscordBridge`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Discord Bridge Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Discord Bridge Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Token` + - **Default:** `false` - - **Description:** + #### `Token` - This is the token of your Discord bot. + - **Description:** - - **Type:** `string` + This is the token of your Discord bot. - #### `GuildId` + - **Type:** `string` - - **Description:** + #### `GuildId` - This is the ID of your server/guild where you have invited the bot to. + - **Description:** - - **Type:** `unsigned long` + This is the ID of your server/guild where you have invited the bot to. - #### `ChannelId` + - **Type:** `unsigned long` - - **Description:** + #### `ChannelId` - This is the ID of a channel on your server/guild where you want to interact with the bot. + - **Description:** - - **Type:** `unsigned long` + This is the ID of a channel on your server/guild where you want to interact with the bot. - #### `OwnersIds` + - **Type:** `unsigned long` - - **Description:** + #### `OwnersIds` - This is a list of Discord user IDs which can interact with the bot. + - **Description:** - - **Type:** `list/array of: unsigned long` + This is a list of Discord user IDs which can interact with the bot. - #### `PrivateMessageFormat` + - **Type:** `list/array of: unsigned long` - - **Description:** + #### `Message_Send_Timeout` - This is a format that will be used when someone has sent you a private message on the server. + - **Description:** - Parts of the message that are between `{` and `}` will be replaced by the Chat Bot during runtime, you should not change them in any way! + How long (in seconds) to wait for a message to be sent to Discord before giving up. - For example `{message}` will be replaced with an actual message, `{username}` will be replaced with the username of the person who sent a message on the server and `{timestamp}` will be replaced with the current date and time. + - **Type:** `integer` - For Discord message formatting/styling, refer to [this guide](https://www.writebots.com/discord-text-formatting/). + - **Default:** `3` - - **Type:** `string` + #### `Allow_Other_Bot_Messages` - - **Default:** `**[Private Message]** {username}: {message}` + - **Description:** - #### `PublicMessageFormat` + When enabled, messages from other Discord bots in the channel are relayed to Minecraft chat. The bridge always ignores its own messages to prevent loops. - - **Description:** + - **Available values:** `true` and `false`. - This is a format that will be used when sending a public message to the Discord channel. + - **Type:** `boolean` - Parts of the message that are between `{` and `}` will be replaced by the Chat Bot during runtime, you should not change them in any way! + - **Default:** `false` - For example `{message}` will be replaced with an actual message, `{username}` will be replaced with the username of the person who sent a message on the server and `{timestamp}` will be replaced with the current date and time. + #### `PrivateMessageFormat` - For Discord message formatting/styling, refer to [this guide](https://www.writebots.com/discord-text-formatting/). + - **Description:** - - **Type:** `string` + The format used when someone sends you a private message on the server. - - **Default:** `{username}: {message}` + Parts of the message between `{` and `}` are replaced by the Chat Bot at runtime; do not change them. - #### `TeleportRequestMessageFormat` + `{message}` is replaced with the message text, `{username}` with the sender's name, and `{timestamp}` with the current date and time. - - **Description:** + For Discord message formatting, refer to [this guide](https://www.writebots.com/discord-text-formatting/). - This is a format that will be used when someone has sent you a Teleport Request. + - **Type:** `string` - Parts of the message that are between `{` and `}` will be replaced by the Chat Bot during runtime, you should not change them in any way! + - **Default:** `**[Private Message]** {username}: {message}` - For example `{message}` will be replaced with an actual message, `{username}` will be replaced with the username of the person who sent a message on the server and `{timestamp}` will be replaced with the current date and time. + #### `PublicMessageFormat` - For Discord message formatting/styling, refer to [this guide](https://www.writebots.com/discord-text-formatting/). + - **Description:** - - **Type:** `string` + The format used when sending a public message to the Discord channel. - - **Default:** `A new Teleport Request from **{username}**!` + Parts of the message between `{` and `}` are replaced by the Chat Bot at runtime; do not change them. + + `{message}` is replaced with the message text, `{username}` with the sender's name, and `{timestamp}` with the current date and time. + + For Discord message formatting, refer to [this guide](https://www.writebots.com/discord-text-formatting/). + + - **Type:** `string` + + - **Default:** `{username}: {message}` + + #### `TeleportRequestMessageFormat` + + - **Description:** + + The format used when someone sends you a teleport request. + + Parts of the message between `{` and `}` are replaced by the Chat Bot at runtime; do not change them. + + `{username}` is replaced with the requester's name. + + For Discord message formatting, refer to [this guide](https://www.writebots.com/discord-text-formatting/). + + - **Type:** `string` + + - **Default:** `A new Teleport Request from **{username}**!` + +
+ +## Discord RPC + +- **Description:** + + This Chat Bot shows your current Minecraft session as a Discord Rich Presence status. It displays information like the server address, your health, current dimension, coordinates, gamemode, and how long you have been connected. + +

Warning

+ + **Discord RPC uses a local IPC socket to communicate with the Discord client. MCC and Discord must be running on the same machine for this to work.** + +
+ +- **Setup:** + + You need a Discord Application ID to use this bot. Here is how to get one: + + 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications/) and click **New Application**. + + 2. Give it a name (this is what shows up in your Discord status, e.g. "Minecraft Console Client"), accept the terms, and click **Create**. + + 3. On the **General Information** page, copy the **Application ID** and paste it into the `ApplicationId` field in your MCC configuration. + + 4. *(Optional)* If you want a custom image in your status, go to the **Rich Presence** tab and click **Art Assets**. Upload an image and give it a name (the **key**). Use that key in the `LargeImageKey` or `SmallImageKey` settings. The default value `mcc_icon` references a built-in MCC icon already registered on the application -- no upload needed if you are happy with that. + + 5. Enable the bot by setting `Enabled` to `true` in your MCC configuration and start MCC with Discord already running. + + Discord updates Rich Presence at most once every 15 seconds regardless of how often MCC sends updates, so you may notice a short delay before your status reflects changes. + +- **Settings:** + + **Section:** **`ChatBot.DiscordRpc`** + +
+ All settings + + #### `Enabled` + + - **Description:** + + This setting specifies if the Discord RPC Chat Bot is enabled. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `false` + + #### `ApplicationId` + + - **Description:** + + Your Discord Application ID. Create one at [discord.com/developers/applications](https://discord.com/developers/applications/). + + - **Type:** `string` + + #### `PresenceDetails` + + - **Description:** + + The top line of the Rich Presence display. Supports placeholders (see below). + + - **Type:** `string` + + - **Default:** `Playing on {server_host}:{server_port}` + + #### `PresenceState` + + - **Description:** + + The second line of the Rich Presence display. Supports placeholders (see below). + + - **Type:** `string` + + - **Default:** `{dimension} - HP: {health}/{max_health}` + + #### `LargeImageKey` + + - **Description:** + + The key of the large image asset uploaded to your Discord application. Leave empty to show no image. + + - **Type:** `string` + + - **Default:** `mcc_icon` + + #### `LargeImageText` + + - **Description:** + + Tooltip text shown when hovering over the large image. Supports placeholders (see below). + + - **Type:** `string` + + - **Default:** `Minecraft Console Client` + + #### `SmallImageKey` + + - **Description:** + + The key of the small image asset uploaded to your Discord application. Leave empty to hide the small image. + + - **Type:** `string` + + - **Default:** *(empty)* + + #### `SmallImageText` + + - **Description:** + + Tooltip text shown when hovering over the small image. Supports placeholders (see below). + + - **Type:** `string` + + - **Default:** *(empty)* + + #### `ShowServerAddress` + + - **Description:** + + Show the server address in the Discord presence. When set to `false`, `{server_host}` and `{server_port}` are replaced with `Hidden` and `****`. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowCoordinates` + + - **Description:** + + Show your coordinates in the Discord presence. When set to `false`, `{x}`, `{y}`, and `{z}` are replaced with `?`. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowHealth` + + - **Description:** + + Show health and food level in the Discord presence. When set to `false`, `{health}`, `{max_health}`, and `{food}` are replaced with `?`. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowDimension` + + - **Description:** + + Show the current dimension in the Discord presence. When set to `false`, `{dimension}` is replaced with `Hidden`. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowGamemode` + + - **Description:** + + Show the current gamemode in the Discord presence. When set to `false`, `{gamemode}` is replaced with `Hidden`. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowElapsedTime` + + - **Description:** + + Show how long you have been connected to the server as an elapsed time in the Discord presence. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `ShowPlayerCount` + + - **Description:** + + Show the number of online players as a party size in the Discord presence. + + - **Available values:** `true` and `false`. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `UpdateIntervalSeconds` + + - **Description:** + + How often (in seconds) to refresh the Discord presence. Minimum value is `1`. + + Note: Discord itself only accepts presence updates once every 15 seconds, so setting this lower than `15` has no visible effect on the Discord side. + + - **Type:** `integer` + + - **Default:** `10` + + ______________________________________________________________________ + + #### Placeholders + + The following placeholders can be used in `PresenceDetails`, `PresenceState`, `LargeImageText`, and `SmallImageText`: + + | Placeholder | Description | + | ---------------- | ------------------------------------------------------------------------------- | + | `{server_host}` | Server hostname (masked if `ShowServerAddress` is `false`) | + | `{server_port}` | Server port (masked if `ShowServerAddress` is `false`) | + | `{username}` | Your Minecraft username | + | `{health}` | Current health (masked if `ShowHealth` is `false`) | + | `{max_health}` | Maximum health, always `20` (masked if `ShowHealth` is `false`) | + | `{food}` | Current food level (masked if `ShowHealth` is `false`) | + | `{dimension}` | Current dimension name, e.g. `Overworld` (masked if `ShowDimension` is `false`) | + | `{gamemode}` | Current gamemode, e.g. `Survival` (masked if `ShowGamemode` is `false`) | + | `{x}` | X coordinate (masked if `ShowCoordinates` is `false`) | + | `{y}` | Y coordinate (masked if `ShowCoordinates` is `false`) | + | `{z}` | Z coordinate (masked if `ShowCoordinates` is `false`) | + | `{player_count}` | Number of players currently online | + | `{protocol}` | Minecraft protocol version number | + +
## Farmer -

Tip

+

Note

**You need to have [Terrain And Movements](configuration.md#terrainandmovements) and [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this bot to work.** @@ -1543,1103 +1985,1529 @@ redirectFrom:

Warning

-**This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues with it and you should treat it as an experimental bot.** +**This bot is still experimental, has some known issues, and should be treated with extra caution on legacy versions.**
-- **Description:** +- **Description:** - This bot can farm crops for you. - When you start it it will plant, break and bonemeal crops in order. + This bot can farm crops for you. + When you start it it will plant, break and bonemeal crops in order. - Supported crops: + Supported crops: - - Beetroot - - Carrot - - Melon - - Netherwart - - Pumpkin - - Potato - - Wheat + - Beetroot + - Carrot + - Melon + - Netherwart + - Pumpkin + - Potato + - Wheat - **Current list of issues:** + Beetroot farming requires Minecraft `1.9+`. - - Sometimes the bot will not bone meal carrots/potatoes or melon/pumpkin stems (you will see it in a pattern of crops that have not been bonemealed) - - Sometimes the bot can jump on to the crops and break the farmland when coming form a different height, it's advised to keep the farming area flat and fenced off so the items to not fly out of the farming area - - If you have a farming platform that is 1 block thick and has air bellow, make it a few blocks thick because the bot can fall through sometimes when logging in and standing on farmland - - Sometimes the bot can be kicked for "invalid movement" packets when farming netherwart on soul sand, we haven't been able to figure why this happens in some parts of the world, while on other it's completely fine, it's advised to keep the farming area small and flat. + **Current list of issues:** - _We're working on solving these issues._ + - Sometimes the bot will not bone meal carrots/potatoes or melon/pumpkin stems (you will see it in a pattern of crops that have not been bonemealed) + - Sometimes the bot can jump on to the crops and break the farmland when coming form a different height, it's advised to keep the farming area flat and fenced off so the items to not fly out of the farming area + - If you have a farming platform that is 1 block thick and has air bellow, make it a few blocks thick because the bot can fall through sometimes when logging in and standing on farmland + - Sometimes the bot can be kicked for "invalid movement" packets when farming netherwart on soul sand, we haven't been able to figure why this happens in some parts of the world, while on other it's completely fine, it's advised to keep the farming area small and flat. - **What the bot does not do as of the time of writing, but are planned features:** + _We're working on solving these issues._ - - Does not collect items which fly off to the side, (it's advised to fence off the farming area with 2 high wall) - - Does not put items to the chest once the inventory is full - - Does not warn you when the inventory is full - - Does not refill inventory with seeds or bonemeal from chests by it self. + **What the bot does not do as of the time of writing, but are planned features:** - > **ℹ️ NOTE: The default radius of scanning is `30` blocks, we suggest that you do not use radius too big because it might slow down the bot. The bigger the radius, the slower the scanning and processing is.** + - Does not collect items which fly off to the side, (it's advised to fence off the farming area with 2 high wall) + - Does not put items to the chest once the inventory is full + - Does not warn you when the inventory is full + - Does not refill inventory with seeds or bonemeal from chests by it self. -- **Commands:** + > **ℹ️ NOTE: The default radius of scanning is `30` blocks, we suggest that you do not use radius too big because it might slow down the bot. The bigger the radius, the slower the scanning and processing is.** - When enabled will add the `/farmer` command. +- **Commands:** - **Usage**: + When enabled will add the `/farmer` command. - ``` - /farmer [radius:] [unsafe:] [teleport:] [debug:]|stop> - ``` + **Usage**: - _Options marked with `[` and `]` are optional and in case of this command can have whatever order you prefer after the `` field._ + ``` + /farmer [radius:] [unsafe:] [teleport:] [debug:]|stop> + ``` - _Options that have `=` means that the value after the `=` is a default value, in case of this command the default radius is 30 blocks._ + _Options marked with `[` and `]` are optional and in case of this command can have whatever order you prefer after the `` field._ - **Examples:** + _Options that have `=` means that the value after the `=` is a default value, in case of this command the default radius is 30 blocks._ - Farming `wheat` in a radius of `40` blocks. + **Examples:** - ``` - /farmer start wheat radius:40 - ``` + Farming `wheat` in a radius of `40` blocks. - Farming `melon` with debug output and direct teleporting: + ``` + /farmer start wheat radius:40 + ``` - ``` - /farmer start melon debug:true teleport:true - ``` + Farming `melon` with debug output and direct teleporting: - Stopping the bot: + ``` + /farmer start melon debug:true teleport:true + ``` - ``` - /farmer stop - ``` + Stopping the bot: -- **Settings:** + ``` + /farmer stop + ``` - **Section:** **`ChatBot.Farmer`** +- **Settings:** - #### `Enabled` + **Section:** **`ChatBot.Farmer`** - - **Description:** +
+ All settings - This setting specifies if the Farmer Chat Bot is enabled. + #### `Enabled` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + This setting specifies if the Farmer Chat Bot is enabled. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `Delay_Between_Tasks` + - **Type:** `boolean` - - **Description:** + - **Default:** `false` - This setting specifies the delay in seconds between each task performed by the bot. + #### `Delay_Between_Tasks` - - **Type:** `integer` + - **Description:** - - **Default:** `1` + This setting specifies the delay in seconds between each task performed by the bot. - - **Minimum:** `1` + - **Type:** `integer` + + - **Default:** `1` + + - **Minimum:** `1` + +
## Follow player -- **Description:** +- **Description:** - This bot enables you to make a bot follow a specific player. + This bot enables you to make a bot follow a specific player. -

Tip

+

Note

- **The bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you, it's similar to making animals follow you when you're holding food in your hand. This is due to a slow pathfinding algorithm, we're working on getting a better one. You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, this might clog the thread for terrain handling) and thus slow the bot even more.** + **The bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you, it's similar to making animals follow you when you're holding food in your hand. This is due to a slow pathfinding algorithm, we're working on getting a better one. You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, this might clog the thread for terrain handling) and thus slow the bot even more.** -
+
-

Tip

+

Note

- **You need to have [terrainandmovements](configuration.md#terrainandmovements) and [entityhandling](configuration.md#entityhandling) enabled in order for this bot to work.** + **You need to have [terrainandmovements](configuration.md#terrainandmovements) and [entityhandling](configuration.md#entityhandling) enabled in order for this bot to work.** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.FollowPlayer`** + **Section:** **`ChatBot.FollowPlayer`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Follow Player Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Follow Player Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Update_Limit` + - **Default:** `false` - - **Description:** + #### `Update_Limit` - The rate at which the bot does calculations (second). + - **Description:** - You can tweak this if you feel the bot is too slow. + The rate at which the bot does calculations (second). - - **Type:** `float` + You can tweak this if you feel the bot is too slow. - - **Default:** `1.5` + - **Type:** `float` - #### `Stop_At_Distance` + - **Default:** `1.5` - - **Description:** + #### `Stop_At_Distance` - Do not follow the player if he is in the range of `X` blocks (prevents the bot from pushing a player in an infinite loop). + - **Description:** - - **Type:** `float` + Do not follow the player if he is in the range of `X` blocks (prevents the bot from pushing a player in an infinite loop). - - **Default:** `3.0` + - **Type:** `float` + + - **Default:** `3.0` + +
## Hangman -- **Description:** +- **Description:** - Hangman game is one of the first bots ever written for MCC, to demonstrate ChatBot capabilities. + Hangman game is one of the first bots ever written for MCC, to demonstrate ChatBot capabilities. - Create a file with words to guess (examples: [`words-en.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-en.txt), [`words-fr.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-fr.txt)) and set it in config inside the `[Hangman]` section. + Create a file with words to guess (examples: [`words-en.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-en.txt), [`words-fr.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-fr.txt)) and set it in config inside the `[Hangman]` section. - Also set `enabled` to `true`, then, add your username in the `botowners` INI setting, and finally, connect to the server and use `/tell start` to start the game. + Also set `enabled` to `true`, then, add your username in the `botowners` INI setting, and finally, connect to the server and use `/tell start` to start the game. -

Tip

+

Note

- **If the bot does not respond to bot owners, see the [Detecting chat messages](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config#detecting-chat-messages) section.** + **If the bot does not respond to bot owners, see the [Detecting chat messages](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config#detecting-chat-messages) section.** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.HangmanGame`** + **Section:** **`ChatBot.HangmanGame`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Hangman Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Hangman Chat Bot is enabled. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `English` + - **Default:** `false` - - **Description:** + #### `English` - This setting specifies if the Hangman Chat Bot should use English. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Hangman Chat Bot should use English. - - **Default:** `true` + - **Available values:** `true` and `false`. - #### `FileWords_EN` + - **Default:** `true` -

Tip

+ #### `FileWords_EN` - **This settings file is for English and is not created by the default** +

Note

-
+ **This settings file is for English and is not created by the default** - - **Description:** +
- This setting specifies the path to the file which Hangman will use for the list of words, each word is added on a separate line. + - **Description:** - - **Default:** `hangman-en.txt` - - **Example**: [`words-en.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-en.txt) + This setting specifies the path to the file which Hangman will use for the list of words, each word is added on a separate line. - #### `FileWords_FR` + - **Default:** `hangman-en.txt` -

Tip

+ - **Example**: [`words-en.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-en.txt) - **This settings file is for French and is not created by the default** + #### `FileWords_FR` -
+

Note

- - **Description:** + **This settings file is for French and is not created by the default** - This setting is same as the above but for French. +
- - **Default:** `hangman-fr.txt` - - **Example**: [`words-fr.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-fr.txt) + - **Description:** + + This setting is same as the above but for French. + + - **Default:** `hangman-fr.txt` + + - **Example**: [`words-fr.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-fr.txt) + +
## Mailer -- **Description:** +- **Description:** - Relay messages between players and servers, like a mail plugin. + Relay messages between players and servers, like a mail plugin. - This bot can store messages when the recipients are offline, and send them when they join the server. + This bot can store messages when the recipients are offline, and send them when they join the server. - The Mailer bot can store and relay mails much like Essential's `/mail` command. + The Mailer bot can store and relay mails much like Essential's `/mail` command. - - `/tell mail [RECIPIENT] [MESSAGE]`: Save your message for future delivery - - `/tell tellonym [RECIPIENT] [MESSAGE]`: Same, but the recipient will receive an anonymous mail + - `/tell mail [RECIPIENT] [MESSAGE]`: Save your message for future delivery + - `/tell tellonym [RECIPIENT] [MESSAGE]`: Same, but the recipient will receive an anonymous mail - The bot will automatically deliver the mail when the recipient is online. The bot also offers a /mailer command from the MCC command prompt: + The bot will automatically deliver the mail when the recipient is online. The bot also offers a /mailer command from the MCC command prompt: - - `/mailer getmails` + - `/mailer getmails` - Show all mails in the console. + Show all mails in the console. - - `/mailer addignored [NAME]` + - `/mailer addignored [NAME]` - Prevent a specific player from sending mails. + Prevent a specific player from sending mails. - - `/mailer removeignored [NAME]` + - `/mailer removeignored [NAME]` - Lift the mailer restriction for this player. + Lift the mailer restriction for this player. - - `/mailer getignored` + - `/mailer getignored` - Show all ignored players. + Show all ignored players. -

Warning

+

Warning

- **The bot identifies players by their name (Not by UUID!). A nickname plugin or a Minecraft rename may cause mails going to the wrong player! Never write something to the bot you wouldn't say in the normal chat (You have been warned!).** + **The bot identifies players by their name (Not by UUID!). A nickname plugin or a Minecraft rename may cause mails going to the wrong player! Never write something to the bot you wouldn't say in the normal chat (You have been warned!).** -
+
-

Warning

+

Warning

- **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `RemoteControl` only if you trust server admins.** + **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `RemoteControl` only if you trust server admins.** -
+
- **Mailer Network:** + **Mailer Network:** - - The Mailer bot can relay messages between servers. + - The Mailer bot can relay messages between servers. - - To set up a network of two or more bots, launch several instances with the bot activated and the same database. + - To set up a network of two or more bots, launch several instances with the bot activated and the same database. - - If you launch two instances from one .exe they should synchronize automatically to the same file. + - If you launch two instances from one .exe they should synchronize automatically to the same file. -* **Settings:** +* **Settings:** - **Section:** **`ChatBot.Mailer`** + **Section:** **`ChatBot.Mailer`** - #### `Enabled` + #### `Enabled` - - **Description:** + - **Description:** - This setting specifies if the Mailer Chat Bot is enabled. + This setting specifies if the Mailer Chat Bot is enabled. - - **Available values:** `true` and `false`. + - **Available values:** `true` and `false`. - - **Type:** `boolean` + - **Type:** `boolean` - - **Default:** `false` + - **Default:** `false` - #### `DatabaseFile` + #### `DatabaseFile` - - **Description:** + - **Description:** - This setting specifies the path to the file where the Mailer Chat Bot will store the mails. + This setting specifies the path to the file where the Mailer Chat Bot will store the mails. - This file will be auto created by the Mailer Chat Bot. + This file will be auto created by the Mailer Chat Bot. - - **Default:** `MailerDatabase.ini` + - **Default:** `MailerDatabase.ini` - #### `IgnoreListFile` + #### `IgnoreListFile` - - **Description:** + - **Description:** - This setting specifies the path to the file where the Mailer Chat Bot will load people who are to be ignored by the Chat Bot. If you want to prevent someone from using this chat bot, add him in this file by writing his nickname on a new line. + This setting specifies the path to the file where the Mailer Chat Bot will load people who are to be ignored by the Chat Bot. If you want to prevent someone from using this chat bot, add him in this file by writing his nickname on a new line. - This file will be auto created by the Mailer Chat Bot. + This file will be auto created by the Mailer Chat Bot. - - **Default:** `MailerIgnoreList.ini` + - **Default:** `MailerIgnoreList.ini` - #### `PublicInteractions` + #### `PublicInteractions` - - **Description:** + - **Description:** - This setting specifies if the Mailer Chat Bot should be interacted with in the public chat (in addition to private messages). + This setting specifies if the Mailer Chat Bot should be interacted with in the public chat (in addition to private messages). - - **Available values:** `true` and `false`. + - **Available values:** `true` and `false`. - - **Type:** `boolean` + - **Type:** `boolean` - - **Default:** `false` + - **Default:** `false` - #### `MaxMailsPerPlayer` + #### `MaxMailsPerPlayer` - - **Description:** + - **Description:** - This setting specifies how many mails the Mailer Chat Bot should store per player at maximum. + This setting specifies how many mails the Mailer Chat Bot should store per player at maximum. - - **Type:** `integer` + - **Type:** `integer` - - **Default:** `10` + - **Default:** `10` - #### `MaxDatabaseSize` + #### `MaxDatabaseSize` - - **Description:** + - **Description:** - This setting specifies the maximum database file size of Mailer Chat Bot in Kilobytes. + This setting specifies the maximum database file size of Mailer Chat Bot in Kilobytes. - - **Type:** `integer` + - **Type:** `integer` - - **Default:** `10000` (10 MB) + - **Default:** `10000` (10 MB) - #### `MailRetentionDays` + #### `MailRetentionDays` - - **Description:** + - **Description:** - This setting specifies how long should the Mailer Chat Bot save/store messages for (in days). + This setting specifies how long should the Mailer Chat Bot save/store messages for (in days). - - **Type:** `integer` + - **Type:** `integer` - - **Default:** `30` + - **Default:** `30` + +## MCP Server + +- **Description:** + + This lets you control MCC from an AI agent such as Claude Code, Codex, Cursor, OpenCode and others. + + Once enabled, your AI client can connect to the running MCC session and ask it to do things like read chat, check nearby players, move around, inspect inventories, or interact with entities. + + The MCP server starts after MCC joins the game. By default it listens on `http://127.0.0.1:33333/mcp`. + + This bot does not log in to Minecraft on its own. MCC still connects to the server normally first. The MCP part simply gives your AI agent a way to use the session that is already running. + + We recommend protecting it with an auth token, even for local use. + +

Warning

+ + This feature is new and experimental, it still requires a lot of testing and polish. Please leave your feedback on our Discord server. + +
+ +

Warning

+ + The performance of task execution depends on how smart the LLM (Large Language Model) (e.g. Sonnet 4.6, GPT 5.4) and Harness/AI Agent (e.g. Claude Code) are. + +
+ +
+ Recommended harnesses / agents + + - Claude Code + - Codex + - OpenCode + - Cursor + - Open Claw + +
+ +
+ Recommended models + + Model rankings are based on the [Agentic Intelligence Benchmark](https://artificialanalysis.ai/?intelligence=agentic-index). + + **Frontier (best results, higher cost):** + - [Claude Opus 4.6](https://openrouter.ai/anthropic/claude-opus-4.6) + - [GPT 5.4](https://openrouter.ai/openai/gpt-5.4) + - [GLM-5](https://openrouter.ai/z-ai/glm-5) -- cheapest in this tier + + **Open-source (close to frontier, lower cost):** + - [MiMo-V2-Pro](https://openrouter.ai/xiaomi/mimo-v2-pro) + - [MiniMax M2.7](https://openrouter.ai/minimax/minimax-m2.7) -- cheapest in this tier + - [Kimi K2.5](https://openrouter.ai/moonshotai/kimi-k2.5) + + **Budget (good for medium and simpler tasks):** + - [Google Gemini 3 Flash](https://openrouter.ai/google/gemini-3-flash-preview) -- recommended + - NVIDIA Nemotron 3 Super: [free tier](https://openrouter.ai/nvidia/nemotron-3-super-120b-a12b:free) / [paid tier](https://openrouter.ai/nvidia/nemotron-3-super-120b-a12b) / [NVIDIA Build](https://build.nvidia.com/nvidia/nemotron-3-super-120b-a12b) -- free tier may hit rate limits; your data is used for model training + - [DeepSeek V3.2](https://openrouter.ai/deepseek/deepseek-v3.2) + + **Ultra-budget (for very simple tasks):** + - [Google Gemini 3.1 Flash Lite Preview](https://openrouter.ai/google/gemini-3.1-flash-lite-preview) + +
+ +- **Settings:** + + **Section:** **`ChatBot.McpServer`** + +
+ Quick start + + 1. Enable `ChatBot.McpServer`. + 2. Set `Transport.RequireAuthToken = true`. + 3. Export a token before starting MCC: + + ```bash + export MCC_MCP_AUTH_TOKEN="replace-me" + ``` + + 4. Leave `Transport.BindHost = "127.0.0.1"` unless you intentionally want to expose the endpoint to other machines. + 5. Start MCC and join a Minecraft server. + 6. Connect your AI client to `http://127.0.0.1:33333/mcp`. + + The embedded host starts after the bot joins the game, not at process launch. + + Tool availability still depends on MCC runtime support. Movement, inventory, and entity tools need the corresponding MCC features to be available for the current version and session. + +
+ +
+ Recommended config + + ```toml + [ChatBot.McpServer] + Enabled = true + Transport = { BindHost = "127.0.0.1", Port = 33333, Route = "/mcp", RequireAuthToken = true, AuthTokenEnvVar = "MCC_MCP_AUTH_TOKEN" } + Capabilities = { SessionStatus = true, ChatAndCommands = true, Movement = true, Inventory = true, EntityWorld = true } + ``` + + For a local setup, keeping the bind host on `127.0.0.1` is the safest default. + + We recommend enabling bearer-token protection and exporting the token before starting MCC: + + ```bash + export MCC_MCP_AUTH_TOKEN="replace-me" + ``` + +
+ +
+ All settings + + #### `Enabled` + + - **Description:** + + Enables or disables the built-in MCP server bot. + + - **Type:** `boolean` + + - **Default:** `false` + + #### `Transport.BindHost` + + - **Description:** + + Hostname or IP address the embedded HTTP server binds to. + + Use `127.0.0.1` for local-only access. Binding to `0.0.0.0` exposes the endpoint to your network. + + - **Type:** `string` + + - **Default:** `127.0.0.1` + + #### `Transport.Port` + + - **Description:** + + TCP port used by the embedded HTTP MCP server. + + - **Type:** `integer` + + - **Default:** `33333` + + #### `Transport.Route` + + - **Description:** + + HTTP path used by the MCP endpoint. + + - **Type:** `string` + + - **Default:** `/mcp` + + #### `Transport.RequireAuthToken` + + - **Description:** + + Require an `Authorization: Bearer ...` header before clients can use the server. + + Recommended: `true` + + - **Type:** `boolean` + + - **Default:** `false` + + #### `Transport.AuthTokenEnvVar` + + - **Description:** + + Environment variable name MCC reads when bearer-token auth is enabled. + + - **Type:** `string` + + - **Default:** `MCC_MCP_AUTH_TOKEN` + + #### `Capabilities.SessionStatus` + + - **Description:** + + Enables session, server, chat-history, event-history, reference, and status-reporting tools. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `Capabilities.ChatAndCommands` + + - **Description:** + + Enables chat sending, internal command execution, respawn, disconnect, quit, and related direct-control tools. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `Capabilities.Movement` + + - **Description:** + + Enables movement, path preview, look, reachability, and nearby block/player queries. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `Capabilities.Inventory` + + - **Description:** + + Enables inventory snapshots, container open/close, window actions, item selection, item use, drop, deposit, and withdraw tools. + + - **Type:** `boolean` + + - **Default:** `true` + + #### `Capabilities.EntityWorld` + + - **Description:** + + Enables entity queries, sign search, block lookup, nearby dropped-item listing, and item pickup helpers. + + - **Type:** `boolean` + + - **Default:** `true` + +
+ + The client examples below assume the default local endpoint `http://127.0.0.1:33333/mcp`. + + The recommended setup is to keep `RequireAuthToken = true`. If you temporarily disable auth for local testing, you can omit the auth-header parts. + +
+ Claude Code + + Official docs: [Claude Code MCP](https://code.claude.com/docs/en/mcp) + + These commands assume the default local MCC endpoint: + + ```bash + claude mcp add --transport http mcc http://127.0.0.1:33333/mcp + claude mcp list + ``` + + If you enabled bearer-token auth in MCC: + + ```bash + claude mcp add --transport http mcc http://127.0.0.1:33333/mcp \ + --header "Authorization: Bearer $MCC_MCP_AUTH_TOKEN" + ``` + + You can check the server status inside Claude Code with `/mcp`. + +
+ +
+ Codex + + Official docs: [OpenAI Docs MCP quickstart](https://developers.openai.com/learn/docs-mcp) + + Add the MCC server with the Codex CLI: + + ```bash + codex mcp add mcc --url http://127.0.0.1:33333/mcp + codex mcp list + ``` + + You can also place it directly in `~/.codex/config.toml`: + + ```toml + [mcp_servers.mcc] + url = "http://127.0.0.1:33333/mcp" + ``` + + If your Codex setup supports custom MCP headers, add the same `Authorization: Bearer ...` header you use in the other examples. + +
+ +
+ Cursor + + Official docs: [Cursor MCP docs](https://docs.cursor.com/en/tools/mcp) + + Create `~/.cursor/mcp.json` and add: + + ```json + { + "mcpServers": { + "mcc": { + "url": "http://127.0.0.1:33333/mcp" + } + } + } + ``` + + Restart Cursor after saving the file. + + If your Cursor setup uses auth headers for remote MCP servers, add `Authorization: Bearer ...` with your MCC token. + +
+ +
+ OpenCode + + Official docs: [OpenCode MCP servers](https://opencode.ai/docs/mcp-servers) + + Put this in `~/.config/opencode/opencode.json` for a global setup, or in `opencode.json` in your project root: + + ```json + { + "$schema": "https://opencode.ai/config.json", + "mcp": { + "mcc": { + "type": "remote", + "url": "http://127.0.0.1:33333/mcp", + "enabled": true + } + } + } + ``` + + If you enabled bearer-token auth in MCC, add headers like this: + + ```json + { + "$schema": "https://opencode.ai/config.json", + "mcp": { + "mcc": { + "type": "remote", + "url": "http://127.0.0.1:33333/mcp", + "enabled": true, + "oauth": false, + "headers": { + "Authorization": "Bearer {env:MCC_MCP_AUTH_TOKEN}" + } + } + } + } + ``` + +
+ +
+ Available MCP tools + + These are the tools currently exposed by `ChatBot.McpServer`. + + **Session, status, and reference** + + - `mcc_session_status`: Get the current MCC session state and feature availability. + - `mcc_server_info`: Get the active server connection info and current TPS. + - `mcc_player_state`: Get the current controlled-player state. + - `mcc_world_state`: Get world state, chunk loading progress, and last observed time and weather values. + - `mcc_chunk_status`: Check chunk loading status for the player location or a target coordinate. + - `mcc_player_stats`: Get player stats, orientation, and current location. + - `mcc_status_effects`: List active player status effects. + - `mcc_loaded_bots`: List loaded built-in bots and scripts. + - `mcc_internal_commands_list`: List available MCC internal commands. + - `mcc_agent_guidance`: Return the MCC MCP operator prompt bundle for external agents. + - `mcc_materials_list`: List known MCC material names. + - `mcc_block_types_list`: List known MCC block types. + - `mcc_entity_types_list`: List known MCC entity types. + - `mcc_world_block_at`: Get block information at a world coordinate. + + **Observed state** + + - `mcc_recent_events`: Read recent high-signal runtime events such as weather, titles, death, respawn, inventory open/close, and joins/leaves. + - `mcc_chat_history`: Read recent chat and system lines seen by MCC. + - `mcc_players_list`: List online players known to MCC. + - `mcc_players_detailed`: List tracked players with UUID, latency, gamemode, and coordinates when available. + + **Chat and direct control** + + - `mcc_send_chat`: Send chat text or a slash command to the server. + - `mcc_quit_client`: Quit MCC cleanly. + - `mcc_disconnect`: Disconnect MCC from the server without quitting the process. + - `mcc_respawn`: Respawn the controlled player when dead. + - `mcc_run_internal_command`: Run an MCC internal command. + - `mcc_animation`: Play a hand-swing animation. + - `mcc_toggle_sneak`: Start or stop sneaking. + - `mcc_toggle_sprint`: Start or stop sprinting. + - `mcc_change_hotbar_slot`: Change the active hotbar slot. + - `mcc_select_item`: Select a hotbar item by item type. + - `mcc_use_item_on_hand`: Use the currently held item. + - `mcc_use_item_on_block`: Use the held item on a target block. + - `mcc_dig_block`: Dig a block at a target location. + - `mcc_place_block`: Place the currently held block or item at a target location. + + **Movement, view, and nearby queries** + + - `mcc_raycast_block`: Raycast from the player's view and return the first non-air block hit. + - `mcc_path_preview`: Compute a path without moving there. + - `mcc_block_scan`: Scan nearby blocks around the player. + - `mcc_blocks_find`: Find nearby blocks by name, type, or ID. + - `mcc_player_nearby`: Check whether a player is nearby. + - `mcc_player_locate`: Locate a tracked player by name. + - `mcc_entity_nearest`: Find the nearest tracked entity that matches the requested filters. + - `mcc_can_reach_position`: Check whether MCC can path to a coordinate without moving there. + - `mcc_move_to`: Path to a world coordinate and verify arrival. + - `mcc_move_to_player`: Locate a tracked player, move to them, and verify arrival. + - `mcc_look_at`: Rotate the view toward a world coordinate. + - `mcc_look_direction`: Rotate the view to a cardinal direction or straight up/down. + - `mcc_look_angles`: Rotate the view to explicit yaw and pitch angles. + + **Inventory and container workflows** + + - `mcc_inventory_snapshot`: Read a snapshot of one inventory. + - `mcc_inventory_search`: Search the player inventory and optionally open containers for matching items. + - `mcc_inventories_list`: List open inventories and containers known to MCC. + - `mcc_container_open_at`: Open a container block at world coordinates and wait for its inventory. + - `mcc_container_close`: Close an open non-player container. + - `mcc_inventory_window_action`: Perform a window action on an inventory slot. + - `mcc_inventory_drop_item`: Drop an exact item count from an inventory. + - `mcc_container_deposit_item`: Move items from the player inventory into an open container. + - `mcc_container_withdraw_item`: Move items from an open container into the player inventory. + + **Entities, signs, and dropped items** + + - `mcc_entities_query`: Query tracked entities. + - `mcc_entities_list`: List tracked entities with optional filters. + - `mcc_entity_info`: Get detailed info for one tracked entity. + - `mcc_entity_interact`: Interact with a tracked entity. + - `mcc_entity_attack`: Attack a tracked entity. + - `mcc_signs_find`: Find nearby signs by text. + - `mcc_items_list`: List nearby dropped item entities. + - `mcc_items_pickup`: Move to and pick up nearby dropped items of a given type. + +
+ +
+ Simple example usage + + Once your client is connected, keep the prompts simple and concrete. + + - `What server is MCC connected to right now?` + - `List online players and tell me whether Steve is nearby.` + - `Preview a path to 10 80 0. If the path looks valid, move there.` + - `Show my player inventory, then open the chest at 2 80 0 and tell me what is inside.` + - `List nearby dropped Stone items and pick them up.` + - `Find the nearest ArmorStand and show me its details.` + +
## Map -- **Description:** +- **Description:** - This Chat Bot allows you to render items maps in the console, to `.bmp` images and to relay them to Discord using the [Discord Bridge](#discord-bridge) Chat Bot. + This Chat Bot allows you to render items maps in the console, to `.bmp` images and to relay them to Discord using the [Discord Bridge](#discord-bridge) Chat Bot. - This is useful for solving captchas on servers which require it, or saving the map art into an image. + This is useful for solving captchas on servers which require it, or saving the map art into an image. - The maps are **rendered** into `Rendered_Maps` folder which will be auto created in the same folder where the client executable is located. + The maps are **rendered** into `Rendered_Maps` folder which will be auto created in the same folder where the client executable is located. -- **Commands:** +- **Commands:** - When enabled will add the `/maps` command. + When enabled will add the `/maps` command. - **Usage**: + **Usage**: - ``` - /maps > | maps > - ``` + ``` + /maps > | maps > + ``` -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.Map`** + **Section:** **`ChatBot.Map`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Map Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Map Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Render_In_Console` + - **Default:** `false` - - **Description:** + #### `Render_In_Console` - This setting specifies if the Map Chat Bot should render the map in the console. + - **Description:** - It is recommended to use something like Power Shell for the best map quality (at least for Windows users). + This setting specifies if the Map Chat Bot should render the map in the console. - - **Available values:** `true` and `false`. + It is recommended to use something like Power Shell for the best map quality (at least for Windows users). - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `true` + - **Type:** `boolean` - #### `Save_To_File` + - **Default:** `true` -

Warning

+ #### `Save_To_File` - **If you want the Discord relay feature, you must enable this setting!** +

Warning

-
+ **If you want the Discord relay feature, you must enable this setting!** - - **Description:** +
- This setting specifies if the Map Chat Bot should render the map and save it into a file (`.bmp` format) + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Map Chat Bot should render the map and save it into a file (`.bmp` format) - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Auto_Render_On_Update` + - **Default:** `false` -

Warning

+ #### `Auto_Render_On_Update` - **On some versions older than 1.17 this could cause some performance issue on older hardware if there a lot of maps being rendered, since map updates are sent multiple times a second. Be careful.** +

Warning

-
+ **On some versions older than 1.17 this could cause some performance issue on older hardware if there a lot of maps being rendered, since map updates are sent multiple times a second. Be careful.** - - **Description:** +
- This setting specifies if the Map Chat Bot should automatically render maps as they're received from the servers. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Map Chat Bot should automatically render maps as they're received from the servers. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Delete_All_On_Unload` + - **Default:** `false` - - **Description:** + #### `Delete_All_On_Unload` - This setting specifies if the Map Chat Bot should automatically delete rendered maps when un-loaded or reloaded. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Map Chat Bot should automatically delete rendered maps when un-loaded or reloaded. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `true` + - **Type:** `boolean` - #### `Notify_On_First_Update` + - **Default:** `true` - - **Description:** + #### `Notify_On_First_Update` - This setting specifies if the Map Chat Bot should notify you when it got a map from the server for the first time. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Map Chat Bot should notify you when it got a map from the server for the first time. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Rasize_Rendered_Image` + - **Default:** `false` -

Tip

+ #### `Resize_Rendered_Image` - **The bigger the size, the less is the quality.** +

Note

-
+ **The bigger the size, the less is the quality.** -

Tip

+
- **For upscaling your maps you could use (getting a bit better quality): https://deepai.org/machine-learning-model/torch-srgan** +

Tip

-
+ **For upscaling your maps you could use (getting a bit better quality): https://deepai.org/machine-learning-model/torch-srgan** - - **Description:** +
- This setting specifies if the Map Chat Bot should resize the rendered image (the one that is saved to a file). + - **Description:** - This is useful if you're relying map images to Discord via the [Discord Bridge](#discord-bridge) Chat Bot. + This setting specifies if the Map Chat Bot should resize the rendered image (the one that is saved to a file). - The default map size is `128x128`. + This is useful if you're relying map images to Discord via the [Discord Bridge](#discord-bridge) Chat Bot. - - **Available values:** `true` and `false`. + The default map size is `128x128`. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Resize_To` + - **Default:** `false` -

Tip

+ #### `Resize_To` - **Might be a bit slow on less powerful systems when rendering a lot of maps. Lower down the resolution if you have any performance issues. If your system is not that powerful and can't handle it, use external tools for upscaling and resizing.** +

Note

-
+ **Might be a bit slow on less powerful systems when rendering a lot of maps. Lower down the resolution if you have any performance issues. If your system is not that powerful and can't handle it, use external tools for upscaling and resizing.** - - **Description:** +
- Which size the map should be resized to if `Rasize_Rendered_Image` is `true`. + - **Description:** + Which size the map should be resized to if `Rasize_Rendered_Image` is `true`. - - **Type:** `integer` + - **Type:** `integer` - - **Default:** `512` + - **Default:** `512` - #### `Send_Rendered_To_Discord` + #### `Send_Rendered_To_Discord` -

Warning

+

Warning

- **The [Discord Bridge](#discord-bridge) Chat Bot must be enabled and configured!** + **The [Discord Bridge](#discord-bridge) Chat Bot must be enabled and configured!** -
+
-

Warning

+

Warning

- **You need to enable `Save_To_File` in order for this to work.** + **You need to enable `Save_To_File` in order for this to work.** -
+
-

Tip

+

Note

- **Sometimes when the client connects, the [Discord Bridge](#discord-bridge) will be loaded a tiny bit after. Rendered map images are queued up and sent in order as soon as the [Discord Bridge](#discord-bridge) is ready and connected.** + **Sometimes when the client connects, the [Discord Bridge](#discord-bridge) will be loaded a tiny bit after. Rendered map images are queued up and sent in order as soon as the [Discord Bridge](#discord-bridge) is ready and connected.** -
+
- - **Description:** + - **Description:** - Send a rendered map (saved to a file) to a Discord channel via the [Discord Bridge](#discord-bridge) Chat Bot. + Send a rendered map (saved to a file) to a Discord channel via the [Discord Bridge](#discord-bridge) Chat Bot. + - **Type:** `boolean` - - **Type:** `boolean` + - **Default:** `false` - - **Default:** `false` +
## PlayerList Logger -- **Description:** - Log the list of players periodically into a textual file. +- **Description:** -- **Settings:** + Log the list of players periodically into a textual file. - **Section:** **`ChatBot.PlayerListLogger`** +- **Settings:** - #### `Enabled` + **Section:** **`ChatBot.PlayerListLogger`** - - **Description:** +
+ All settings - This setting specifies if the PlayerList Logger Chat Bot is enabled. + #### `Enabled` - - **Available values:** `true` and `false`. + - **Description:** - - **Default:** `false` + This setting specifies if the PlayerList Logger Chat Bot is enabled. - #### `File` + - **Available values:** `true` and `false`. - - **Description:** + - **Default:** `false` - This setting specifies the name of the player list Log file that will be created. + #### `File` - - **Default:** `playerlog.txt` + - **Description:** - #### `Delay` + This setting specifies the name of the player list Log file that will be created. - - **Description:** + - **Default:** `playerlog.txt` - Save the list of players every how many seconds. + #### `Delay` - - **Type:** `float` + - **Description:** - - **Default:** `60.0` + Save the list of players every how many seconds. + + - **Type:** `float` + + - **Default:** `60.0` + +
## Remote Control -- **Description:** +- **Description:** - Send MCC console commands to your bot through server PMs (`/tell`). + Send MCC console commands to your bot through server PMs (`/tell`). - You need to have [ChatFormat](configuration.md#chat-format) working correctly and add yourself in [botowners](configuration.md#botowners) to use the bot. + You need to have [ChatFormat](configuration.md#chat-format) working correctly and add yourself in [botowners](configuration.md#botowners) to use the bot. -

Warning

+

Warning

- **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `RemoteControl` only if you trust server admins.** + **Server admins can spoof PMs (`/tellraw`, `/nick`) so enable `RemoteControl` only if you trust server admins.** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.RemoteControl`** + **Section:** **`ChatBot.RemoteControl`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Remote Control Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Remote Control Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `AutoTpaccept` + - **Default:** `false` - - **Description:** + #### `AutoTpaccept` - This setting specifies if the Remote Control Chat Bot should automatically accept teleport requests. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Remote Control Chat Bot should automatically accept teleport requests. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `true` + - **Type:** `boolean` - #### `AutoTpaccept_Everyone` + - **Default:** `true` - - **Description:** + #### `AutoTpaccept_Everyone` - This setting specifies if the Remote Control Chat Bot should automatically accept teleport requests from everyone. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Remote Control Chat Bot should automatically accept teleport requests from everyone. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` + + - **Default:** `false` + +
## Replay Capture -- **Description:** +- **Description:** - Enable recording of the game (`/replay start`) and replay it later using the Replay Mod (https://www.replaymod.com/). + Enable automatic recording of the game and replay it later using the Replay Mod (https://www.replaymod.com/). -

Warning

+ Use `/replay save` to create a snapshot replay file while recording, and `/replay stop` to finalize the active recording. - **This bot does not work for 1.19, we need maintainers for it.** +

Warning

-
+ **Use `/replay stop` or exit MCC gracefully with `/quit` so the replay file can be finalized cleanly.** -

Tip

+
- **Please note that due to technical limitations, the client player (you) will not be shown in the replay file** +

Note

-
+ **Please note that due to technical limitations, the client player (you) will not be shown in the replay file** -

Warning

+
- **You SHOULD use `/replay stop` or exit the program gracefully with `/quit` OR THE REPLAY FILE MAY GET CORRUPT!** +

Note

-
+ **Each MCC instance uses its own temporary replay cache, so multiple MCC clients can record from the same folder without overwriting each other.** -- **Settings:** +
- **Section:** **`ChatBot.ReplayCapture`** +- **Settings:** - #### `Enabled` + **Section:** **`ChatBot.ReplayCapture`** - - **Description:** +
+ All settings - This setting specifies if the Replay Mod Chat Bot is enabled. + #### `Enabled` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + This setting specifies if the Replay Mod Chat Bot is enabled. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `Backup_Interval` + - **Type:** `boolean` - - **Description:** + - **Default:** `false` - This setting specifies the time interval in seconds when the replay file should be auto-saved. + #### `Backup_Interval` - Use `-1` to disable. + - **Description:** - - **Type:** `float` + This setting specifies the time interval in seconds when the replay file should be auto-saved. - - **Default:** `300.0` + Use `-1` to disable. + + - **Type:** `float` + + - **Default:** `300.0` + +
## Script Scheduler -- **Description:** +- **Description:** - Schedule commands and scripts to launch on various events such as server join, date/time or time interval. + Schedule commands and scripts to launch on various events such as server join, date/time or time interval. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.ScriptScheduler`** + **Section:** **`ChatBot.ScriptScheduler`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Script Scheduler Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Script Scheduler Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - ### Defining a task + - **Default:** `false` -

Tip

+ ### Defining a task - **It is recommended that you align subsections to the right by one tab or 4 spaces for better readability.** +

Tip

-
+ **It is recommended that you align subsections to the right by one tab or 4 spaces for better readability.** - - **Description:** +
- Each task is defined as a new subsection `[[ChatBot.ScriptScheduler.TaskList]]` of the section: `[ChatBot.ScriptScheduler]`. + - **Description:** - **Subsection format:** + Each task is defined as a new subsection `[[ChatBot.ScriptScheduler.TaskList]]` of the section: `[ChatBot.ScriptScheduler]`. - ```toml - [[ChatBot.ScriptScheduler.TaskList]] - = - = - ``` - - **Available settings/options:** - - - `Trigger_On_First_Login` - - Will trigger the task when you login the first time. - - **Available values**: `true` and `false` - - **Type**: `boolean` - - - `Trigger_On_Login` - - Will trigger the task each time you login. - - **Available values**: `true` and `false` - - **Type**: `boolean` - - - `Trigger_On_Times` - - This will enable the task to trigger at exact time(s) you want. - - The type of this setting is `inline table`, that has the following sub-settings/options: - - - `Enable` - Enables/Disables the setting (Boolean, so either `true` or `false`) - - - `Times` - An array/list of times on which the task should run/trigger (each element is of the [Local Time](https://toml.io/en/v1.0.0#local-time) type, eg. `14:00:00`, so: `hours:minutes:seconds`) - - **Example**: - - ```toml - Trigger_On_Times = { Enable = true, Times = [ 14:00:00, 22:35:8] } - ``` - - - `Trigger_On_Interval` - - This will enable the task to trigger at certain interval which you've defined. - - The type of this setting is `inline table`, that has the following sub-settings/options: - - - `Enable` - Enables/Disables the setting (Boolean, so either `true` or `false`) - - - `MinTime` - Time in seconds (the type is `double`, eg. `3.14`) - - - `MaxTime` - Time in seconds (the type is `double`, eg. `3.14`) - - **If `MinTime` and `MaxTime` are the same, the interval will be consistent, however if they are not, the ChatBot will generate a random interval in between those two numbers provided, each time the task is run.** - - **Example**: - - ```toml - Trigger_On_Interval = { Enable = true, MinTime = 30.0, MaxTime = 160.0 } - ``` - - ### Full example + **Subsection format:** ```toml - [ChatBot.ScriptScheduler] - Enabled = true - - [[ChatBot.ScriptScheduler.TaskList]] - Task_Name = "Task Name 1" - Trigger_On_First_Login = false - Trigger_On_Login = false - Trigger_On_Times = { Enable = true, Times = [ 14:00:00, ] } - Trigger_On_Interval = { Enable = true, MinTime = 3.6, MaxTime = 4.8 } - Action = "send /hello" - - [[ChatBot.ScriptScheduler.TaskList]] - Task_Name = "Task Name 2" - Trigger_On_First_Login = false - Trigger_On_Login = true - Trigger_On_Times = { Enable = false, Times = [ ] } - Trigger_On_Interval = { Enable = false, MinTime = 1.0, MaxTime = 10.0 } - Action = "send /login pass" + [[ChatBot.ScriptScheduler.TaskList]] + = + = ``` + **Available settings/options:** + + - `Trigger_On_First_Login` + + Will trigger the task when you login the first time. + + **Available values**: `true` and `false` + + **Type**: `boolean` + + - `Trigger_On_Login` + + Will trigger the task each time you login. + + **Available values**: `true` and `false` + + **Type**: `boolean` + + - `Trigger_On_Times` + + This will enable the task to trigger at exact time(s) you want. + + The type of this setting is `inline table`, that has the following sub-settings/options: + + - `Enable` - Enables/Disables the setting (Boolean, so either `true` or `false`) + + - `Times` - An array/list of times on which the task should run/trigger (each element is of the [Local Time](https://toml.io/en/v1.0.0#local-time) type, eg. `14:00:00`, so: `hours:minutes:seconds`) + + **Example**: + + ```toml + Trigger_On_Times = { Enable = true, Times = [ 14:00:00, 22:35:8] } + ``` + + - `Trigger_On_Interval` + + This will enable the task to trigger at certain interval which you've defined. + + The type of this setting is `inline table`, that has the following sub-settings/options: + + - `Enable` - Enables/Disables the setting (Boolean, so either `true` or `false`) + + - `MinTime` - Time in seconds (the type is `double`, eg. `3.14`) + + - `MaxTime` - Time in seconds (the type is `double`, eg. `3.14`) + + **If `MinTime` and `MaxTime` are the same, the interval will be consistent, however if they are not, the ChatBot will generate a random interval in between those two numbers provided, each time the task is run.** + + **Example**: + + ```toml + Trigger_On_Interval = { Enable = true, MinTime = 30.0, MaxTime = 160.0 } + ``` + + ### Full example + + ```toml + [ChatBot.ScriptScheduler] + Enabled = true + + [[ChatBot.ScriptScheduler.TaskList]] + Task_Name = "Task Name 1" + Trigger_On_First_Login = false + Trigger_On_Login = false + Trigger_On_Times = { Enable = true, Times = [ 14:00:00, ] } + Trigger_On_Interval = { Enable = true, MinTime = 3.6, MaxTime = 4.8 } + Action = "send /hello" + + [[ChatBot.ScriptScheduler.TaskList]] + Task_Name = "Task Name 2" + Trigger_On_First_Login = false + Trigger_On_Login = true + Trigger_On_Times = { Enable = false, Times = [ ] } + Trigger_On_Interval = { Enable = false, MinTime = 1.0, MaxTime = 10.0 } + Action = "send /login pass" + ``` + +
+ ## Telegram Bridge -- **Description:** +- **Description:** - This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. + This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. -

Warning

+

Warning

- **You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel.** + **You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel.** -
+
-- **Setup:** +- **Setup:** - 1. First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather - 2. Click on `Start` button and read the bot reply, then type `/newbot`, the Botfather will guide you through the bot creation. - 3. Once you create the bot, copy the **API key** that you have gotten, and put it into the `Token` field of `ChatBot.TelegramBridge` section (this section). - 4. Then launch the client and go to Telegram, find your newly created bot by searching for it with its username, and open a DM with it. - 5. Click on `Start` button and type and send the following command `.chatid` to obtain the chat id. - 6. Copy the chat id number (eg. `2627844670`) and paste it in the `ChannelId` field and add it to the `Authorized_Chat_Ids` field (in this section) (an id in "Authorized_Chat_Ids" field is a number/long, not a string!), then save the file. - Now you can use the bot using it's DM. + 1. First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather + 2. Click on `Start` button and read the bot reply, then type `/newbot`, the Botfather will guide you through the bot creation. + 3. Once you create the bot, copy the **API key** that you have gotten, and put it into the `Token` field of `ChatBot.TelegramBridge` section (this section). + 4. Then launch the client and go to Telegram, find your newly created bot by searching for it with its username, and open a DM with it. + 5. Click on `Start` button and type and send the following command `.chatid` to obtain the chat id. + 6. Copy the chat id number (eg. `2627844670`) and paste it in the `ChannelId` field and add it to the `Authorized_Chat_Ids` field (in this section) (an id in "Authorized_Chat_Ids" field is a number/long, not a string!), then save the file. + Now you can use the bot using it's DM. -

Danger

+

Danger

- **Do not share your API key with anyone else as it will give them the control over your bot. Save it securely.** + **Do not share your API key with anyone else as it will give them the control over your bot. Save it securely.** -
+
-

Danger

+

Danger

- **If you do not add the id of your chat DM with the bot to the "Authorized_Chat_Ids" field, ayone who finds your bot via search will be able to execute commands and send messages!** + **If you do not add the id of your chat DM with the bot to the "Authorized_Chat_Ids" field, ayone who finds your bot via search will be able to execute commands and send messages!** -
+
-

Danger

+

Danger

- **An id pasted in to the "Authorized_Chat_Ids" should be a number/long, not a string!** + **An id pasted in to the "Authorized_Chat_Ids" should be a number/long, not a string!** -
+
-- **Settings:** +- **Settings:** - **Section:** **`ChatBot.TelegramBridge`** + **Section:** **`ChatBot.TelegramBridge`** - #### `Enabled` +
+ All settings - - **Description:** + #### `Enabled` - This setting specifies if the Telegram Bridge Chat Bot is enabled. + - **Description:** - - **Available values:** `true` and `false`. + This setting specifies if the Telegram Bridge Chat Bot is enabled. - - **Type:** `boolean` + - **Available values:** `true` and `false`. - - **Default:** `false` + - **Type:** `boolean` - #### `Token` + - **Default:** `false` - - **Description:** + #### `Token` - Your Telegram Bot token. + - **Description:** - - **Type:** `string` + Your Telegram Bot token. - - **Default:** empty + - **Type:** `string` - #### `ChannelId` + - **Default:** empty - - **Description:** + #### `ChannelId` - An ID of a channel where you want to interact with the MCC using the bot. + - **Description:** - - **Type:** `string` + An ID of a channel where you want to interact with the MCC using the bot. - - **Default:** empty + - **Type:** `string` - #### `Authorized_Chat_Ids` + - **Default:** empty - - **Description:** + #### `Authorized_Chat_Ids` - A list of Chat IDs that are allowed to send messages and execute commands. - To get an id of your chat DM with the bot use `.chatid` bot command in Telegram. + - **Description:** - - **Type:** `array of strings` + A list of Chat IDs that are allowed to send messages and execute commands. + To get an id of your chat DM with the bot use `.chatid` bot command in Telegram. - - **Default:** empty + - **Type:** `array of strings` - #### `Message_Send_Timeout` + - **Default:** empty - - **Description:** + #### `Message_Send_Timeout` - How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second). + - **Description:** - - **Type:** `integer` + How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second). - - **Default:** 3 + - **Type:** `integer` - **Message Formats** + - **Default:** 3 - Words wrapped with `{` and `}` are going to be replaced during the code execution, do not change them! - For example, `{message}` is going to be replace with an actual message, `{username}` will be replaced with an username, `{timestamp}` with the current time. - For Telegram message formatting, check the [following](https://mccteam.github.io/r/tg-fmt.html). + **Message Formats** - #### `PrivateMessageFormat` + Words wrapped with `{` and `}` are going to be replaced during the code execution, do not change them! + For example, `{message}` is going to be replace with an actual message, `{username}` will be replaced with an username, `{timestamp}` with the current time. + For Telegram message formatting, check the [following](https://mccteam.github.io/r/tg-fmt.html). - - **Description:** + #### `PrivateMessageFormat` - A format that is used to display a private chat message on the minecraft server, in a Telegram channel. + - **Description:** - - **Type:** `string` + A format that is used to display a private chat message on the minecraft server, in a Telegram channel. - - **Default:** `*(Private Message)* {username}: {message}` + - **Type:** `string` - #### `PublicMessageFormat` + - **Default:** `*(Private Message)* {username}: {message}` - - **Description:** + #### `PublicMessageFormat` - A format that is used to display a public chat message on the minecraft server, in a Telegram channel. + - **Description:** - - **Type:** `string` + A format that is used to display a public chat message on the minecraft server, in a Telegram channel. - - **Default:** `{username}: {message}` + - **Type:** `string` - #### `TeleportRequestMessageFormat` + - **Default:** `{username}: {message}` - - **Description:** + #### `TeleportRequestMessageFormat` - A format that is used to display a teleport request on the minecraft server, in a Telegram channel. + - **Description:** - - **Type:** `string` + A format that is used to display a teleport request on the minecraft server, in a Telegram channel. - - **Default:** `A new Teleport Request from **{username}**!` + - **Type:** `string` + - **Default:** `A new Teleport Request from **{username}**!` + +
## Items Collector -- **Description:** +- **Description:** - Collect items on the ground using this Chat Bot. + Collect items on the ground using this Chat Bot. -- **Settings:** +- **Settings:** - **Section:** **`ChatBot.ItemsCollector`** + **Section:** **`ChatBot.ItemsCollector`** - #### `Enabled` +
+ All settings - - **Description:** - - This setting specifies if the Items Collector chat bot is enabled. + #### `Enabled` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + This setting specifies if the Items Collector chat bot is enabled. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `Collect_All_Item_Types` + - **Type:** `boolean` - - **Description:** + - **Default:** `false` - Specifies if the bot will collect all items, regardless of their type. - If you want to use the whitelisted item types, disable this by setting it to `false`. + #### `Collect_All_Item_Types` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + Specifies if the bot will collect all items, regardless of their type. + If you want to use the whitelisted item types, disable this by setting it to `false`. - - **Default:** `false` + - **Available values:** `true` and `false`. - #### `Items_Whitelist` + - **Type:** `boolean` - - **Description:** + - **Default:** `false` - In this list you can specify which items the bot will collect. - To enable this, set the `Collect_All_Item_Types` to false. + #### `Items_Whitelist` -

Note

+ - **Description:** - **This does not prevent the bot from accidentally picking up other items, it only goes to positions where it finds the whitelisted items** + In this list you can specify which items the bot will collect. + To enable this, set the `Collect_All_Item_Types` to false. -
+

Note

- - **Available values:** [Item Type List](https://raw.githubusercontent.com/MCCTeam/Minecraft-Console-Client/master/MinecraftClient/Inventory/ItemType.cs) + **This does not prevent the bot from accidentally picking up other items, it only goes to positions where it finds the whitelisted items** - - **Type:** `array of strings with item names` +
- - **Default:** `[ "Diamond", "NetheriteIngot" ]` + - **Available values:** [Item Type List](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs) - #### `Delay_Between_Tasks` + - **Type:** `array of strings with item names` - - **Description:** + - **Default:** `[ "Diamond", "NetheriteIngot" ]` - Delay in milliseconds between bot scanning items (Recommended: 300-500) + #### `Delay_Between_Tasks` - - **Type:** `integer` + - **Description:** - - **Default:** `300` + Delay in milliseconds between bot scanning items (Recommended: 300-500) - #### `Collection_Radius` + - **Type:** `integer` - - **Description:** + - **Default:** `300` - The radius of blocks in which bot will look for items to collect. + #### `Collection_Radius` - - **Type:** `double` + - **Description:** - - **Default:** `30.0` + The radius of blocks in which bot will look for items to collect. - #### `Always_Return_To_Start` + - **Type:** `double` - - **Description:** + - **Default:** `30.0` - Specifies if the bot will return to it's starting position after there are no items to collect. + #### `Always_Return_To_Start` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + Specifies if the bot will return to it's starting position after there are no items to collect. - - **Default:** `true` + - **Available values:** `true` and `false`. - #### `Prioritize_Clusters` + - **Type:** `boolean` - - **Description:** + - **Default:** `true` - Specifies if the bot will go after clustered items instead for the closest ones. + #### `Prioritize_Clusters` - - **Available values:** `true` and `false`. + - **Description:** - - **Type:** `boolean` + Specifies if the bot will go after clustered items instead for the closest ones. - - **Default:** `true` + - **Available values:** `true` and `false`. + - **Type:** `boolean` -## WebSocket Chat Bot + - **Default:** `true` -- **Description:** - - This chat bot allows you to remotely execute commands on the MCC and make Chat Bots in other programming languages over Web Socket. - - You can make your own library to do this, or use the reference implementation one which has been writen in TypeScript/JavaScript: [MCC.js](https://github.com/milutinke/MCC.js) - - If you want to write your own library, you can follow this guide on the protocol specification and avaliable events and commands: [WebSocket Chat Bot Guide](websocket/README.md) - -- **Settings:** - - **Section:** **`ChatBot.WebSocketBot`** - - #### `Enabled` - - - **Description:** - - This setting specifies if the Web Socket chat bot is enabled. - - - **Available values:** `true` and `false`. - - - **Type:** `boolean` - - - **Default:** `false` - - #### `Ip` - - - **Description:** - - The IP address that Websocket server will be bound to. - - - **Type:** `string` - - - **Default:** `127.0.0.1` (localhost) - - #### `Port` - - - **Description:** - - The Port that Websocket server will be bound to. - - - **Type:** `number` - - - **Default:** `8043` - - #### `Password` - - - **Description:** - - A password that will be used to authenticate on thw Websocket server - - **It is recommended to change the default password and to set a strong one** - - - **Type:** `string` - - - **Default:** `wspass12345` - - #### `DebugMode` - - - **Description:** - - This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions. - - - **Type:** `boolean` - - - **Default:** `false` \ No newline at end of file +
diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index b3a67299..4a1b594b 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -1,40 +1,34 @@ --- title: Configuration -redirectFrom: - - "/g/conf/index.html" - - "/g/conf.html" +redirectFrom: + - /g/conf/index.html + - /g/conf.html --- # Configuration -**Minecraft Console Client** can be both configured by the [command line parameters](usage.md#command-line-parameters) and the configuration file. +**Minecraft Console Client** can be configured through both [command-line parameters](usage.md#command-line-parameters) and the configuration file. -By the default all of the configurations are stored in the configuration file named `MinecraftClient.ini` which is created the first time you run the program, but you also can specify your own configuration file by providing a path to it as a first parameter when starting the MCC, check out [Usage](usage.md#quick-usage-of-mcc-with-examples) for examples. - -

Warning

- -**Recently we have changed the configuration format from INI to TOML, the documentation had to be updated. If you spot a mistake, please report it on our Discord or in the repository as an issue.** - -
+By default, MCC stores its settings in `MinecraftClient.ini`, which is created the first time you run the program. You can also pass a custom configuration file path as the first argument when starting MCC. See [Usage](usage.md#quick-usage-of-mcc-with-examples) for examples. ## Notes -- Some settings will be omitted from the documentation due to them being not used often, we do not want documentation to be cluttered, we advise you to manually read through the configuration file, where every setting has a description next to it. -- Some plugin/bot related settings will be covered in the plugins section, not here +- Some less common settings are not repeated here. The generated config file contains inline descriptions for every setting. +- Bot-specific settings are documented in [Chat Bots](chat-bots.md). ## Configuration File ### Format -The configuration file uses the [TOML format](https://toml.io/en/), all of the options are key-value pairs separated into sections. +The configuration file uses the [TOML format](https://toml.io/en/). Options are key-value pairs grouped into sections. -Sections are defined in-between the square brackets (Example: `[This is a section]`), each occurrence of this marks a beginning of a new section. +Sections are defined between square brackets, for example `[This is a section]`. -The settings/options are defined as key-value pairs, where the name of the setting and the value are separated by the equals sign `=` (Example: `some-setting=some value`). +Settings are written as key-value pairs, with the key and value separated by `=`, for example `some-setting = "some value"`. Lines starting with `#` are comments, they do not have an effect on the configuration of the program, their purpose is purely a descriptive one. -**To get familiar with all the data types and styles of settings please read the [official TOML documenation](https://toml.io/en/v1.0.0).** +**For the full syntax and data types, see the [official TOML documentation](https://toml.io/en/v1.0.0).** Full Example: @@ -52,353 +46,544 @@ Section_Enabled = true colors = [ "red", "yellow", "green" ] [ThirdSection.Subsection] -Coordinate = { x = 145, y = 64, y = 2045 } +Coordinate = { x = 145, y = 64, z = 2045 } ``` ## Main Section ### Main General section -- **Section header:** `Main.General` +- **Section header:** `Main.General` + +
+Account, Server, and Authentication settings #### `Account` -- **Description:** +- **Description:** - This setting is where you need to provide your in-game name (for offline accounts) or email for Microsoft accounts (Mojang accounts do not work anymore) and your password (if using an offline account, use `-` for the password). + This setting is where you provide your account login information. -- **Format:** + For **Microsoft accounts**, set `Login` to your Microsoft email. You do not need to provide a password because MCC uses the OAuth 2.0 device code flow for authentication (you sign in through your browser, with full 2FA support). - `Account = { Login = "", Password = "" }` + For **offline accounts**, set `Login` to your desired in-game name and `Password` to `-`. -- **Type:** `inline table` + For **Yggdrasil accounts**, set `Login` and `Password` to the credentials for your authlib server. -- **Example:** +- **Format:** - `Account = { Login = "some.random.player@gmail.com", Password = "myEpicPassword123" }` + `Account = { Login = "" }` + +- **Type:** `inline table` + +- **Examples:** + + Microsoft account (password not needed): + + ``` + Account = { Login = "player@example.com" } + ``` + + Offline account: + + ``` + Account = { Login = "Steve", Password = "-" } + ``` #### `Server` -- **Description:** +- **Description:** - This is the setting where you provide the address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically) + This is the setting where you provide the address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically) - Host can also fill in the nickname of the server in the "Server List" below. + Host can also fill in the nickname of the server in the "Server List" below. -- **Format:** `Server = { Host = "", Port = }` +- **Format:** `Server = { Host = "", Port = }` -- **Type:** `inline table` +- **Type:** `inline table` -- **Example:** +- **Example:** - ``` - Server = { Host = "mysupercoolserver.com" } - ``` + ``` + Server = { Host = "mysupercoolserver.com" } + ``` - ``` - Server = { Host = "192.168.1.27", Port = 12345 } - ``` + ``` + Server = { Host = "192.168.1.27", Port = 12345 } + ``` - ``` - Server = { Host = "ServerAlias1" } - ``` + ``` + Server = { Host = "ServerAlias1" } + ``` #### `AccountType` -- **Description:** +- **Description:** - This setting is where you define the type of your account: `mojang` or `microsoft` + This setting defines the account type: `mojang`, `microsoft`, or `yggdrasil`. -

Tip

+

Note

- **Mojang accounts are going to stop working soon for everyone, they already are not working for some people.** + **Use `microsoft` for normal Microsoft accounts. `yggdrasil` is for custom authlib/Yggdrasil servers.** -
+
-- **Type:** `string` +- **Type:** `string` -- **Default:** `microsoft` +- **Default:** `microsoft` -- **Example:** +- **Example:** - ``` - AccountType = "microsoft" - ``` + ``` + AccountType = "microsoft" + ``` #### `Method` -- **Description:** +- **Description:** - This setting is where you define the way you will sign in with your Microsoft account, available options are `mcc` and `browser`. + This setting is where you define the way you will sign in with your Microsoft account, available options are `mcc` and `browser`. The `mcc` method uses the OAuth 2.0 device code flow: MCC will display a code and a URL, and you complete the sign-in (including 2FA) in your browser. The `browser` method opens a sign-in page in your browser and you paste the resulting code back into MCC. -- **Type:** `string` +- **Type:** `string` -- **Default:** `mcc` +- **Default:** `mcc` -- **Example:** +- **Example:** - ``` - Method = "mcc" - ``` + ``` + Method = "mcc" + ``` + +#### `AuthServer` + +- **Description:** + + This subsection is used when `AccountType` is set to `yggdrasil`. It points MCC at the authlib/Yggdrasil server used for login, session checks, and profile key requests. + + MCC now writes this as a dedicated TOML subsection instead of an inline table: + + ```toml + [Main.General.AuthServer] + ``` + + `Host` accepts either a plain host name or a `host:port` pair. If you include the port there, MCC updates `Port` to match. + + `AuthlibInjectorAPIPath` defaults to `/api/yggdrasil`. Change it if your authlib-injector server uses a different prefix, such as `/authlib-injector`. + + `UseHttps` defaults to `true`. Set it to `false` if your local or development auth server only exposes plain HTTP. + +- **Type:** `section` + +- **Default:** + + ```toml + [Main.General.AuthServer] + Port = 443 + AuthlibInjectorAPIPath = "/api/yggdrasil" + UseHttps = true + Host = "" + ``` + +- **Example:** + + ``` + [Main.General.AuthServer] + Host = "auth.example.com" + Port = 443 + AuthlibInjectorAPIPath = "/api/yggdrasil" + UseHttps = true + ``` + + ``` + [Main.General.AuthServer] + Host = "127.0.0.1" + Port = 25585 + AuthlibInjectorAPIPath = "/authlib-injector" + UseHttps = false + ``` + +#### `AuthUser` + +- **Description:** + + This setting allows for Yggdrasil authlib multi-user selection. It selects which profile MCC should use when the authlib/Yggdrasil server returns multiple available profiles. Leave it empty to pick the profile interactively. + +- **Type:** `string` + +- **Default:** `""` + +- **Example:** + + ``` + AuthUser = "SomePlayer" + ``` + +
### Main Advanced section -- **Section header:** `Main.Advanced` +- **Section header:** `Main.Advanced` + +
+Advanced settings (Language, Version, Features, and more) #### `Language` -- **Description:** +- **Description:** - This setting is where you define which language you want to use. + This setting is where you define which language you want to use. - When connecting to 1.6+ servers, you will need a translation file to display properly some chat messages.These files describe how some messages should be printed depending on your preferred language. + When connecting to 1.6+ servers, you will need a translation file to display properly some chat messages.These files describe how some messages should be printed depending on your preferred language. - The client will automatically load `en_GB.lang` from your Minecraft folder if Minecraft is installed on your computer, or download it from Mojang's servers. You may choose another language in the configuration file. + The client will automatically load `en_GB.lang` from your Minecraft folder if Minecraft is installed on your computer, or download it from Mojang's servers. You may choose another language in the configuration file. - To find your language code, check [this link](https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239s). + To find your language code, check [this list](https://mccteam.github.io/r/l-code.html). -- **Type:** `string` +- **Type:** `string` -- **Default:** `en_gb` +- **Default:** `en_us` -- **Example:** +- **Example:** - ``` - Language = "en_gb" - ``` + ``` + Language = "en_us" + ``` + +#### `EnableSentry` + +- **Description:** + + Set this to `false` to opt out of Sentry error reporting. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `LoadMccTranslation` + +- **Description:** + + Set this to `false` to keep MCC in English even when translated strings are available. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `LoadResourcePackTranslations` + +- **Description:** + + Set this to `false` to ignore translations provided by server resource packs. When enabled, MCC caches extracted resource-pack translation data locally so future joins can reuse it without downloading the pack again. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `LoadForgeModTranslations` + +- **Description:** + + Set this to `true` to load translations from local Forge mod jars for the mod IDs announced by the server. MCC first checks the folder from `ForgeModTranslationPath` when it is set. Otherwise it checks the local `mods` folder, and if `AutoDiscoverForgeModTranslationSources` is enabled it also scans standard launcher folders such as `.minecraft/mods`, Prism Launcher instances, and CurseForge instances. MCC falls back to `en_us` when the selected locale is missing, and caches parsed results by jar hash. + +- **Type:** `boolean` + +- **Default:** `false` + +#### `AutoDiscoverForgeModTranslationSources` + +- **Description:** + + Set this to `false` to stop scanning launcher-managed mod folders outside the current working directory. This setting only matters when `LoadForgeModTranslations` is enabled and `ForgeModTranslationPath` is empty. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `ForgeModTranslationPath` + +- **Description:** + + Optional path to a mods folder. When this is set, MCC loads Forge mod translations from that folder instead of using automatic discovery. + + If automatic discovery does not find the mod you need, copy that mod jar into the configured folder, or into the local `mods` folder next to MCC, and MCC will read translations from there without modifying the jar. + +- **Type:** `string` + +- **Default:** `""` #### `ConsoleTitle` -- **Description:** +- **Description:** - This setting is where you can change the title of the program window if you want to. You can use the variables in it. + This setting is where you can change the title of the program window if you want to. You can use the variables in it. -- **Type:** `string` +- **Type:** `string` -- **Default:** `"%username%@%serverip% - Minecraft Console Client"` +- **Default:** `"%username%@%serverip% - Minecraft Console Client"` -- **Example:** +- **Example:** - ``` - ConsoleTitle = "%username%@%serverip% - Minecraft Console Client" - ``` + ``` + ConsoleTitle = "%username%@%serverip% - Minecraft Console Client" + ``` #### `InternalCmdChar` -- **Description:** +- **Description:** - This setting is where you can change the prefix character of internal MCC commands. + This setting is where you can change the prefix character of internal MCC commands. - Available options: + Available options: - - `none` - - `slash` - - `backslash` + - `none` + - `slash` + - `backslash` -- **Type:** `string` +- **Type:** `string` -- **Default:** `slash` +- **Default:** `slash` -- **Example:** +- **Example:** - ``` - InternalCmdChar = "slash" - ``` + ``` + InternalCmdChar = "slash" + ``` #### `MessageCooldown` -- **Description:** +- **Description:** - This setting is where you can change the minimum delay in seconds between messages to avoid being kicked for spam. + This setting is where you can change the minimum delay in seconds between messages to avoid being kicked for spam. -- **Type:** `float` +- **Type:** `float` -- **Default:** `1.0` +- **Default:** `1.0` + +#### `MaxChatMessageLength` + +- **Description:** + + Overrides the maximum chat message length. By default, MCC caps messages at 100 characters on Minecraft 1.10 and below, and 256 characters on 1.11 and above. Set to `0` to keep the default. + + Some servers (like Hypixel on 1.8) accept messages longer than the vanilla protocol default for that version. This setting lets you match whatever limit the server actually allows. + +

Warning

+ + **Setting this to a value the server doesn't support may get you kicked. Only change it if you know the server accepts longer messages than the version default.** + +
+ +- **Type:** `integer` + +- **Default:** `0` + +- **Range:** `0` - `32767` #### `BotOwners` -- **Description:** +- **Description:** - This setting is where you can set the owners of the bots/client which can be used by some plugins. The names are separated as strings within an array, separated by commas. + This setting is where you can set the owners of the bots/client which can be used by some plugins. The names are separated as strings within an array, separated by commas. -- **Format:** +- **Format:** - ``` - BotOwners = [ "", "", ... ] - ``` + ``` + BotOwners = [ "", "", ... ] + ``` -- **Type:** `array of strings` +- **Type:** `array of strings` -- **Default:** `[ "Player1", "Player2", ]` +- **Default:** `[ "Player1", "Player2", ]` -- **Example:** +- **Example:** - ``` - BotOwners = [ "milutinke", "bradbyte", "BruceChen", ] - ``` + ``` + BotOwners = [ "milutinke", "bradbyte", "BruceChen", ] + ``` -

Warning

+

Warning

- **Admins can impersonate players on versions older than 1.19** - -
+ **Admins can impersonate players on versions older than 1.19** +
#### `MinecraftVersion` -- **Description:** +- **Description:** - This setting is where you can set the version you are playing on. + This setting is where you can set the version you are playing on. -- **Format:** `MinecraftVersion = ""` +- **Format:** `MinecraftVersion = ""` -- **Type:** `string` +- **Type:** `string` -- **Version format:** `1.X.X` +- **Version format:** `1.X.X` -- **Type:** `string` +- **Type:** `string` -- **Default:** `auto` +- **Default:** `auto` -- **Example:** +- **Example:** - ``` - MinecraftVersion = "1.18.2" - ``` + ``` + MinecraftVersion = "1.18.2" + ``` -

Tip

+

Note

- **MCC supports only 1.4.6 - 1.19.2** + **Current code support is `1.4.6` through `26.1`.** -
+
#### `EnableForge` -- **Description:** +- **Description:** - This setting is where you can define if you're playing on a forge server. + This setting is where you can define if you're playing on a forge server. -- **Type:** `string` +- **Type:** `string` -- **Available options:** +- **Available options:** - - `auto` - - `no` - - `force` + - `auto` + - `no` + - `force` -- **Default:** `auto` +- **Default:** `no` -

Tip

+

Note

- **Force-enabling only works for MC 1.13 +** + **Force-enabling only works for MC 1.13 +** -
+
#### `BrandInfo` -- **Description:** +- **Description:** - This setting is where you can change how MCC identifies itself to the server. It can be whatever you like, example: `vanilla`, `mcc`, `empty`. + This setting is where you can change how MCC identifies itself to the server. It can be whatever you like, example: `vanilla`, `mcc`, `empty`. -- **Type:** `string` +- **Type:** `string` -- **Default:** `mcc` +- **Default:** `mcc` -

Tip

+

Note

- **For playing on Hypixel you need to use `vanilla`** + **For playing on Hypixel you need to use `vanilla`** -
+
#### `ChatbotLogFile` -- **Description:** +- **Description:** - This setting is where you can set the path to the file which will contain the logs, leave empty for no log file. + This setting is where you can set the path to the file which will contain the logs, leave empty for no log file. -- **Type:** `string` +- **Type:** `string` -- **Default:** Empty +- **Default:** Empty -- **Example:** +- **Example:** - ``` - ChatbotLogFile = "my-log.txt" - ``` + ``` + ChatbotLogFile = "my-log.txt" + ``` #### `PrivateMsgsCmdName` -- **Description:** +- **Description:** - The name of the command which is used for remote control of the bot. + The name of the command which is used for remote control of the bot. -- **Type:** `string` +- **Type:** `string` -- **Default:** `tell` +- **Default:** `tell` #### `ShowSystemMessages` -- **Description:** +- **Description:** - This setting is where you can define if you want to see the system messages (example command block outputs) if you're an OP. + This setting is where you can define if you want to see the system messages (example command block outputs) if you're an OP. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `ShowXPBarMessages` -- **Description:** +- **Description:** - This setting is where you can define if you want to see the Boss XP Bar messages. + This setting is where you can define if you want to see the Boss XP Bar messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` - > **Note: Can create a spam if there is a bunch of withers** + > **Note: Can create a spam if there is a bunch of withers** #### `ShowChatLinks` -- **Description:** +- **Description:** - This setting is where you can define if you want to decode links embedded in chat messages and show them in console. + This setting is where you can define if you want to decode links embedded in chat messages and show them in console. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `ShowInventoryLayout` -- **Description:** +- **Description:** - This setting is where you can define if you want to have the MCC show you the inventory in a form of an ASCII art when using the `/inventory` internal command. + This setting is where you can define if you want to have the MCC show you the inventory in a form of an ASCII art when using the `/inventory` internal command. - How it looks like: + How it looks like: - ![ASCII Art here](/images/guide/PlayerInventory.png "ASCII Art here") + ![ASCII Art here](/images/guide/PlayerInventory.png "ASCII Art here") -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` + +#### `ShowEffectMessages` + +- **Description:** + + This setting controls whether MCC prints messages when one of your active effects is gained or expires. + + Set it to `false` if a beacon or another repeated effect source is spamming the console. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `ShowEffectNamesInTUI` + +- **Description:** + + This setting lets you show full effect names and levels in the TUI status bar instead of the compact icon-only effect display. + +- **Type:** `boolean` + +- **Default:** `false` #### `TerrainAndMovements` -- **Description:** +- **Description:** - This setting is where you can set if you want to enable terrain movement, so you can use command like `/move` and some bots. + This setting is where you can set if you want to enable terrain movement, so you can use command like `/move` and some bots. -

Warning

+

Warning

- **This feature is currently not supported in `1.4.6 - 1.6`.** + **This feature is currently not supported in `1.4.6 - 1.6`.** -
+
-- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` -

Tip

+

Note

**Sometimes the latest versions might not support this straight away, since Mojang often makes changes to this.** @@ -406,884 +591,1105 @@ Coordinate = { x = 145, y = 64, y = 2045 } #### `InventoryHandling` -- **Description:** +- **Description:** - This setting is where you can set if you want to enable inventory handling using the `/inventory` command. + This setting is where you can set if you want to enable inventory handling using the `/inventory` command. -

Warning

+

Warning

- **This feature is currently not supported in `1.4.6 - 1.9`. But we are working on getting it supported in 1.8 and 1.9.** + **This feature is currently supported on `1.8+` and is unavailable on `1.4.6 - 1.7.10`.** -
+
-- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `EntityHandling` -- **Description:** +- **Description:** - This setting is where you can set if you want to enable interactions with entities such as players, mobs, minecarts, etc.. + This setting is where you can set if you want to enable interactions with entities such as players, mobs, minecarts, etc.. -

Warning

+

Warning

- **This feature is currently not supported in `1.4.6 - 1.7`.** + **This feature is currently not supported in `1.4.6 - 1.7`.** -
+
-- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` -

Tip

+

Note

- **Sometimes the latest versions might not support this straight away, since Mojang often makes changes to this.** + **Sometimes the latest versions might not support this straight away, since Mojang often makes changes to this.** -
+
#### `SessionCache` -- **Description:** +- **Description:** - This setting is where you can define is you want your session info to be stored on the disk or in memory, or not to be stored (this will make you login every time which will add some time to the process). + This setting is where you can define is you want your session info to be stored on the disk or in memory, or not to be stored (this will make you login every time which will add some time to the process). - You can disable this by using `none`. + You can disable this by using `none`. - The `disk` option will save your login authorization token on the disk, but this can be a bit of a security risk if someone else has access to your folder where you have MCC installed. + The `disk` option will save your login authorization token on the disk, but this can be a bit of a security risk if someone else has access to your folder where you have MCC installed. - The `memory` will last until you close down the program. + The `memory` will last until you close down the program. -- **Type:** `string` +- **Type:** `string` -- **Default:** `disk` +- **Default:** `disk` #### `ProfileKeyCache` -- **Description:** +- **Description:** - Same as `SessionCache` but for your profile keys which are used for chat signing and validation. + Same as `SessionCache` but for your profile keys which are used for chat signing and validation. -- **Type:** `string` +- **Type:** `string` -- **Default:** `disk` +- **Default:** `disk` #### `ResolveSrvRecords` -- **Description:** +- **Description:** - Use `no`, `fast` (5s timeout), or `yes`. + Use `no`, `fast` (5s timeout), or `yes`. - Required for joining some servers. + Required for joining some servers. -- **Type:** `string` +- **Type:** `string` -- **Default:** `fast` +- **Default:** `fast` #### `PlayerHeadAsIcon` -- **Description:** +- **Description:** - This setting allows you to set the icon of the program to be the head of your in-game skin. + This setting allows you to set the icon of the program to be the head of your in-game skin. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` -

Tip

+

Note

- **Only works on Windows XP-8 or Windows 10 with old console** + **Only works on Windows XP-8 or Windows 10 with old console** -
+
#### `ExitOnFailure` -- **Description:** +- **Description:** - This setting allows you to define if your want to disable pauses on error, for using MCC in non-interactive scripts + This setting allows you to define if your want to disable pauses on error, for using MCC in non-interactive scripts -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `CacheScript` -- **Description:** +- **Description:** - This setting allows you to define if your want to have MCC cache compiled scripts for faster load on low-end devices. + This setting allows you to define if your want to have MCC cache compiled scripts for faster load on low-end devices. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `Timestamps` -- **Description:** +- **Description:** - This setting allows you to define if your want to have MCC prepend timestamps to chat messages. + This setting allows you to define if your want to have MCC prepend timestamps to chat messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `AutoRespawn` -- **Description:** +- **Description:** - This setting allows you to define if your want to auto respawn if you die. + This setting allows you to define if your want to auto respawn if you die. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` -

Tip

+

Note

- **Make sure the spawn point is safe** + **Make sure the spawn point is safe** -
+
#### `MinecraftRealms` -- **Description:** +- **Description:** - This setting allows you to define if your want to enable support for joining Minecraft Realms. + This setting allows you to define if your want to enable support for joining Minecraft Realms. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `MoveHeadWhileWalking` -- **Description:** +- **Description:** - This setting allows you to define if your want to enable head movement while walking to avoid anti-cheat triggers + This setting allows you to define if your want to enable head movement while walking to avoid anti-cheat triggers -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `TcpTimeout` -- **Description:** +- **Description:** - This setting allows you to define a custom timeout period in seconds. Use only if you know what you're doing. + This setting allows you to define a custom timeout period in seconds. Use only if you know what you're doing. -- **Type:** `integer` +- **Type:** `integer` -- **Default:** `30` +- **Default:** `30` #### `EnableEmoji` -- **Description:** +- **Description:** - This setting allows you to disable emojis in the [`chunk`](usage.md#chunk) command. + This setting allows you to disable emojis in the [`chunk`](usage.md#chunk) command. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `MovementSpeed` -- **Description:** +- **Description:** - This setting allows you to change the movement speed of the bot. + This setting allows you to change the movement speed of the bot. -- **Type:** `integer` +- **Type:** `integer` -- **Default:** `2` +- **Default:** `2`

Warning

**A movement speed higher than 2 may be considered cheating by some plugins.** +
+ #### `IgnoreInvalidPlayerName` -- **Description:** +- **Description:** - Minecraft player name can only consist of English letters, numbers, and underscore symbols. Other name will be considered as invalid and ignored by default. + Minecraft player name can only consist of English letters, numbers, and underscore symbols. Other name will be considered as invalid and ignored by default. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` -
+
### Account List section -- **Section header:** `Main.Advanced.AccountList` +- **Section header:** `Main.Advanced.AccountList` -- **Description:** +- **Description:** - This section allows you to add multiple accounts so you can switch easily between them on the fly. + This section allows you to add multiple accounts so you can switch easily between them on the fly. -- **Usage examples:** +- **Usage examples:** - `/connect Player1` + `/connect Player1` -- **Type:** `array of inline tables` +- **Type:** `array of inline tables` -- **Format:** +- **Format:** - ```toml - = { Login = "", Password = "" } - ``` + ```toml + = { Login = "", Password = "" } + ``` -- **Examples:** +- **Examples:** - ```toml - Player1 = { Login = "playerone@email.com", Password = "thepassword" } - ``` + ```toml + Player1 = { Login = "playerone@email.com", Password = "thepassword" } + ``` ### Server List section -- **Section header:** `Main.Advanced.ServerList` +- **Section header:** `Main.Advanced.ServerList` -- **Description:** +- **Description:** - This section allows you to add multiple server aliases which enables fast and easy switching between servers. Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. + This section allows you to add multiple server aliases which enables fast and easy switching between servers. Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. -- **Usage examples:** +- **Usage examples:** - `/connect Server2` + `/connect Server2` -- **Type:** `array of inline tables` +- **Type:** `array of inline tables` -- **Format:** +- **Format:** - ```toml - = { Host = "", Port = } - ``` + ```toml + = { Host = "", Port = } + ``` -- **Examples:** +- **Examples:** - ```toml - ServerAlias1 = { Host = "mc.awesomeserver.com" } - ServerAlias2 = { Host = "192.168.1.27", Port = 12345 } - ``` + ```toml + ServerAlias1 = { Host = "mc.awesomeserver.com" } + ServerAlias2 = { Host = "192.168.1.27", Port = 12345 } + ``` ### Signature section -- **Section header:** `Signature` +- **Section header:** `Signature` -- **Description:** +- **Description:** - Affects only Minecraft 1.19+. + Affects only Minecraft 1.19+. - This section contains settings related to a new chat reporting (signing and verifying) feature introduced by Mojang. + This section contains settings related to a new chat reporting (signing and verifying) feature introduced by Mojang. + +
+Chat signing and verification settings #### `LoginWithSecureProfile` -- **Description:** +- **Description:** - Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with `enforce-secure-profile=true` + Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with `enforce-secure-profile=true` -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `SignChat` -- **Description:** +- **Description:** - Whether to sign the chat sent from the MCC. + Whether to sign the chat sent from the MCC. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `SignMessageInCommand` -- **Description:** +- **Description:** - Whether to sign the messages contained in the commands sent by the MCC. + Whether to sign the messages contained in the commands sent by the MCC. - For example, the message in `/msg` and `/me` + For example, the message in `/msg` and `/me` -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `MarkLegallySignedMsg` -- **Description:** +- **Description:** - Use green color block to mark chat with legitimate signatures. + Use green color block to mark chat with legitimate signatures. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `true` #### `MarkModifiedMsg` -- **Description:** +- **Description:** - Use yellow color block to mark chat that have been modified by the server. + Use yellow color block to mark chat that have been modified by the server. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `MarkIllegallySignedMsg` -- **Description:** +- **Description:** - Use red color block to mark chat without legitimate signature. + Use red color block to mark chat without legitimate signature. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `MarkSystemMessage` -- **Description:** +- **Description:** - Use gray color block to mark system message (always without signature). + Use gray color block to mark system message (always without signature). -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `true` #### `ShowModifiedChat` -- **Description:** +- **Description:** - Set to true to display messages modified by the server, false to display the original signed messages. + Set to true to display messages modified by the server, false to display the original signed messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `ShowIllegalSignedChat` -- **Description:** +- **Description:** - Whether to display chat and messages in commands without legal signature. + Whether to display chat and messages in commands without legal signature. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` -### Logging section +
-- **Section header:** `Logging` +### App Vars values section + +- **Section header:** `AppVar.VarStirng` + +
+Logging and filtering settings #### `DebugMessages` -- **Description:** +- **Description:** - This setting allows you to define if your want to see debug messages while the client is running, this is useful when there is a bug and you want to report a problem, or if you're developing a script/bot and you want to debug it. + This setting allows you to define if your want to see debug messages while the client is running, this is useful when there is a bug and you want to report a problem, or if you're developing a script/bot and you want to debug it. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `ChatMessages` -- **Description:** +- **Description:** - This setting allows you to define if your want to see chat messages. + This setting allows you to define if your want to see chat messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `InfoMessages` -- **Description:** +- **Description:** - This setting allows you to define if your want to see info messages. + This setting allows you to define if your want to see info messages. - Most of the messages from MCC. + Most of the messages from MCC. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `WarningMessages` -- **Description:** +- **Description:** - This setting allows you to define if your want to see warning messages. + This setting allows you to define if your want to see warning messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `ErrorMessages` -- **Description:** +- **Description:** - This setting allows you to define if your want to see error messages. + This setting allows you to define if your want to see error messages. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `ChatFilterRegex` -- **Description:** +- **Description:** - This setting allows you to define if your want to filter chat messages being logged using a Regex expression. + This setting allows you to define if your want to filter chat messages being logged using a Regex expression. - More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). + More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). -- **Type:** `string` +- **Type:** `string` -- **Default:** `.*` +- **Default:** `.*` -

Tip

+

Note

- **Not filtering anything by default** + **Not filtering anything by default** -
+
#### `DebugFilterRegex` -- **Description:** +- **Description:** - This setting allows you to define if your want to filter debug messages being logged using a Regex expression. + This setting allows you to define if your want to filter debug messages being logged using a Regex expression. - More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). + More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). -- **Type:** `string` +- **Type:** `string` -- **Default:** `.*` +- **Default:** `.*` -

Tip

+

Note

- **Not filtering anything by default** + **Not filtering anything by default** -
+
#### `FilterMode` -- **Description:** +- **Description:** - Can be `disable`, `blacklist` or `whitelist` + Can be `disable`, `blacklist` or `whitelist` - "disable" will disable the filter, `blacklist` hides the messages, while the `whitelist` shows the messages that match the Regex expression that you've defined. + "disable" will disable the filter, `blacklist` hides the messages, while the `whitelist` shows the messages that match the Regex expression that you've defined. -- **Type:** `string` +- **Type:** `string` -- **Default:** `disable` +- **Default:** `disable` #### `LogToFile` -- **Description:** +- **Description:** - This setting allows you to define if your want to log messages to a file. + This setting allows you to define if your want to log messages to a file. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `LogFile` -- **Description:** +- **Description:** - This setting allows you to define a path to a file where you want to log messages if you have enabled logging to a file with `LogToFile = true`. + This setting allows you to define a path to a file where you want to log messages if you have enabled logging to a file with `LogToFile = true`. -- **Type:** `string` +- **Type:** `string` -- **Default:** `console-log.txt` +- **Default:** `console-log.txt` -

Tip

+

Note

- **%username% and %serverip% will be substituted with your username and the IP address of the server you are connected to. So you can use something like: `console-log-%username%-%serverip%.txt`** + **%username% and %serverip% will be substituted with your username and the IP address of the server you are connected to. So you can use something like: `console-log-%username%-%serverip%.txt`** -
+
#### `PrependTimestamp` -- **Description:** +- **Description:** - This setting allows you to define if your want prepend timestamps to messages that are written to the log file. + This setting allows you to define if your want prepend timestamps to messages that are written to the log file. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `SaveColorCodes` -- **Description:** +- **Description:** - This setting allows you to define if your want keep the server color codes in the logged messages. + This setting allows you to define if your want keep the server color codes in the logged messages. - Example of a color coded message: `§bsome message` + Example of a color coded message: `§bsome message` -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` + +
## App Vars section -- **Section header:** `AppVar` +- **Section header:** `AppVar` -- **Description:** +- **Description:** - This section allows you to define your own custom settings/variables which you can use in scripts, bots or other setting fields. + This section allows you to define your own custom settings/variables which you can use in scripts, bots or other setting fields. - To define a variable/setting, simply make a new line with the following format under the `[AppVar.VarStirng]` section: + To define a variable/setting, simply make a new line with the following format under the `[AppVar.VarStirng]` section: -

Tip

+

Note

- **`%username%`, `%serverip%`, `%datetime%` are reserved variables** + **`%username%`, `%login%`, `%serverip%`, `%serverport%`, `%datetime%`, `%date%`, `%players%` are reserved read-only variables** -
+
-- **Section header:** `Logging` +- **Section header:** `AppVar.VarStirng` -- **Examples:** +- **Examples:** - ``` - your_var = "your_value" - "your var 2" = "your value 2" - ``` + ``` + your_var = "your_value" + "your var 2" = "your value 2" + ``` + +## Console section + +- **Section header:** `Console` + +- **Description:** + + Console-related settings for input handling and command suggestions. + +### Console General section + +- **Section header:** `Console.General` + +
+Console display settings + +#### `ConsoleColorMode` + +- **Description:** + + Use `disable`, `legacy_4bit`, `vt100_4bit`, `vt100_8bit`, or `vt100_24bit`. + + If the terminal shows garbled escape sequences like `←[0m`, try `legacy_4bit` or disable color output. + +- **Type:** `string` + +- **Default:** `vt100_24bit` + +#### `Display_Input` + +- **Description:** + + Set this to `false` if you do not want MCC to echo the current input line while typing. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `Display_Chat` + +- **Description:** + + Set this to `false` if you want MCC to keep receiving chat without printing it in the console. + + This only affects console output. It does not stop bots from receiving chat, and it does not turn off chat file logging. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `History_Input_Records` + +- **Description:** + + Maximum number of remembered console input lines. + +- **Type:** `integer` + +- **Default:** `32` + +
+ +### Console CommandSuggestion section + +- **Section header:** `Console.CommandSuggestion` + +- **Description:** + + Command completion suggestions in the console. + +
+Command suggestion settings + +#### `Enable` + +- **Description:** + + Set this to `false` to disable command completion suggestions. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `Enable_Color` + +- **Description:** + + Enables colored suggestions when the terminal color mode supports it. + +- **Type:** `boolean` + +- **Default:** `true` + +#### `Use_Basic_Arrow` + +- **Description:** + + Use this if the suggestion arrows are not displayed correctly in your terminal. + +- **Type:** `boolean` + +- **Default:** `false` + +#### `Max_Suggestion_Width` + +- **Description:** + + Maximum width of the suggestion popup. + +- **Type:** `integer` + +- **Default:** `30` + +### Console TabList section + +- **Section header:** `Console.TabList` + +- **Description:** + + Settings for the `/tab` command and the live tab overlay in TUI mode. + +
+Tab list settings + +#### `ShowTeams` + +- **Description:** + + Show a separate team column in `/tab` output. + + This is disabled by default so `/tab` stays closer to the in-game player list and keeps the output compact. Team formatting still applies to player names even when the extra column is hidden. + + When enabled, MCC shows the team display name when the server provides one. If the server only sends an internal team identifier, MCC hides that noise instead of printing a raw UUID-like value. + +- **Type:** `boolean` + +- **Default:** `false` + +- **Example:** + + ```toml + [Console.TabList] + ShowTeams = true + ``` + +
+ +#### `Max_Displayed_Suggestions` + +- **Description:** + + Maximum number of suggestions shown at once. + +- **Type:** `integer` + +- **Default:** `6` + +#### Color fields + +- **Description:** + + The suggestion text, tooltip, and arrow colors are stored as hex color strings such as `#f8fafc`. + + MCC validates these values on startup and falls back to built-in defaults if a color string is invalid. + +
## Proxy section -- **Section header:** `Proxy` +- **Section header:** `Proxy` -- **Description:** +- **Description:** - Connect to a server via a proxy instead of connecting directly. + Connect to a server via a proxy instead of connecting directly. + +
+Proxy settings #### `Enabled_Login` -- **Description:** +- **Description:** - If Mojang session services or Microsoft login services are blocked on your network or your ip is blacklisted or rate limited by Microsoft, set the value to `true`. + If Mojang session services or Microsoft login services are blocked on your network or your ip is blacklisted or rate limited by Microsoft, set the value to `true`. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` + +#### `Enabled_Update` + +- **Description:** + + Use the proxy when MCC checks for updates. + +- **Type:** `boolean` + +- **Default:** `false` #### `Enabled_Ingame` -- **Description:** +- **Description:** - Whether to connect to the game server through a proxy. + Whether to connect to the game server through a proxy. - If connecting to a port 25565 (Minecraft) is blocked on your network, set the value to `true` to login and connect using the proxy. + If connecting to a port 25565 (Minecraft) is blocked on your network, set the value to `true` to login and connect using the proxy. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` -

Warning

+

Warning

- **Make sure your server rules allow Proxies or VPNs before setting the setting to `true`, or you may face consequences!** + **Make sure your server rules allow Proxies or VPNs before setting the setting to `true`, or you may face consequences!** -
+
#### `Server` -- **Description:** +- **Description:** - The proxy server IP and port. + The proxy server IP and port. - Proxy server must allow HTTPS for login, and non-443 ports for playing. + Proxy server must allow HTTPS for login, and non-443 ports for playing. -- **Format:** +- **Format:** - ``` - Server = { Host = "", Port = } - ``` + ``` + Server = { Host = "", Port = } + ``` -- **Default:** `{ Host = "0.0.0.0", Port = 8080 }` +- **Default:** `{ Host = "0.0.0.0", Port = 8080 }` #### `Proxy_Type` -- **Description:** +- **Description:** - The type of your proxy. + The type of your proxy. - Available options: + Available options: - - `HTTPT` - - `SOCKS4` - - `SOCKS4a` - - `SOCKS5` + - `HTTP` + - `SOCKS4` + - `SOCKS4a` + - `SOCKS5` -- **Type:** `string` +- **Type:** `string` -- **Default:** `HTTPT` +- **Default:** `HTTP` #### `Username` -- **Description:** +- **Description:** - The proxy account username. + The proxy account username. - Only needed for password protected proxies. + Only needed for password protected proxies. -- **Default:** `` `` +- **Default:** ` ` #### `Password` -- **Description:** +- **Description:** - The proxy account password. + The proxy account password. - Only needed for password protected proxies. + Only needed for password protected proxies. -- **Default:** `` `` +- **Default:** ` ` + +
## MCSettings section -- **Section header:** `MCSettings` +- **Section header:** `MCSettings` -- **Description:** +- **Description:** - Client settings related to language, render distance, difficulty, chat and skins. + Client settings related to language, render distance, difficulty, chat and skins. + +
+Game client settings #### `Enabled` -- **Description:** +- **Description:** - This setting allows you to specify if you want to use settings from this section. + This setting allows you to specify if you want to use settings from this section. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `Locale` -- **Description:** +- **Description:** - Use any language implemented in Minecraft + Use any language implemented in Minecraft -- **Type:** `string` +- **Type:** `string` -- **Default:** `en_US` +- **Default:** `en_US` #### `RenderDistance` -- **Description:** +- **Description:** - Render distance in chunks: `0 - 255` + Render distance in chunks: `0 - 255` -- **Type:** `integer` +- **Type:** `integer` -- **Default:** `8` +- **Default:** `8` #### `Difficulty` -- **Description:** +- **Description:** - Available options: + Available options: - - `peaceful` - - `easy` - - `normal` - - `difficult` + - `peaceful` + - `easy` + - `normal` + - `difficult` -- **Type:** `string` +- **Type:** `string` -- **Default:** `normal` +- **Default:** `peaceful` #### `ChatMode` -- **Description:** +- **Description:** - This setting allows you to effectively mute yourself. + This setting allows you to effectively mute yourself. - Available options: + Available options: - - `enabled` (You can chat) - - `commands` (You can only do commands) - - `disabled` + - `enabled` (You can chat) + - `commands` (You can only do commands) + - `disabled` -- **Type:** `string` +- **Type:** `string` -- **Default:** `enabled` +- **Default:** `enabled` #### `ChatColors` -- **Description:** +- **Description:** - This setting allows you to disable chat colors. + This setting allows you to disable chat colors. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `MainHand` -- **Description:** +- **Description:** - This setting allows you to specify your main hand. + This setting allows you to specify your main hand. -- **Available values:** `right` and `left` +- **Available values:** `right` and `left` -- **Type:** `string` +- **Type:** `string` -- **Default:** `left` +- **Default:** `left` + +
## MCSettings Skin section -- **Section header:** `MCSettings.Skin` +- **Section header:** `MCSettings.Skin` -- **Description:** +- **Description:** - Skin options. + Skin options. + +
+Skin visibility settings #### `Cape` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your skin cape shown. + This setting allows you to specify if you want to have your skin cape shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `Hat` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your skin hat shown. + This setting allows you to specify if you want to have your skin hat shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `Jacket` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your skin jacket shown. + This setting allows you to specify if you want to have your skin jacket shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `Sleeve_Left` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your left sleeve shown. + This setting allows you to specify if you want to have your left sleeve shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `Sleeve_Right` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your right sleeve shown. + This setting allows you to specify if you want to have your right sleeve shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `Pants_Left` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your left part of the pants shown. + This setting allows you to specify if you want to have your left part of the pants shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `Pants_Right` -- **Description:** +- **Description:** - This setting allows you to specify if you want to have your right part of the pants shown. + This setting allows you to specify if you want to have your right part of the pants shown. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` + +
## Chat Format section -- **Section header:** `ChatFormat` +- **Section header:** `ChatFormat` -- **Description:** +- **Description:** - The MCC does it best to detect chat messages, but some server have unusual chat formats. + The MCC does it best to detect chat messages, but some server have unusual chat formats. - When this happens, you'll need to configure the chat format yourself using settings from this section. + When this happens, you'll need to configure the chat format yourself using settings from this section. - The MCC uses Regular Expressions (Regex) to detect the chat formatting, in case that you're not familiar with Regex you can use the following resources to learn it and test it out: + The MCC uses Regular Expressions (Regex) to detect the chat formatting, in case that you're not familiar with Regex you can use the following resources to learn it and test it out: - - Crash courses: - - [Regex video tutorial by Web Dev Simplified](https://www.youtube.com/watch?v=rhzKDrUiJVk) - - [Regex on paper by Crack Concepts](https://www.youtube.com/watch?v=9RksQ5YT7FM) - - In-depth tutorials: + - Crash courses: - - [Quite a long and detailed tutorial by Svetlin Nakov](https://www.youtube.com/watch?v=DS9IO0W7-0Q) - - [Microsoft Documentation on Regex](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) + - [Regex video tutorial by Web Dev Simplified](https://www.youtube.com/watch?v=rhzKDrUiJVk) + - [Regex on paper by Crack Concepts](https://www.youtube.com/watch?v=9RksQ5YT7FM) - - Testing Regex expressions online: - - [https://regex101.com/](https://regex101.com/) - - [https://regexr.com/](https://regexr.com/) + - In-depth tutorials: + + - [Quite a long and detailed tutorial by Svetlin Nakov](https://www.youtube.com/watch?v=DS9IO0W7-0Q) + - [Microsoft Documentation on Regex](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) + + - Testing Regex expressions online: + + - [https://regex101.com/](https://regex101.com/) + - [https://regexr.com/](https://regexr.com/) + +
+Chat format settings #### `Builtins` -- **Description:** +- **Description:** - This setting allows you to define if your want use the default chat formats. + This setting allows you to define if your want use the default chat formats. - Set to `false` to avoid conflicts with custom formats. + Set to `false` to avoid conflicts with custom formats. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `true` +- **Default:** `true` #### `UserDefined` -- **Description:** +- **Description:** - This setting allows you to define if your want to use the custom chat formats defined bellow using Regex. + This setting allows you to define if your want to use the custom chat formats defined bellow using Regex. - Set to `true` to use the custom formats defined in `Public`, `Private` and `TeleportRequest`. + Set to `true` to use the custom formats defined in `Public`, `Private` and `TeleportRequest`. -- **Type:** `boolean` +- **Type:** `boolean` -- **Default:** `false` +- **Default:** `false` #### `Public` -- **Description:** +- **Description:** - This setting allows you to specify a custom chat message format using Regex (Regular expressions). + This setting allows you to specify a custom chat message format using Regex (Regular expressions). - More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). + More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). - Only works when `Builtins` is set to `false`. + Only works when `Builtins` is set to `false`. -- **Type:** `string` +- **Type:** `string` -- **Default:** `Public = "^<([a-zA-Z0-9_]+)> (.+)$"` +- **Default:** `Public = "^<([a-zA-Z0-9_]+)> (.+)$"` #### `Private` -- **Description:** +- **Description:** - This setting allows you to specify a custom chat message format for private messages using Regex (Regular expressions). + This setting allows you to specify a custom chat message format for private messages using Regex (Regular expressions). - More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). + More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). - Only works when `Builtins` is set to `false`. + Only works when `Builtins` is set to `false`. -- **Type:** `string` +- **Type:** `string` -- **Default:** `Private = "^([a-zA-Z0-9_]+) whispers to you: (.+)$"` +- **Default:** `Private = "^([a-zA-Z0-9_]+) whispers to you: (.+)$"` #### `TeleportRequest` -- **Description:** +- **Description:** - This setting allows you to specify a custom chat message format for a Teleport request using Regex (Regular expressions). + This setting allows you to specify a custom chat message format for a Teleport request using Regex (Regular expressions). - More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). + More on Regex [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference). - Only works when `Builtins` is set to `false`. + Only works when `Builtins` is set to `false`. -- **Type:** `string` +- **Type:** `string` -- **Default:** `TeleportRequest = '^([a-zA-Z0-9_]+) has requested (?:to|that you) teleport to (?:you|them)\.$'` +- **Default:** `TeleportRequest = '^([a-zA-Z0-9_]+) has requested (?:to|that you) teleport to (?:you|them)\.$'` + +
+ +## Chat Bot section + +- **Section header:** `ChatBot` + +- **Description:** + + This top-level section groups the built-in bot configs that ship with MCC. + + The detailed options for each bot are documented in [Chat Bots](chat-bots.md), so this page only covers the shared runtime and client settings. diff --git a/docs/guide/contibuting.md b/docs/guide/contibuting.md index 5d4c6711..8a9460f6 100644 --- a/docs/guide/contibuting.md +++ b/docs/guide/contibuting.md @@ -4,22 +4,30 @@ title: Contributing # Contributing -At this moment this page needs to be created. +This page is still being filled in. For now, use the sections below as the current contributor entry points for code, docs, and translation work. -For now you can use our article from the [Git Hub repository Wiki](https://github.com/MCCTeam/Minecraft-Console-Client/wiki/Update-console-client-to-new-version) written by [ReinforceZwei](https://github.com/ReinforceZwei). +If you are doing maintainer-style work with coding agents, start with [AI-Assisted Development](ai-assisted-development.md). It covers the shell setup, local server loop, and the skills in `.skills/`. + +You can also use the guide in the [GitHub repository wiki](https://github.com/MCCTeam/Minecraft-Console-Client/wiki/Update-console-client-to-new-version) written by [ReinforceZwei](https://github.com/ReinforceZwei). + +For now, the project has three main contribution paths: + +- code and bot work in the main MCC client +- documentation updates in `docs/` +- translations through Crowdin ## Translations -To improve translations for MCC, please visit: [Crowdin - Minecraft Console Client](https://crwd.in/minecraft-console-client). +To improve translations for MCC, please visit: [Crowdin - Minecraft Console Client](https://crowdin.com/project/minecraft-console-client). **It is recommended to translate `MCC in-app text` first.** -If you can't find the language you want to translate into, please contact us at Github or Discord to add it. +If you cannot find the language you want to translate into, contact us on GitHub or Discord and we can add it. -Github: https://github.com/MCCTeam/Minecraft-Console-Client +GitHub: https://github.com/MCCTeam/Minecraft-Console-Client Discord: https://discord.gg/9HPr2EE4C4 ## Contributors -[Check out our contributors on Github](https://github.com/MCCTeam/Minecraft-Console-Client/graphs/contributors). +[Check out our contributors on GitHub](https://github.com/MCCTeam/Minecraft-Console-Client/graphs/contributors). diff --git a/docs/guide/creating-bots.md b/docs/guide/creating-bots.md index b5012a3b..076f4600 100644 --- a/docs/guide/creating-bots.md +++ b/docs/guide/creating-bots.md @@ -4,17 +4,18 @@ title: Creating Chat Bots # Creating Chat Bots -- [Notes](#notes) -- [Requirements](#requirements) -- [Quick Introduction](#quick-introduction) -- [Examples](#examples) -- [C# API](#c#-api) +- [Notes](#notes) +- [Requirements](#requirements) +- [Quick Introduction](#quick-introduction) +- [Examples](#examples) +- [AI-Assisted Bot Authoring](#ai-assisted-bot-authoring) +- [C# API](#c#-api) ## Notes -

Tip

+

Note

-**For now this page contains only the bare basics of the Chat Bot API, enough of details to teach you how to make basic Chat Bots. For more details you need to take a look at the [ChatBot.cs](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Scripting/ChatBot.cs) and [Examples](#examples). This page will be improved in the future.** +**This page covers the basics of the Chat Bot API. For the full surface area, read [ChatBot.cs](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Scripting/ChatBot.cs) and the example scripts linked below.**
@@ -22,27 +23,27 @@ title: Creating Chat Bots ## Requirements -- A basic knowledge of C# programming language -- A text editor +- A basic knowledge of C# programming language +- A text editor If you're not familiar with the C# programming language, we suggest taking a look at the following resources: Crash courses: -- [C# Crash Course playlist by Teddy Smit](https://www.youtube.com/watch?v=67oWw9TanOk&list=PL82C6-O4XrHfoN_Y4MwGvJz5BntiL0z0D) +- [C# Crash Course playlist by Teddy Smit](https://www.youtube.com/watch?v=67oWw9TanOk&list=PL82C6-O4XrHfoN_Y4MwGvJz5BntiL0z0D) More in-depth: -- [Learn C# Youtube Playlist by Microsoft](https://www.youtube.com/playlist?list=PLdo4fOcmZ0oVxKLQCHpiUWun7vlJJvUiN) -- [Getting started with C# (An index of tutorials and the documentation) by Microsoft](https://docs.microsoft.com/en-us/dotnet/csharp/) +- [Learn C# YouTube Playlist by Microsoft](https://www.youtube.com/playlist?list=PLdo4fOcmZ0oVxKLQCHpiUWun7vlJJvUiN) +- [Getting started with C# (an index of tutorials and documentation) by Microsoft](https://learn.microsoft.com/en-us/dotnet/csharp/) ## Quick Introduction This introduction assumes that you have the basic knowledge of C#. -

Tip

+

Note

-**Here we will use terms Chat Bot and Script interchangeably** +**In this page, "Chat Bot" and "Script" are used interchangeably.**
@@ -59,8 +60,8 @@ MCC.LoadBot(new ExampleChatBot()); // The code and comments above are defining a "Script Metadata" section -// Every single chat bot (script) must be a class which extends the ChatBot class. -// Your class must be instantiates in the "Script Metadata" section and passed to MCC.LoadBot function. +// Every chat bot script must define a class that extends ChatBot. +// Instantiate that class in the script metadata section and pass it to MCC.LoadBot. class ExampleChatBot : ChatBot { // This method will be called when the script has been initialized for the first time, it's called only once @@ -92,7 +93,7 @@ class ExampleChatBot : ChatBot Start MCC, connect to a server and run the following internal command: `/script ExampleChatBot.cs`. -If you did everything right you should see: `[Example Chat Bot] An example Chat Bot has been initialised!` message appear in your console log. +If everything worked, you should see `[Example Chat Bot] An example Chat Bot has been initialized!` in the console. ### Structure of Chat Bots @@ -111,9 +112,9 @@ Every single Chat Bot (Script) must have this section at the beginning in order `//MCCScript 1.0` marks the beginning of the **Script Metadata** section, this must always be on the first line or the Chat Bot (Script) will not load and will throw an error. -`//MCCScript Extensions` marks the end of the **Script Metadata** section, this must be defined before a Chat Bot (Script) class. +`//MCCScript Extensions` marks the end of the **Script Metadata** section. It must appear before the Chat Bot class. -In order for your Chat Bot (Script) to properly load in-between the `//MCCScript 1.0` and the `//MCCScript Extensions` lines you must instantiate your Chat Bot (Script) class and pass it to the `MCC.LoadBot` function. +To load a Chat Bot script, instantiate the bot class between `//MCCScript 1.0` and `//MCCScript Extensions`, then pass it to `MCC.LoadBot`. Example code: @@ -121,15 +122,15 @@ Example code: MCC.LoadBot(new YourChatBotClassNameHere()); ``` -**Script Metadata** section allows for including C# packages and libraries with: `//using ` and `/dll `. +The **Script Metadata** section also lets you include namespaces and DLL references with `//using ` and `//dll `. -

Tip

+

Note

**Avoid adding whitespace between `//` and keywords**
-By the default the following packages are loaded: +By default, the following namespaces are loaded: ```csharp using System; @@ -167,28 +168,185 @@ MCC.LoadBot(new ExampleChatBot()); ### Chat Bot Class -After the end of the **Script Metadata** section, you basically can define any number of classes you like, the only limitation is that the main class of your Chat Bot (Script) must extend `ChatBot` class. +After the **Script Metadata** section, you can define any number of helper classes. The main bot class must extend `ChatBot`. There are no required methods, everything is optional. -When the Chat Bot (Script) has been initialized for the first time the `Initialize` method will be called. +When the Chat Bot is initialized for the first time, the `Initialize` method is called. -In it you can initialize variables, eg. Dictionaries, etc.. +Use it to initialize state such as dictionaries or cached values. -

Tip

+

Note

**For allocating resources like a database connection, we recommend allocating them in `AfterGameJoined` and freeing them in `OnDisconnect`** -
. +
## Examples -You can find a lot of examples in our Git Hub Repository at [ChatBots](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/ChatBots) and [config](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config). +You can find more examples in the [ChatBots](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/ChatBots) and [config](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config) folders in the GitHub repository. + +## AI-Assisted Bot Authoring + +If you are using an AI coding agent on this repository, use the `mcc-chatbot-authoring` skill for bot work. + +Skill links: + +- [Browse the skill on GitHub](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/.skills/mcc-chatbot-authoring) +- [Download the skill directory](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FMCCTeam%2FMinecraft-Console-Client%2Ftree%2Fmaster%2F.skills%2Fmcc-chatbot-authoring) + +This skill is meant for: + +- standalone `/script` bots +- built-in MCC chat bots +- bot repairs and ports +- event handlers, movement logic, inventory logic, and plugin-channel work + +Its default behavior is important: if you ask for "a bot" without saying otherwise, it should prefer a standalone `//MCCScript` bot loaded with `/script`. It should only choose a built-in bot when you explicitly ask for repo wiring, automatic config loading, or a compiled MCC bot. + +The skill also follows MCC-specific rules, for example: + +- do not send chat from `Initialize()` +- use `AfterGameJoined()` for chat or commands after login +- normalize chat with `GetVerbatim(text)` before `IsChatMessage(...)` or `IsPrivateMessage(...)` +- fully clean up commands, timers, plugin channels, and movement locks + +### Example prompts + +```text +Create a standalone MCC /script bot that watches public chat for the word "auction" and logs matching messages to the console. Use the mcc-chatbot-authoring skill. +``` + +```text +Fix this existing MCC script bot so it stops sending chat from Initialize() and moves the startup command to AfterGameJoined(). Use the mcc-chatbot-authoring skill. +``` + +```text +Make a built-in MCC chat bot named AutoTorch and wire it fully into the repo config and bot registration. Use the mcc-chatbot-authoring skill. +``` + +```text +Create a standalone MCC /script bot that follows private messages, uses GetVerbatim(text), and replies only to bot owners. Use the mcc-chatbot-authoring skill. +``` + +## Achievements And Advancements + +Chat bots and C# scripts can read the current achievement state and react to updates. + +Useful methods: + +- `GetAchievements()` +- `GetUnlockedAchievements()` +- `GetLockedAchievements()` +- `OnAchievementUpdate(IReadOnlyList updated, IReadOnlyList removedIds, bool reset)` + +Things worth knowing: + +- On `1.8` to `1.11.2`, ids use the legacy `achievement.*` format. +- On `1.12+`, ids use advancement resource ids such as `minecraft:story/root`. +- Legacy achievements usually have `Title = null` and `Description = null` because the server does not send display metadata in the statistics packet. +- On newer versions, revoking an advancement may remove it from the current set instead of turning it into a locked entry, so `removedIds` matters. + +Example: + +```csharp +//MCCScript 1.0 + +MCC.LoadBot(new AchievementWatcher()); + +//MCCScript Extensions + +public class AchievementWatcher : ChatBot +{ + public override void AfterGameJoined() + { + Achievement[] known = GetAchievements(); + LogToConsole($"Known achievements: {known.Length}"); + } + + public override void OnAchievementUpdate(IReadOnlyList updated, IReadOnlyList removedIds, bool reset) + { + LogToConsole($"Achievement update: reset={reset}, updated={updated.Count}, removed={removedIds.Count}"); + + foreach (Achievement achievement in updated) + { + string title = achievement.Title ?? achievement.Id; + string state = achievement.IsCompleted ? "done" : "todo"; + LogToConsole($" - {title}: {state}"); + } + + foreach (string removedId in removedIds) + LogToConsole($" - removed: {removedId}"); + } +} +``` + +## Scoreboard teams + +Chat bots and C# scripts can read the current team state and react to team changes. + +Useful methods and events: + +- `GetTeams()` - returns a snapshot of all teams the server has sent +- `GetPlayerTeam(playerName)` - returns the team a specific player is on, or `null` +- `OnTeam(teamName, method, displayName, friendlyFlags, nameTagVisibility, collisionRule, color, prefix, suffix, players)` - called whenever a team packet arrives + +The `method` byte tells you what changed: + +- `0` - team created (includes full parameters and initial member list) +- `1` - team removed +- `2` - team parameters updated (display name, colors, rules) +- `3` - players added to the team +- `4` - players removed from the team + +The `color` field is a `ChatFormatting` enum ordinal. Common values: `0`=black, `9`=blue, `10`=green, `12`=red, `14`=yellow, `-1`=none/reset. + +The `nameTagVisibility` and `collisionRule` strings take values from the Minecraft wiki: `"always"`, `"never"`, `"hideForOtherTeams"`, `"hideForOwnTeam"` (visibility) or `"pushOtherTeams"`, `"pushOwnTeam"` (collision). + +Example: + +```csharp +//MCCScript 1.0 + +MCC.LoadBot(new TeamWatcher()); + +//MCCScript Extensions + +public class TeamWatcher : ChatBot +{ + public override void AfterGameJoined() + { + foreach (var team in GetTeams().Values) + LogToConsole($"Team '{team.Name}' has {team.Members.Count} member(s)"); + } + + public override void OnTeam(string teamName, byte method, string displayName, + byte friendlyFlags, string nameTagVisibility, string collisionRule, + int color, string prefix, string suffix, List players) + { + switch (method) + { + case 0: + LogToConsole($"Team '{teamName}' created with {players.Count} member(s)"); + break; + case 1: + LogToConsole($"Team '{teamName}' removed"); + break; + case 3: + LogToConsole($"{string.Join(", ", players)} joined team '{teamName}'"); + break; + case 4: + LogToConsole($"{string.Join(", ", players)} left team '{teamName}'"); + break; + } + } +} +``` ## C# API -As of the time of writing, the C# API has been changed in forks that are yet to be merged, so for now you can use the [ChatBot.cs](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Scripting/ChatBot.cs) for reference. +The authoritative reference for the C# API is [ChatBot.cs](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Scripting/ChatBot.cs). Each method is well documented with standard C# documentation comments. -In the future we will make a script to auto-generate this section based on the documentation in the code. +This page intentionally stays focused on the basics. For newer hooks and overloads, check the source file directly. diff --git a/docs/guide/creating-text-script.md b/docs/guide/creating-text-script.md index 0c4aa0a9..25140f9d 100644 --- a/docs/guide/creating-text-script.md +++ b/docs/guide/creating-text-script.md @@ -4,13 +4,14 @@ title: Creating Simple Script # Creating Simple Script -A simple script is a text file with one command per line. See [Internal Commands](https://mccteam.github.io/guide/usage.html#internal-commands) section or type `/help` in the console to see available commands. Any line beginning with `#` is ignored and treated as a comment. +A simple script is a text file with one command per line. See the [Internal Commands](usage.md#internal-commands) section, or type `/help` in the console to see the available commands. Any line beginning with `#` is ignored and treated as a comment. -Application variables defined using the set command or [AppVars] INI section can be used. The following read-only variables can also be used: `%username%, %login%, %serverip%, %serverport%, %datetime%` +Application variables defined with the `set` command or in the `[AppVars]` config section can be used. The following read-only variables are also available: `%username%`, `%login%`, `%serverip%`, `%serverport%`, `%datetime%`, `%players%` (`%players%` expands to the current online player names separated by commas, or an empty string when not connected). ## Example `sample-script.txt`: Send a hello message, wait 60 seconds and disconnect from server. + ``` # This is a sample script for Minecraft Console Client # Any line beginning with "#" is ignored and treated as a comment. @@ -21,6 +22,6 @@ send Now quitting. Bye :) exit ``` -Go to [example scripts](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config) to see more example. +See the [example scripts](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config) folder for more examples. -If you want need advanced functions, please see [Creating Chat Bots](creating-bots.md) \ No newline at end of file +If you need more advanced behavior, see [Creating Chat Bots](creating-bots.md). diff --git a/docs/guide/installation.md b/docs/guide/installation.md index cf1db6f0..53877eb6 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -4,42 +4,80 @@ title: Installation # Installation -- [YouTube Tutorials](#youtube-tutorials) -- [Download a compiled binary](#download-a-compiled-binary) -- [Building from the source code](#building-from-the-source-code) -- [Run using Docker](#using-docker) -- [Run on Android](#run-on-android) -- [Run MCC 24/7 on a VPS](#run-on-a-vps) +- [Quick Install (one-liner)](#quick-install) +- [YouTube Tutorials](#youtube-tutorials) +- [Download a compiled binary](#download-a-compiled-binary) +- [Building from the source code](#building-from-the-source-code) +- [Run using Docker](#using-docker) +- [Run on Android](#run-on-android) +- [Run MCC 24/7 on a VPS](#run-on-a-vps) + +## Quick Install + +The quickest way to get MCC is to run the installer script for your platform. It auto-detects your OS and CPU architecture, fetches the latest release from GitHub, and saves the binary to your current directory. + +### Linux / macOS + +Open a terminal in the folder where you want MCC and run: + +```bash +curl -fsSL https://mccteam.github.io/install.sh | sh +``` + +If you prefer `wget`: + +```bash +wget -qO- https://mccteam.github.io/install.sh | sh +``` + +The script downloads `MinecraftClient` and marks it executable. Supported architectures: `x64`, `arm64`, `arm` (Linux only). + +### Windows + +Open **PowerShell** in the folder where you want MCC and run: + +```powershell +iwr -useb https://mccteam.github.io/install.ps1 | iex +``` + +The script downloads `MinecraftClient.exe`. Supported architectures: `x64`, `x86`, `arm64`. + +::: tip +You can also download the scripts directly and inspect them before running: +- Linux/macOS: [install.sh](https://mccteam.github.io/install.sh) +- Windows: [install.ps1](https://mccteam.github.io/install.ps1) +::: ## YouTube Tutorials If you're not the kind of person that likes textual tutorials, our community has made video tutorials available on YouTube. -- [Installation on Windows by Daenges](https://www.youtube.com/watch?v=BkCqOCa2uQw) -- [Installation on Windows + Auto AFK and More by Dexter113](https://www.youtube.com/watch?v=FxJ0KFIHDrY) +- [Installation on Windows by Daenges](https://www.youtube.com/watch?v=BkCqOCa2uQw) +- [Installation on Windows + Auto AFK and More by Dexter113](https://www.youtube.com/watch?v=FxJ0KFIHDrY) ## Download a compiled binary -You can download a compiled binary file of the latest build from our Releases section on Git Hub: [Download](https://github.com/MCCTeam/Minecraft-Console-Client/releases) +You can download a compiled binary of the latest build from the [GitHub Releases](https://github.com/MCCTeam/Minecraft-Console-Client/releases) page. ## Building from the source code -We recommend you to download our precompiled binary file from [GitHub](https://github.com/MCCTeam/Minecraft-Console-Client/releases). +We recommend you to download our precompiled binary file from [GitHub](https://github.com/MCCTeam/Minecraft-Console-Client/releases). However, if you want to build the program from source code, please follow the guide. ### Windows +
+Windows build instructions + Requirements: -- [Git](https://www.git-scm.com/) -- [.NET 7.0 or new-er](https://dotnet.microsoft.com/en-us/download) or [Visual Studio](https://visualstudio.microsoft.com/) configured for C# app development +- [Git](https://www.git-scm.com/) +- [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download) or [Visual Studio](https://visualstudio.microsoft.com/) configured for C# app development -

Tip

- - **If you want to modify the code, and you are new to C# or in programming in general, you might want to watch some C# tutorials, we recommend the ones listed in [Creating Bots](creating-bots.md#requirements) section.** - -
+::: note +If you want to modify the code and you are new to C# or programming in general, the tutorials listed in [Creating Bots](creating-bots.md#requirements) are a good starting point. +::: #### Cloning using Git @@ -48,26 +86,32 @@ Install [Git](https://www.git-scm.com/) 1. Make a new folder where you want to keep the source code 2. Then open it up, hold `SHIFT` and do a `right-click` on the empty white space in the folder 3. Click on `Git Bash Here` in the context menu -4. Clone the [Git Hub Repository](https://github.com/MCCTeam/Minecraft-Console-Client) by typing end executing the following command: +4. Clone the [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client) by running: ```bash git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive ``` +If you cloned the repository without `--recursive`, run: + +```bash +git submodule update --init --recursive +``` + 5. Once the repository has been cloned, you can close the `Git Bash` terminal emulator 6. Open up the new cloned folder #### Download translation resources (optional) -1. Visit [MCC project's homepage on Crowdin](https://crowdin.com/project/minecraft-console-client). -2. You will need to log in to your Crowdin account in order to download. -3. Click on the language you want to download the translation for. -4. Find `MinecraftClient` -> `Resources` -> `Translations` -> `MCC in-app text` -5. Click the button `•••` at the end of the line. -6. Click Download and save the file to folder `/MinecraftClient/Resources/Translations/`. -7. Find `MinecraftClient` -> `Resources` -> `ConfigComments` -> `Comments in the settings file` -8. Click the button `•••` at the end of the line. -9. Click Download and save the file to folder `/MinecraftClient/Resources/ConfigComments/`. +01. Visit [MCC project's homepage on Crowdin](https://crowdin.com/project/minecraft-console-client). +02. You will need to log in to your Crowdin account in order to download. +03. Click on the language you want to download the translation for. +04. Find `MinecraftClient` -> `Resources` -> `Translations` -> `MCC in-app text` +05. Click the button `•••` at the end of the line. +06. Click Download and save the file to folder `/MinecraftClient/Resources/Translations/`. +07. Find `MinecraftClient` -> `Resources` -> `ConfigComments` -> `Comments in the settings file` +08. Click the button `•••` at the end of the line. +09. Click Download and save the file to folder `/MinecraftClient/Resources/ConfigComments/`. 10. Find `MinecraftClient` -> `Resources` -> `AsciiArt` -> `ASCII Arts (Please use fixed-width fonts for editing)` 11. Click the button `•••` at the end of the line. 12. Click Download and save the file to folder `/MinecraftClient/Resources/AsciiArt/`. @@ -83,91 +127,161 @@ git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive 6. Right click on `MinecraftClient` solution in the `Solution Explorer` 7. Click `Build` -If the build has succeeded, the compiled binary `MinecraftClient.exe` will be in `MinecraftClient/bin/Release/net7.0/win-x64/publish` folder. +If the build succeeds, the published binary `MinecraftClient.exe` will be in `MinecraftClient/bin/Release/net10.0/win-x64/publish/`. #### Building using .NET manually without Visual Studio +

Tip

+ +If you are following the AI-assisted repo workflow, use WSL or another Unix-style shell and prefer `source tools/mcc-env.sh` followed by `mcc-build`. That path keeps MCC's session and temp-build helpers enabled. The `dotnet` commands below are the low-level manual fallback. + +
+ 1. Open the `Minecraft-Console-Client` folder you've cloned or downloaded 2. Open the PowerShell (`Right-Click` on the whitespace and click `Open PowerShell`, or in Windows Explorer: `File -> Open PowerShell`) -3. Run the following command to build the project: +3. Install the .NET 10 SDK if you do not already have it. The easiest current option on Windows is: -```bash -dotnet publish MinecraftClient -f net7.0 -r win-x64 --no-self-contained -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None +```powershell +winget install Microsoft.DotNet.SDK.10 ``` -If the build has succeeded, the compiled binary `MinecraftClient.exe` will be in `MinecraftClient/bin/Release/net7.0/win-x64/publish` folder. +4. Run the following command for a normal local build: + +```bash +dotnet build MinecraftClient.sln -c Release +``` + +5. If you want a release-like published binary that matches the repo's CI workflow, run: + +```bash +source tools/mcc-env.sh +mcc-publish --rid win-x64 +``` + +6. Verify the SDK installation if needed: + +```bash +dotnet --info +``` + +If the publish step succeeds, the published binary `MinecraftClient.exe` will be in `MinecraftClient/bin/Release/net10.0/win-x64/publish/`. + +
### Linux, macOS -

Tip

+
+Linux and macOS build instructions -**If you're using Linux we will assume that you should be able to install git on your own. If you don't know how, search it up for your distribution, it should be easy. (Debian based distros: `apt install git`, Arch based: `pacman -S git`)** +

Note

+ +**If you're using Linux we will assume that you should be able to install git on your own. If you don't know how, search it up for your distribution, it should be easy. (Debian based distros: `apt install git`, Arch based: `pacman -S git`)**
Requirements: -- Git +- Git - - Linux: + - Linux: - - [Install Git on macOS](https://git-scm.com/download/mac) + - [Install Git on macOS](https://git-scm.com/download/mac) -- .NET SDK 7.0 or new-er +- .NET 10 SDK - - [Install .NET on Linux](https://docs.microsoft.com/en-us/dotnet/core/install/linux) - - [Install .NET on macOS](https://docs.microsoft.com/en-us/dotnet/core/install/macos) + - [Install .NET on Linux](https://learn.microsoft.com/en-us/dotnet/core/install/linux) + - [Install .NET on Ubuntu](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install) + - [Install .NET on macOS](https://learn.microsoft.com/en-us/dotnet/core/install/macos) #### Cloning using Git 1. Open up a terminal emulator and navigate to the folder where you will store the MCC -2. Recursively clone the [Git Hub Repository](https://github.com/MCCTeam/Minecraft-Console-Client) by typing end executing the following command: +2. Recursively clone the [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client) by running: ```bash git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive ``` 3. Go to the folder you've cloned (should be `Minecraft-Console-Client`) -4. If you want to download translation resources, please check out [Download translation resources](#download-translation-resources-optional) -5. Run the following command to build the project: - - On Linux: +4. Install the .NET 10 SDK. - ```bash - dotnet publish MinecraftClient -f net7.0 -r linux-x64 --no-self-contained -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None - ``` + - On Ubuntu 24.04 LTS, use the built-in Ubuntu package feeds: -

Tip

+ ```bash + sudo apt-get update && \ + sudo apt-get install -y dotnet-sdk-10.0 + ``` - **If you're using Linux that is either ARM, 32-bit, Rhel based, Using Musl, or Tirzen, [find an appropriate RID](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#linux-rids) for your platform and replace the `-r linux-64` with an appropriate `-r RID_NAME` (Example for arm: `-r linux-arm64`)** + - On macOS, the normal path is to use the official installer from the [.NET download page](https://dotnet.microsoft.com/en-us/download). Pick `Arm64` for Apple Silicon and `x64` for Intel Macs. -
+5. If you want to download translation resources, please check out [Download translation resources](#download-translation-resources-optional) - - On macOS: +6. For the repo's normal local development workflow, source the helper environment and build through `mcc-build`: - ```bash - dotnet publish MinecraftClient -f net7.0 -r osx-x64 --no-self-contained -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None - ``` + ```bash + source tools/mcc-env.sh + mcc-build + ``` -

Tip

+7. If you specifically want the low-level manual .NET command instead of the MCC wrapper, run: - **If you're not using MAC with Intel, find an appropriate RID for your ARM processor, [find an appropriate RID](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#macos-rids) and replace the `-r osx-64` with an appropriate `-r RID_NAME` (Example for arm: `-r osx.12-arm64`)** + ```bash + dotnet build MinecraftClient.sln -c Release + ``` -
+8. Run the following command if you want a release-like published binary that matches the repo's CI workflow: + + - On Linux: + + ```bash + source tools/mcc-env.sh + mcc-publish --rid linux-x64 + ``` + +

Note

+ + **If you are using Linux on ARM, 32-bit, RHEL-based distributions, or Musl, [pick the appropriate RID](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog#linux-rids) for your platform and replace `-r linux-x64` with it, for example `-r linux-arm64`.** + +
+ + - On macOS: + + ```bash + source tools/mcc-env.sh + mcc-publish --rid osx-x64 + ``` + +

Note

+ + **If you are not using an Intel Mac, [pick the appropriate RID](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog#macos-rids) for your processor and replace `-r osx-x64` with it, for example `-r osx-arm64`.** + +
If the build has succeeded, the compiled binary `MinecraftClient` will be in: -- Linux: `MinecraftClient/bin/Release/net7.0/linux-x64/publish/` -- macOS: `MinecraftClient/bin/Release/net7.0/osx-x64/publish/` +- Linux: `MinecraftClient/bin/Release/net10.0/linux-x64/publish/` +- macOS: `MinecraftClient/bin/Release/net10.0/osx-x64/publish/` + +You can verify the SDK installation with: + +```bash +dotnet --info +``` + +
## Using Docker +
+Docker setup and usage + Requirements: -- Git -- Docker +- Git +- Docker -

Tip

+

Note

**This section is for more advanced users, if you do not know how to install git or docker, you can take a look at other sections for Git, and search on how to install Docker on your system.** @@ -175,11 +289,11 @@ Requirements:

Warning

-**Pay attention at warnings, Docker currently works, but you must start the containers in the interactive mode or MCC will crash, we're working on solving this.** +**Docker works, but you need to start the container in interactive mode. Starting it in headless mode can still crash MCC.**
-1. Clone the [Git Hub Repository](https://github.com/MCCTeam/Minecraft-Console-Client) by typing end executing the following command: +1. Clone the [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client) by running: ```bash git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive @@ -196,12 +310,12 @@ docker build -t minecraft-console-client:latest .

Danger

-**There is a bug with the ConsoleInteractive which causes a crash when a container is started in a headless mode, so you need to use the interactive mode. Do not restart containers in a classic way, stop then and start them with interactive mode (this command), after that simply detach with `CTRL + P` and then `CTRL + Q`.** +**Because of a ConsoleInteractive issue, starting the container in headless mode can crash MCC. Start it with the interactive command below, then detach with `CTRL + P` followed by `CTRL + Q` if you want to leave it running in the background.**
```bash -# You could also ignore the -v parameter if you dont want to mount the volume that is up to you. If you don't it's harder to edit the .ini file if thats something you want to do +# You can omit -v if you do not want a mounted volume. Keeping the volume makes it much easier to edit the TOML config stored in MinecraftClient.ini from the host. docker run -it -v :/opt/data minecraft-console-client:latest ``` @@ -234,11 +348,11 @@ Remember to remove the container after usage: docker-compose down ``` -If you use the INI file and entered your data (username, password, server) there, you can start your container using +If you use `MinecraftClient.ini` and entered your data there, you can start your container using ```bash docker-compose up -docker-compose up -d #for deamonized running in the background +docker-compose up -d # for daemonized background running ``` Note that you won't be able to interact with the client using `docker-compose up`. If you want that functionality, please use the first method: `docker-compose run MCC`. @@ -249,23 +363,25 @@ As above, you can stop and remove the container using docker-compose down ``` +
+ ## Run on Android -It is possible to run the Minecraft Console Client on Android through Termux and Ubuntu 22.04 in it, however it requires a manual setup with a lot of commands, be careful no to skip any steps. Note that this might take anywhere from 10 to 20 minutes or more to do depending on your technical knowledge level, Internet speed and CPU speed. +It is possible to run Minecraft Console Client on Android through Termux and Ubuntu, but it requires a manual setup, so be careful not to skip any steps. Depending on your technical background, internet speed, and device speed, this can take anywhere from 10 to 20 minutes or more. -

Tip

+

Note

-**This section is going to get a bit technical, I'll try my best to make everything as simple as possible. If you are having trouble following along or if you encounter any issues, feel free to open up a discussion on our Github repository page.** +**This section gets a bit technical. If you run into issues, open a discussion on our GitHub repository page, or ask us on our Discord server.**
-

Tip

+

Note

**You're required to have some bare basic knowledge of Linux, if you do not know anything about it, watch [this video](https://www.youtube.com/watch?v=SkB-eRCzWIU) to get familiar with basic commands.**
-

Tip

+

Note

**Here we're installing everything on the root account for simplicity sake, if you want to make a user account, make sure you update the command which reference the `/root` directory with your home directory.** @@ -273,254 +389,119 @@ It is possible to run the Minecraft Console Client on Android through Termux and ### Installation +
+Android installation steps (Termux + Ubuntu + .NET + MCC) + #### Termux

Warning

-**The Play Store version of Termux is outdated and not supported, do not use it, use the the [Github one](https://github.com/termux/termux-app/releases/latest/).** +**The Play Store version of Termux is outdated and not supported. Install Termux from [F-Droid](https://f-droid.org/packages/com.termux/) (recommended) or from the [GitHub releases page](https://github.com/termux/termux-app/releases/latest/).**
-Go to [the Termux Github latest release](https://github.com/termux/termux-app/releases/latest/), download the `debug_universal.apk`, unzip it and run it. +**F-Droid (recommended):** Install the [F-Droid](https://f-droid.org/) app store, search for "Termux", and install it. -

Tip

+**GitHub releases:** Go to [the latest Termux GitHub release](https://github.com/termux/termux-app/releases/latest/), download the APK file whose name contains `universal` (e.g. `termux-app_v...-debug_universal.apk`), and install it. -**If your file manager does not let you run APK files, install and use `File Manager +` and give it a permission to install 3rd party applications when asked.** +

Note

+ +**If your file manager does not let you install APK files, install and use `File Manager +` and grant it permission to install third-party applications when asked.**
-

Danger

+

Warning

-**Once you have installed Termux, open it, bring down the Android menu for notifications, on Termux notification, drag down until you see the following options: `Exit | Acquire wakelock`, press on the `Acquire wakelock` and allow Termux to have a battery optimization exclusion permission when asked. If you do not do this, your performance will be poorer and the Termux might get killed by Android while running in the background!** +**Once you have installed Termux, open it, pull down the Android notification drawer, find the Termux notification, and expand it (swipe down on the notification) until you see `Exit | Acquire wakelock`. Tap `Acquire wakelock` and allow Termux to bypass battery optimization when prompted. Skipping this step may cause Termux to be killed by Android when running in the background.**
-#### Installing Ubuntu 22.04 +#### Installing Ubuntu -At this stage, you have 2 options: +We use `proot-distro`, an official Termux utility, to install Ubuntu. It will install the latest Ubuntu LTS release available for your device architecture. -1. Following this textual tutorial -2. Watching a [Youtube tutorial for installing Ubuntu](https://www.youtube.com/watch?v=5yit2t7smpM) +Open Termux and run the following commands one at a time, in order: -

Tip

+1. `pkg update -y` +2. `pkg upgrade -y` +3. `pkg install proot-distro -y` -**If you decide to watch the Youtube tutorial, watch only up to `1:58`, the steps after are not needed and might just confuse you.** +

Note

+ +**If you are asked to press Y/N during the update or upgrade step, enter Y and press Enter.**
-In order to install Ubuntu 22.04 in Termux you require `wget` and `proot`, we're going to install them in the next step. +Now install Ubuntu: -Once you have Termux installed open it up and run the following command one after other (in order): +```bash +pd install ubuntu:26.04 +``` -1. `pkg update` -2. `pkg upgrade` -3. `pkg install proot wget` +Once the installation finishes, start Ubuntu with: -

Tip

+```bash +pd login ubuntu +``` -**If you're asked to press Y/N during the update/upgrade command process, just enter Y and press Enter** +

Note

+ +**Every time you open Termux after it has been closed, run this command to get back into Ubuntu.**
-Then you need to download an installation script using the following command: +#### Installing .NET + +First, update the Ubuntu package lists and install a few dependencies and tools: ```bash -wget https://raw.githubusercontent.com/MFDGaming/ubuntu-in-termux/master/ubuntu.sh -``` - -Once the script has downloaded, run it with: - -```bash -bash ubuntu.sh -``` - -Then you will be asked a question, enter `Y` and press `Enter`. - -Once the installation is complete, you can start Ubuntu with: - -```bash -./startubuntu.sh -``` - -

Tip

- -**Now every time you open Termux after it has been closed, in order to access Ubuntu you have to use this command** - -
- -#### Installing .NET on ARM - -Since there are issues installing .NET 7.0 via the APT package manager at the time of writing, we will have to install it manually. - -First we need to update the APT package manager repositories and install dependencies. - -To update the APT repositories, run the following command: - -```bash -apt update -y && apt upgrade -y -``` - -After you did it, we need to install dependencies for .NET, with the following command: - -```bash -apt install wget nano unzip libc6 libgcc1 libgssapi-krb5-2 libstdc++6 zlib1g libicu70 libssl3 -y -``` - -After you have installed dependencies, it's time to install .NET, you either can follow this tutorial or the [Microsoft one](https://docs.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#manual-install). - -Navigate to your `/root` home directory with the following command: - -```bash -cd /root -``` - -First you need to download .NET 7.0, you can do it with the following command: - -```bash -wget https://download.visualstudio.microsoft.com/download/pr/6cd2eaa7-4c06-4168-b90b-ee2d6bb40b10/4a8387eb07e17d262bfb9965f6d34462/dotnet-sdk-7.0.203-linux-arm64.tar.gz -``` - -

Tip

- -**This tutorial assumes that you have 64 bit version of ARM processor, if you happen to have a 32-bit version replace the link in the command above with [this one](https://download.visualstudio.microsoft.com/download/pr/55972ef4-146e-47e6-b014-0163cbaca6a3/fa9713f73f44088898843016d68c5929/dotnet-sdk-7.0.203-linux-arm.tar.gz)** - -
- -

Tip

- -**This tutorial assumes that you're following along and using Ubuntu 22.04, if you're using a different distro, like Alpine, go to [here](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) and copy an appropriate link for your distro.** - -
- -Once the file has been downloaded, you need to run the following commands in order: - -1. `DOTNET_FILE=dotnet-sdk-7.0.203-linux-arm64.tar.gz` - -

Warning

- - **If you're using a different download link, update the file name in this command to match your version.** - -
- -2. `export DOTNET_ROOT=/root/.dotnet` - -

Warning

- - **Here we're installing .NET in `/root`, if you're installing it somewhere else, make sure to set your own path!** - -
- -3. `mkdir -p "$DOTNET_ROOT" && tar zxf "$DOTNET_FILE" -C "$DOTNET_ROOT"` -4. `export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools` - -Now we need to tell our shell to know where the `dotnet` command is, for future sessions, since the commands above just tell this current session where the `dotnet` is located. - -

Warning

- -**You will need a basic knowledge of Nano text editor, if you do not know how to use it, watch this [Youtube video tutorial](https://www.youtube.com/watch?v=DLeATFgGM-A)** - -
- -To enable this, we need to edit our `/root/.bashrc` file with the following command: - -```bash -nano /root/.bashrc -``` - -Scroll down to the bottom of the file using `Page Down` (`PGDN`) button, make a new line and paste the following text: - -```bash -export DOTNET_ROOT=/root/.dotnet/ -export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools -``` - -

Warning

- -**Here we're installing .NET in `/root`, if you're installing it somewhere else, make sure to set your own path!** - -
- -Save the file usign the following combination of keys: `CTRL + X`, type `Y` and press Enter. - -Veryfy that .NET was installed correctly by running: - -```bash -dotnet -``` - -You should get a help page: - -```bash -root@localhost:~# dotnet - -Usage: dotnet [options] -Usage: dotnet [path-to-application] - -Options: - -h|--help Display help. - --info Display .NET information. - --list-sdks Display the installed SDKs. - --list-runtimes Display the installed runtimes. - -path-to-application: - The path to an application .dll file to execute. +apt update && apt upgrade -y && apt install -y dotnet-sdk-10.0 wget curl nano ``` #### Installing MCC -Finally, we can install MCC. +Now we can install MCC. -

Warning

- -**If you have a 32 ARM processor, you need to build the MCC yourself, take a look at the [Building From Source](#building-from-the-source-code) section. Also make sure to be using the appropriate `-r` parameter value for your architecture.** - -
- -Let's make a folder where the MCC will be stored with the following command: +Let's make a folder where MCC will be stored: ```bash mkdir MinecraftConsoleClient -``` - -Then enter it the newly created folder: - -```bash cd MinecraftConsoleClient ``` -Download the MCC with the following command: +Download the latest MCC binary for ARM (the script auto detects the platform): ```bash -wget https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest/download/MinecraftClient-linux-arm64.zip +wget -qO- https://mccteam.github.io/install.sh | sh ``` -Unzip it with the following command: +Now you can run the MCC with this command: ```bash -unzip MinecraftClient-linux-arm64.zip -``` - -You can remove the zip archive now, we do not need it anymore, with: - -```bash -rm MinecraftClient-linux-arm64.zip -``` - -And finally run it with: - -``` ./MinecraftClient ``` -#### After installation +#### Running MCC -When you run Termux next time, you need to start Ubuntu with: `./startubuntu.sh` +When you open Termux next time, start Ubuntu with: -Then you can start the MCC again with `./MinecraftClient` +```bash +pd login ubuntu +``` -To stop MCC from running you can press `CTRL + C` +Then enter the folder you made `cd MinecraftConsoleClient` (If you named it different, use that name). -To edit the configuration/settings, you need a text editor, we recommend Nano, as it's very simple to use, if you have followed the installation steps above, you should be familiar with it, if not, check out [this tutorial](https://www.youtube.com/watch?v=DLeATFgGM-A). +Then run MCC with: `./MinecraftClient` + +To stop MCC from running you can press: `CTRL + C` + +To edit the configuration/settings, you need a text editor, we recommend Nano, as it's very simple to use. + +

Note

+ +**If you do not know how to use Nano, watch this [YouTube tutorial](https://www.youtube.com/watch?v=DLeATFgGM-A).** + +
For downloading files, you can use the `wget` file we have installed, simply run: @@ -528,29 +509,31 @@ For downloading files, you can use the `wget` file we have installed, simply run Also, here are some linux tutorials for people who are new to it: -- [Linux Terminal Introduction by ExplainingComputers](https://www.youtube.com/watch?v=SkB-eRCzWIU) -- [Linux Crash Course - nano (command-line text editor) by Learn Linux TV](https://www.youtube.com/watch?v=DLeATFgGM-A) -- [Linux Crash Course - The wget Command by Learn Linux TV](https://www.youtube.com/watch?v=F80Z5qd2b_4) -- [Linux Basics: How to Untar and Unzip Files (tar, gzip) by webpwnized](https://www.youtube.com/watch?v=1DF0dTscHHs) +- [Linux Terminal Introduction by ExplainingComputers](https://www.youtube.com/watch?v=SkB-eRCzWIU) +- [Linux Crash Course - nano (command-line text editor) by Learn Linux TV](https://www.youtube.com/watch?v=DLeATFgGM-A) +- [Linux Crash Course - The wget Command by Learn Linux TV](https://www.youtube.com/watch?v=F80Z5qd2b_4) +- [Linux Basics: How to Untar and Unzip Files (tar, gzip) by webpwnized](https://www.youtube.com/watch?v=1DF0dTscHHs) + +
## Run on a VPS -

Tip

+

Note

-**This is a new section, if you find a mistake, please report it by opening an Issue in our [Github repository](https://github.com/MCCTeam/Minecraft-Console-Client). Thank you!** +**This is a newer section. If you spot a mistake, please report it by opening an issue in our [GitHub repository](https://github.com/MCCTeam/Minecraft-Console-Client).**
The **Minecraft Console Client** can be run on a VPS 24 hours, 7 days a week. -- [What is a VPS?](#what-is-a-vps) -- [Prerequisites](#prerequisites) -- [Where to get a VPS](#where-to-get-a-vps) -- [Initial Amazon VPS setup](#initial-amazon-vps-setup) -- [Initial VPS setup](#initial-vps-setup) -- [Creating a new user account](#creating-a-new-user) -- [Installing .NET Core 6](#installing-net-core-6) -- [Installing the Minecraft Console Client](#installing-mcc-on-a-vps) +- [What is a VPS?](#what-is-a-vps) +- [Prerequisites](#prerequisites) +- [Where to get a VPS](#where-to-get-a-vps) +- [Initial Amazon VPS setup](#initial-amazon-vps-setup) +- [Initial VPS setup](#initial-vps-setup) +- [Creating a new user account](#creating-a-new-user) +- [Installing .NET Core 6](#installing-net-core-6) +- [Installing the Minecraft Console Client](#installing-mcc-on-a-vps) ### What is a VPS? @@ -558,42 +541,45 @@ VPS stands for a **V**irtual **P**rivate **S**erver, it's basically a remote vir You can use a VPS for hosting a website, or a an app, or a game server, or your own VPN, or the Minecraft Console Client. -Here is a [Youtube video](https://youtu.be/42fwh_1KP_o) that explains it in more detail if you're interested. +Here is a [YouTube video](https://youtu.be/42fwh_1KP_o) that explains it in more detail if you are interested. ### Prerequisites -1. Gitbash (if you're on Windows) +1. Git Bash (if you are on Windows) - Download and install [Gitbash](https://git-scm.com/downloads). + Download and install [Git Bash](https://git-scm.com/downloads). -

Tip

+

Note

- **Make sure to allow the installation to add it to the context menu** + **Make sure to allow the installation to add it to the context menu** -
+
-2. `ssh` and `ssh-keygen` commands (On Windows they're available with Gitbash, on macOs and Linux they should be available by default, it not, search on how to install them) +2. `ssh` and `ssh-keygen` commands (on Windows they are available with Git Bash; on macOS and Linux they should be available by default. If not, install them first.) 3. Basic knowledge of Linux shell commands, terminal emulator usage, SSH and Nano editor. - If you already know this, feel free to skip. + If you already know this, feel free to skip. - if you get stuck, watch those tutorials. + if you get stuck, watch those tutorials. - If you're new to this, you can learn about it here: + If you're new to this, you can learn about it here: - - [What is Linux? by Bennett Bytes](https://www.youtube.com/watch?v=JsWQUOEL0N8) - - [Linux Terminal Introduction by ExplainingComputers](https://www.youtube.com/watch?v=SkB-eRCzWIU) - - [Linux Crash Course - nano (command-line text editor) by Learn Linux TV](https://www.youtube.com/watch?v=DLeATFgGM-A) - - [Linux Crash Course - The wget Command by Learn Linux TV](https://www.youtube.com/watch?v=F80Z5qd2b_4) - - [Linux Basics: How to Untar and Unzip Files (tar, gzip) by webpwnized](https://www.youtube.com/watch?v=1DF0dTscHHs) + - [What is Linux? by Bennett Bytes](https://www.youtube.com/watch?v=JsWQUOEL0N8) + - [Linux Terminal Introduction by ExplainingComputers](https://www.youtube.com/watch?v=SkB-eRCzWIU) + - [Linux Crash Course - nano (command-line text editor) by Learn Linux TV](https://www.youtube.com/watch?v=DLeATFgGM-A) + - [Linux Crash Course - The wget Command by Learn Linux TV](https://www.youtube.com/watch?v=F80Z5qd2b_4) + - [Linux Basics: How to Untar and Unzip Files (tar, gzip) by webpwnized](https://www.youtube.com/watch?v=1DF0dTscHHs) ### Where to get a VPS +
+VPS providers and pricing + You have 2 options: -- [Buying a VPS](#buying-a-vps) -- [Getting an AWS EC2 VPS for free (12 months free trial)](#aws-ec2-vps) +- [Buying a VPS](#buying-a-vps) +- [Getting an AWS EC2 VPS for free (12 months free trial)](#aws-ec2-vps) #### Buying a VPS @@ -607,39 +593,39 @@ The MCC is not expensive to run, so it can run on basically any hardware, you do

Danger

-**In this tutorial we will be using `Ubuntu 22.04`, make sure to select it as the OS when buying a VPS.** +**In this tutorial we will be using `Ubuntu 24.04 LTS`, so pick that family when choosing your VPS image.**
Some of the reliable and cheap hosting providers (sorted for price/performance): -- [E-Trail](https://e-trail.net/vps) +- [E-Trail](https://e-trail.net/vps) - **Minimum price**: `2.50 EUR / month` + **Minimum price**: `2.50 EUR / month` -

Tip

+

Note

- **Does not have Ubuntu 22.04 in the dropdown menu when ordering, you will have to re-install later or ask support to do it.** + **If Ubuntu 24.04 LTS is not in the dropdown when ordering, you may need to reinstall later or ask support to do it.** -
+
-- [OVH Cloud](https://www.ovhcloud.com/de/vps/) +- [OVH Cloud](https://www.ovhcloud.com/de/vps/) - **Minimum price**: `3.57 EUR / month` + **Minimum price**: `3.57 EUR / month` -- [Hetzner Cloud](https://www.hetzner.com/cloud) +- [Hetzner Cloud](https://www.hetzner.com/cloud) - **Minimum price**: `4.51 EUR / month` + **Minimum price**: `4.51 EUR / month` -- [Digital Ocean](https://www.digitalocean.com/pricing/droplets) +- [Digital Ocean](https://www.digitalocean.com/pricing/droplets) - **Minimum price**: `4 EUR / month` + **Minimum price**: `4 EUR / month` -- [Contabo](https://contabo.com/en/vps/) +- [Contabo](https://contabo.com/en/vps/) - **Minimum price**: `7 EUR / month` + **Minimum price**: `7 EUR / month` - **More serious VPS able to host multiple applications, 4 CPU cores and 8 GB of RAM, 200 GB SSD** + **More serious VPS able to host multiple applications, 4 CPU cores and 8 GB of RAM, 200 GB SSD** You also may want to search for better deals. @@ -663,7 +649,7 @@ You also may want to search for better deals.
-

Tip

+

Note

**If you're not banned, sometimes fetching the keys can take some time, try giving it a minute or two, if it still hangs, hit some keys to refresh the screen, or try restarting and running again. If it still happens, use tmux instead of screen.** @@ -673,9 +659,14 @@ Register on AWS and enter all of your billing info and a phone number. Once you're done, you can continue to [Setting up the Amazon VPS](#setting-up-an-aws-vps). +
+ ### Initial Amazon VPS setup -

Tip

+
+AWS EC2 setup steps + +

Note

**Skip this section if you're not using AWS. Go to [Initial VPS setup](#initial-vps-setup)** @@ -683,7 +674,7 @@ Once you're done, you can continue to [Setting up the Amazon VPS](#setting-up-an When you register and open the `AWS Console`, click on the Search field on the top of the page and search for: `EC2` -

Tip

+

Note

**Make sure to select the region closest to you for the minimal latency** @@ -695,7 +686,7 @@ Fill out the `Name` field with a name of your preference. ![VPS Name](/images/guide/VPS_Name.png) -For the **Application and OS images** select `Ubuntu Server 22.04 LTS (HVM), SSD Volume Type`. +For the **Application and OS images** select the current `Ubuntu Server 24.04 LTS` image. The exact AWS label may vary slightly by point release.

Danger

@@ -719,11 +710,11 @@ For the **Key pair (login)** click on **Create new key pair** and name it `VpsRo For the **Network settings** check the following checkboxes on: -- `Allow SSH traffic from` (Anywhere) -- `Allow HTTPs traffic from the internet` -- `Allow HTTP traffic from the internet` +- `Allow SSH traffic from` (Anywhere) +- `Allow HTTPs traffic from the internet` +- `Allow HTTP traffic from the internet` -

Tip

+

Note

**The SSH traffic from Anywhere is not the best thing for security, you might want to enter IP addresses of your devices from which you want to access the VPS manually.** @@ -747,13 +738,13 @@ In order to login with SSH, you are going to use the following command: ssh -i ubuntu@ ``` -

Tip

+

Note

**`<` and `>` are not typed, that is just a notation for a placeholder!**
-

Tip

+

Note

**`ubuntu` is a default root account username for Ubuntu on AWS!** @@ -769,9 +760,14 @@ If you've provided the right info you should get `Welcome to Ubuntu 20.04.5 LTS` Now you can continue to [Creating a new user](#creating-a-new-user) +
+ ### Initial VPS setup -

Tip

+
+Non-AWS VPS login steps + +

Note

**This section if for those who do not use AWS, if you use AWS skip it** @@ -781,7 +777,7 @@ When you order the VPS, most likely you will be asked to provide the root accoun Other option is that you will get your login info in the email once the setup is done. -Once you have the root login account info, you need [Gitbash](https://git-scm.com/downloads) on Windows and `ssh` if you're on macOS or Linux (if you do not have it by some chance, search on how to install it, it is simple). +Once you have the root login account info, you need [Git Bash](https://git-scm.com/downloads) on Windows and `ssh` on macOS or Linux. If you're on Windows open `Git Bash`, on mac OS and Linux open a `Terminal` and type the following command: @@ -789,7 +785,7 @@ If you're on Windows open `Git Bash`, on mac OS and Linux open a `Terminal` and ssh @ ``` -

Tip

+

Note

**If you're given a custom port other than `22` by your host, you should add `-p ` before the username (eg. `ssh -p @`) or `:` after the ip (eg. `ssh @:`)** @@ -809,13 +805,18 @@ ssh -p 2233 root@142.26.73.14 Once you've logged in you should see a Linux prompt and a welcome message if there is one set by your provider. +
+ ### Creating a new user +
+User account and SSH key setup + Once you've logged in to your VPS you need to create a new user and give it SSH access. In this tutorial we will be using `mcc` as a name for the user account that will be running the MCC. -

Tip

+

Note

**You may be wondering why we're creating a separate user account and making it be accessible over SSH only. This is for security reasons, if you do not want to do this, you're free to skip it, but be careful.** @@ -833,13 +834,13 @@ Now we need to give it a password, execute the following command, type the passw sudo passwd mcc ``` -

Tip

+

Note

**When you're typing a password it will not be displayed on the screen, but you're typing it for real.**
-

Tip

+

Note

**Make sure you have a strong password!** @@ -953,9 +954,9 @@ Then find the `#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2` l Additionally for better security you can do the following: -- Set `PermitRootLogin` to `yes` -- Change the `Port` to some number of your choice (22-65000) (Make sure it's at least 2 digits and avoid common ports used by other apps like: 21, 80, 35, 8080, 3000, etc...) -- Uncomment `#PasswordAuthentication yes` by removing the `#` in front and set it to `yes` (This will disable password login, you will be able to login with SSH keys only!) +- Set `PermitRootLogin` to `yes` +- Change the `Port` to some number of your choice (22-65000) (Make sure it's at least 2 digits and avoid common ports used by other apps like: 21, 80, 35, 8080, 3000, etc...) +- Uncomment `#PasswordAuthentication yes` by removing the `#` in front and set it to `yes` (This will disable password login, you will be able to login with SSH keys only!) Save the file with `CTRL + O`, hit Enter, close it with `CTRL + X`. @@ -993,7 +994,7 @@ Example: ssh -i MCC_Key mcc@3.71.108.69 ``` -

Tip

+

Note

**If you've changed the `Port`, make sure you add a `-p ` option after the `-i ` option (eg. `ssh -i MCC_Key -p 8973 mcc@3.71.108.69`)!** @@ -1003,22 +1004,21 @@ If did everything correctly you should see a Linux prompt and a welcome message You can do `whoami` to see your username. -Now you can install .NET Core 7 and MCC. +Now you can install the .NET 10 SDK and MCC. -### Installing .NET Core 7 +
-

Tip

+### Installing .NET 10 SDK + +
+.NET SDK installation on VPS + +

Note

**If your VPS has an ARM CPU, follow [this](#installing-net-on-arm) part of the documentation and then return to section after this one.**
-

Warning

- -**With newer versions of .NET Core 7 on Ubuntu 22.04 you might get the following error: `A fatal error occurred, the folder [/usr/share/dotnet/host/fxr] does not contain any version-numbered child folders`, if you get it, use [this solution](https://github.com/dotnet/sdk/issues/27082#issuecomment-1211143446)** - -
- Log in as the user you've created. Update the system packages and package manager repositories: @@ -1027,40 +1027,16 @@ Update the system packages and package manager repositories: sudo apt update -y && sudo apt upgrade -y ``` -Install `wget`: +On Ubuntu 24.04 LTS, the official Microsoft docs say .NET is available directly from the Ubuntu package feeds, so you do not need to add the old Microsoft package repository for .NET 10. Install the SDK with: ```bash -sudo apt install wget -y +sudo apt-get update -y && sudo apt-get install -y dotnet-sdk-10.0 ``` -Go to your home directory with: +You can verify the installation with: ```bash -cd ~ -``` - -Download the Microsoft repository file: - -```bash -wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -``` - -Add Microsoft repositories to the package manager: - -```bash -sudo dpkg -i packages-microsoft-prod.deb -``` - -Remove the file, we do not need it anymore: - -```bash -rm packages-microsoft-prod.deb -``` - -Finally, install .NET Core 7: - -```bash -sudo apt-get update -y && sudo apt-get install -y dotnet-sdk-7.0 +dotnet --info ``` Run the following command to check if everything was installed correctly: @@ -1085,21 +1061,26 @@ path-to-application: The path to an application .dll file to execute. ``` -If you do not get this output and the installation was not successful, [try other methods](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#2204). +If you do not get this output and the installation was not successful, [try other methods](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install). -If it was successful, you can now install the MCC. +If it was successful, you can now install MCC. + +
### Installing MCC on a VPS -Now that you have .NET Core 7.0 and a user account, you should install the `screen` utility, you will need this in order to keep the MCC running once you close down the SSH session (if you do not have it, the MCC will just stop working once you disconnect). You can look at the `screen` like a window, except it's in a terminal, it lets you have multiple "windows" open at the same time. +
+MCC installation and screen usage -

Tip

+Now that you have the .NET SDK and a user account, install the `screen` utility. You will need it if you want MCC to keep running after you close the SSH session. + +

Note

**There is also a Docker method, if you're using Docker, you do not need the `screen` program.**
-You also can learn about the screen command from [this Youtube tutorial](https://youtu.be/_ZJiEX4rmN4). +You can also learn about the `screen` command from [this YouTube tutorial](https://youtu.be/_ZJiEX4rmN4). To install the `screen` execute the following command: @@ -1109,9 +1090,9 @@ sudo apt install screen -y Now you can install the MCC: -- [Download a compiled binary](#download-a-compiled-binary) -- [Building from the source code](#building-from-the-source-code) -- [Run using Docker](#using-docker) (Doesn't require the `screen` command) +- [Download a compiled binary](#download-a-compiled-binary) +- [Building from the source code](#building-from-the-source-code) +- [Run using Docker](#using-docker) (Doesn't require the `screen` command) How to use the `screen` command? @@ -1127,13 +1108,13 @@ To start a screen, type: screen -S mcc ``` -

Tip

+

Note

**`mcc` here is the name of the screen, you can use whatever you like, but if you've used a different name, make sure you use that one instead of the `mcc` in the following commands.**
-

Tip

+

Note

**You need to make a screen only once, however if you reboot your VPS, you need to start it on each reboot.** @@ -1162,3 +1143,5 @@ screen -ls ``` To stop the MCC, you can hit `CTRL + D` (hit it few times). + +
diff --git a/docs/guide/usage.md b/docs/guide/usage.md index 633ed93c..8b84e9bb 100644 --- a/docs/guide/usage.md +++ b/docs/guide/usage.md @@ -6,15 +6,15 @@ title: Usage How to run the program: -- [Running on Windows](#windows) -- [Running on Linux, macOS](#linux-macos) -- [Running using Docker](#docker) +- [Running on Windows](#windows) +- [Running on Linux, macOS](#linux-macos) +- [Running using Docker](#docker) Using the command line parameters: -- [Examples](#quick-usage-of-mcc-with-examples) -- [Command line parameters](#command-line-parameters) -- [Internal commands](#internal-commands) +- [Examples](#quick-usage-of-mcc-with-examples) +- [Command line parameters](#command-line-parameters) +- [Internal commands](#internal-commands) ## Windows @@ -47,7 +47,7 @@ screen -S mcc # Detach from the screen by pressing CTRL + A + D -# Re-attach if you want to have accces again +# Re-attach if you want access again screen -r mcc ``` @@ -59,21 +59,24 @@ See [Run using Docker](./installation.md#using-docker) ## Command-line usage -**Minecraft Console Client** has a plethora of useful command line parameters, here you can learn about them. +**Minecraft Console Client** has a number of useful command-line parameters. This section covers the most important ones. ### For people not familiar with the command line +
+Introduction to command-line basics + For people who are not familiar with the usage of programs in the command line (terminal emulators), here we will explain what every single thing means, if you're already experienced you can skip this. In command line (terminal emulators) you can run programs by specifying their name and hitting enter, usually programs have additional way of being configured, started or provided some additional data in a different manner, this is achieved by using command line parameters. Command line parameters are written after the name of the program, they're separated by spaces and they can have a few different formats, examples: -- `someparameter` -- `-some-parameter` -- `--some-other-parameter` -- `--some-setting="some value"` -- `-a=5` +- `someparameter` +- `-some-parameter` +- `--some-other-parameter` +- `--some-setting="some value"` +- `-a=5` Parameters with a single dash (`-`) are usually used for a single letter (short-hand) parameters, while the ones with a double dash (`--`) are being used for parameters with a longer/full name. @@ -97,9 +100,13 @@ Here is an example for using a `--help` command line parameter for MCC that will MinecraftClient.exe --help ``` +MCC also supports a few maintenance and debugging switches such as `--upgrade`, `--force-upgrade`, `--generate`, `--keyboard-debug`, `BasicIO`, and `BasicIO-NoColor`. + +
+ ### Quick usage of MCC with examples -

Tip

+

Note

**On Linux and macOS, you need to type: `./MinecraftClient` instead of `MinecraftClient.exe`** @@ -113,26 +120,35 @@ MinecraftClient.exe --section.setting=value [--other settings] MinecraftClient.exe [--other settings] ``` +

Note

+ +**Microsoft accounts use the OAuth 2.0 device code flow and do not require a password on the command line. MCC will display a code and a URL for you to sign in through your browser (with full 2FA support). You can simply omit the password or use `""` as a placeholder.** + +
+ Examples: ```bash -# Logging in as a user: notch, with a password: password123 onto a server with the ip: mc.someserver.com:25565 -MinecraftClient.exe notch password123 mc.someserver.com:25565 +# Microsoft account: connect to a server (you will sign in via device code in your browser) +MinecraftClient.exe player@example.com "" mc.someserver.com:25565 -# Overriding a setting from MinecraftClient.ini using a command line parameter +# Offline account: connect with a chosen username +MinecraftClient.exe Steve - mc.someserver.com:25565 + +# Overriding a setting from MinecraftClient.ini using a command-line parameter MinecraftClient.exe --debugmessages=false -# Providing a custom settings ini file and overriding a language to Chinese +# Providing a custom settings file and overriding the language to Chinese MinecraftClient.exe CustomSettingsFile.ini --language=zh ``` ### Rules of using the command line parameters -You can mix and match arguments by following theses rules: +You can mix and match arguments by following these rules: -- First positional argument may be either the login or a settings file -- Other positional arguments are read in order: login, password, server, command -- Arguments starting with `--` can be in any order and position +- First positional argument may be either the login or a settings file +- Other positional arguments are read in order: login, password, server, command +- Arguments starting with `--` can be in any order and position Examples and further explanations: @@ -140,38 +156,39 @@ Examples and further explanations: MinecraftClient.exe ``` -- This will automatically connect you to the chosen server. -- You may omit password and/or server to specify e.g. only the login -- To specify a server but ask password interactively, use `""` as password. -- To specify offline mode with no password, use `-` as password. +- This will automatically connect you to the chosen server. +- You may omit password and/or server to specify e.g. only the login +- For Microsoft accounts, password is not required (device code flow is used). Use `""` as a placeholder if you need to specify a server. +- To specify offline mode with no password, use `-` as password. ```bash MinecraftClient.exe "/mycommand" ``` -- This will automatically send `/mycommand` to the server and close. -- To send several commands and/or stay connected, use the 1ScriptScheduler1 bot instead. +- This will automatically send `/mycommand` to the server and close. +- To send several commands or stay connected, use the `ScriptScheduler` bot instead. ```bash MinecraftClient.exe ``` -- This will load the specified configuration file -- If the file contains login / password / server ip, it will automatically connect. +- This will load the specified configuration file +- If the file contains login / server ip, it will automatically connect. +- For Microsoft accounts, authentication happens through the device code flow (no password needed in the file). ```bash MinecraftClient.exe --setting=value [--other settings] ``` -- Specify settings on the command-line, see possible value in the configuration file -- Use `--section.setting=value` for settings outside the `[Main]` section -- Example: `--antiafk.enabled=true` for enabling the `AntiAFK` bot +- Specify settings on the command-line, see possible value in the configuration file +- Use `--section.setting=value` for settings outside the `[Main]` section +- Example: `--antiafk.enabled=true` for enabling the `AntiAFK` bot ```bash MinecraftClient.exe [--other settings] ``` -- Load the specified configuration file and override some settings from the file +- Load the specified configuration file and override some settings from the file ## Internal Commands @@ -181,1003 +198,1589 @@ From chat prompt, commands must by default be prepended with a slash, eg. `/quit In scripts and remote control, no slash is needed to perform the command, eg. `quit`. -

Tip

+

Note

**Some commands may not be documented yet or are defined in description of Chat Bots, use `/help` to list them all, or you can contribute to this page.**
-### `animation` +
+animation -- **Description:** +- **Description:** - Swing your main or off hand. + Swing your main or off hand. -- **Usage:** +- **Usage:** - ``` - /animation - ``` + ``` + /animation + ``` -### `bed` +
-- **Description:** +
+achievement - Allows you to make the bot sleep easily, all about sleeping in one command. +- **Description:** -- **Usage:** + Show the achievements or advancements currently known to MCC. - Basic usage: `bed leave|sleep |sleep ` + On Minecraft `1.8` to `1.11.2`, MCC tracks legacy achievements such as `achievement.openInventory`. -- **Examples:** + On Minecraft `1.12+`, MCC tracks advancements such as `minecraft:story/root`. - Leave a bed: +- **Usage:** - ``` - /bed leave - ``` + ``` + /achievement + /achievement list + /achievement locked + /achievement unlocked + ``` - Sleep in a bed on 124 84 76: +- **Examples:** - ``` - /bed sleep 124 84 76 - ``` + List everything MCC currently knows: - Sleep in a bed using relative coordinates: + ``` + /achievement + ``` - ``` - /bed sleep ~ ~ ~-2 - ``` + Show only incomplete entries: - Automatically find a bed in radius of 50 blocks and sleep in it: + ``` + /achievement locked + ``` - ``` - /bed sleep 50 - ``` + Show only completed entries: -### `blockinfo` + ``` + /achievement unlocked + ``` -

Tip

+- **Notes:** -**You need to have [Terrain And Movements](configuration.md#terrainandmovements) enabled in order for this to work.** + The command only shows data the server has already sent to MCC. -
+ Legacy achievements do not include titles or descriptions in the protocol, so older servers usually show the raw id instead. -- **Description:** +
- Reports the block type at the given position. +
+book - If you use the `-s` option it will report the types of blocks around the targeted blokcs. - -- **Usage:** - - Basic usage: - - ``` - /blockinfo [-s] - ``` - -### `bots` - -- **Description:** - - Allows you to list and unload a specific bot or all bots. - - Useful when debugging and developing scripts. - -- **Usage:** - - ``` - /bots > - ``` - -- **Examples:** - - Unload a bot called CustomScript - - ``` - /bots unload CustomScript - ``` - - Unload all bots - - ``` - /bots unload all - ``` - -### `changeslot` - -- **Description:** - - Change your selected slot in the hotbar. - -

Tip

- - **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** - -
- -- **Usage:** - - ``` - /changeslot <1-9> - ``` - -### `chunk` - -- **Description:** - - Displays the chunk loading status in a nice way. - -

Warning

- - **To use this feature you need to enable the [Terrain and Movements](configuration.md#terrainandmovements)** - -
- -

Tip

- - **You need a terminal with emoji support, like Powershell 7, Windows Terminal or Alacritty, if you do not want emoji support and want to use cmd or powershell 5, disable emojis with: [`enableemoji`](configuration.md#enableemoji)** - -
- -- **Usage:** - - ``` - /chunk status [chunkX chunkZ|locationX locationY locationZ] - ``` - - How it looks: - - ![Chunk status](/images/guide/ChunkStatus.png) - -### `dig` - -- **Description:** - - Dig a block on a specific coordinate. - -- **Usage:** - - ``` - /dig - ``` - -- **Example:** - - ``` - /dig 127 63 12 - ``` - - Using relative coordinates: - - ``` - /dig ~ ~-1 ~2 - ``` - -### `dropitem` - -- **Description:** - - Drop all items of a specific type from your inventory. - -

Tip

- - **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** - -
- -- **Usage:** - - ``` - /dropitem - ``` - -

Tip

- - **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** - -
- -- **Example:** - - ``` - /dropitem diamond - ``` - -### `enchant` - -

Tip

+

Note

**You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.**
-- **Description:** +- **Description:** - Allows you to enchant items in an enchanting table. + Read the book in your main hand, or edit it if it is a writable book. - You need to first open an enchanting table and then place and item that you want to enchant and lapis in the enchanting table, and then you can execute the command. + In TUI mode, `/book read` opens a page viewer instead of printing the whole book to chat. The same viewer also opens automatically when the server tells the client to open a book. - To open an enchanting table you can use the [`useblock`](#useblock) command. + If you are holding a writable book, you can replace all pages, update one page, insert a page, delete a page, and sign the finished book. -- **Usage:** +- **Usage:** - Basic usage: + Read the current book or a single page: - ``` - /enchant - ``` + ``` + /book read [page] + ``` -### `entity` + Replace the whole writable book from inline text or a file: -- **Description:** + ``` + /book write text + /book write file + ``` - Attack an entity, use an entity or get a list of entities around you. + Open the TUI editor or edit specific pages from the command line: -

Tip

+ ``` + /book edit + /book edit page + /book edit insert + /book edit delete + ``` - **You need to have [Inventory Handling](configuration.md#inventoryhandling) and [Entity Handling](configuration.md#entityhandling) enabled in order for this to work.** + Sign the writable book in your main hand: -
+ ``` + /book sign + ``` -- **Usage:** +- **Notes:** - Basic usage: + `read` works with a writable book or a written book in your main hand. - ``` - /entity <id|entitytype> <attack|use> - ``` + `write`, `edit`, and `sign` require a writable book in your main hand. - Get a list of entities around you: + Use `\n` for line breaks and `\f` for page breaks when passing inline text. - ``` - /entity - ``` + MCC checks page count, page length, and title length against the current protocol before sending the packet. - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + The interactive editor is only available in TUI mode. - **All entity types can be found [here](https://mccteam.github.io/r/entity/#L15).** + In the TUI book view, `PageUp`/`PageDown` switches pages. - </div> +- **Examples:** -- **Examples:** + Read the held book: - Attack a Zombie: + ``` + /book read + ``` - ``` - /entity Zombie attack - ``` + Show only page 2: -### `execif` + ``` + /book read 2 + ``` -- **Description:** + Write two pages from the command line: - Allows you to execute a command if a specific condition is met. + ``` + /book write text First page\nSecond line\fSecond page + ``` - The condition is a C# expression and the local variables you set using [`set`](#set), [`setrnd`](#setrnd) or the configuration file can be used. + Load the book text from a file: - The condition is always returned as a boolean, so only comparison can be done, if needed cast the expression result to bool. + ``` + /book write file ./letter.txt + ``` - Also the instance of MCC is available with `MCC.`. + Replace page 3: - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /book edit page 3 Updated text for page three + ``` - **All local variables are treated as strings in the app, when comparing their values, you can use `<variable> == "<value>"`, or better use [`.Equals`](https://www.programiz.com/csharp-programming/library/string/equals) method** + Insert a new page before page 2: - </div> + ``` + /book edit insert 2 This page goes before the old page 2 + ``` -- **Usage:** + Delete page 4: - Basic usage: `/execif <condition (C# expression)> <command>` + ``` + /book edit delete 4 + ``` -- **Examples:** + Sign the current writable book: - Setting a variable and using it: + ``` + /book sign Meeting Notes + ``` - ``` - /set test=Something - /execif 'test == "Something"' "send Success!" - ``` +</details> - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +<details> +<summary><code>bed</code></summary> - **You can use single quote (`'`) to wrap your expression if the expression contains double quote (`"`)** +- **Description:** - **Adding back-slash (`\`) before the double quote will also work (`/execif "test == \"Something\"" "send Success!"`)** + Allows you to make the bot sleep easily, all about sleeping in one command. - </div> +- **Usage:** - ``` - /set test2=1 - /execif 'test2 == "1"' "send Success 2!" - ``` + Basic usage: `bed leave|sleep <x> <y> <z>|sleep <radius>` - Basic C# expression: +- **Examples:** - ``` - /execif "1 + 2 + 3 == 6" "send Success!" - ``` + Leave a bed: - Using MCC class: + ``` + /bed leave + ``` - ``` - /execif "MCC.GetHealth() == 20.0" "send Success!" - ``` + Sleep in a bed on 124 84 76: - Using in combination with [`execmulti`](#execmulti): + ``` + /bed sleep 124 84 76 + ``` - ``` - /execif "1 == 1" "execmulti send 1 -> send 2 -> send 3" - ``` + Sleep in a bed using relative coordinates: -### `execmulti` + ``` + /bed sleep ~ ~ ~-2 + ``` -- **Description:** + Automatically find a bed in radius of 50 blocks and sleep in it: - Allows you to execute multiple commands in succession on a single line, useful for debugging or when using [`execif`](#execif) + ``` + /bed sleep 50 + ``` - Commands are separated by `->` +</details> -- **Usage:** +<details> +<summary><code>blockinfo</code></summary> - Basic usage: `execmulti <command 1> -> <command 2> -> <command 3> -> ...` +<div class="custom-container note"><p class="custom-container-title">Note</p> -- **Examples:** +**You need to have [Terrain And Movements](configuration.md#terrainandmovements) enabled in order for this to work.** - ``` - /execmulti send 1 -> send 2 -> send 3 -> sneak - ``` +</div> -### `quit` +- **Description:** -- **Alias:** `exit` -- **Description:** + Reports the block type at the given position. - Disconnect from the server and close the application + If you use the `-s` option, it also reports the surrounding block types. -### `reco` +- **Usage:** -- **Description:** + Basic usage: - Disconnect and reconnect to the server + ``` + /blockinfo <x> <y> <z> [-s] + ``` -- **Usage:** +</details> - ``` - /reco [account] - ``` +<details> +<summary><code>bots</code></summary> - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +- **Description:** - **`[account]` is an account alias defined in accounts file, for more info check out [accountlist](configuration.html#accountlist)** + Allows you to list and unload a specific bot or all bots. - </div> + Useful when debugging and developing scripts. -### `reload` +- **Usage:** -- **Description:** + ``` + /bots <list|unload <bot name|all>> + ``` - Reloads settings from MinecraftClient.ini and Chat Bots. +- **Examples:** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + Unload a bot called CustomScript - **Some settings won't be reloaded since they are used before the client initialization. Also, settings provided by the command line paramteres will be overriden. This also does not reload the ReplayBot due to technical limitations.** + ``` + /bots unload CustomScript + ``` - </div> + Unload all bots -- **Usage:** + ``` + /bots unload all + ``` - ``` - /reload - ``` +</details> -### `connect` +<details> +<summary><code>changeslot</code></summary> -- **Description:** +- **Description:** - Go to the given server and resume the script + Change your selected slot in the hotbar. -- **Usage:** + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /connect <server> [account] - ``` + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + </div> - **`<server>` is either a server IP or a server alias defined in servers file, for more info check out [serverlist](configuration.html#serverlist)** +- **Usage:** - </div> + ``` + /changeslot <1-9> + ``` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +</details> - **`[account]` is an account alias defined in accounts file, for more info check out [accountlist](configuration.html#accountlist)** +<details> +<summary><code>chunk</code></summary> - </div> +- **Description:** -### `script` + Displays the chunk loading status in a nice way. -- **Description:** + <div class="custom-container warning"><p class="custom-container-title">Warning</p> - Run a script containing a list of commands + **To use this feature you need to enable the [Terrain and Movements](configuration.md#terrainandmovements)** -- **Usage:** + </div> - ``` - /script <script name> - ``` + <div class="custom-container note"><p class="custom-container-title">Note</p> -### `send` + **You need a terminal with emoji support, like Powershell 7, Windows Terminal or Alacritty, if you do not want emoji support and want to use cmd or powershell 5, disable emojis with: [`enableemoji`](configuration.md#enableemoji)** -- **Description:** + </div> - Send a message or a command to the server +- **Usage:** -- **Usage:** + ``` + /chunk status [chunkX chunkZ|locationX locationY locationZ] + ``` - ``` - /send <text> - ``` + How it looks: -### `respawn` + ![Chunk status](/images/guide/ChunkStatus.png) -- **Description:** +</details> - Use this to respawn if you are dead (like clicking "respawn" in-game) +<details> +<summary><code>dig</code></summary> -- **Usage:** +- **Description:** - ``` - /respawn - ``` + Dig a block on a specific coordinate. -### `log` +- **Usage:** -- **Description:** + ``` + /dig <x> <y> <z> + ``` - Display some text in the console (useful for scripts) +- **Example:** -- **Usage:** + ``` + /dig 127 63 12 + ``` - ``` - /log <text> - ``` + Using relative coordinates: -- Example: + ``` + /dig ~ ~-1 ~2 + ``` - ``` - /log this is some text - ``` +</details> -### `list` +<details> +<summary><code>dropitem</code></summary> -- **Description:** +- **Description:** - List players logged in to the server (uses tab list info sent by server) + Drop all items of a specific type from your inventory. -- **Usage:** + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /list - ``` + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** -### `set` + </div> -- **Description:** +- **Usage:** - Set a value which can be used as `%variable%` in further commands + ``` + /dropitem <itemtype> + ``` -- **Usage:** + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /set <variable>=<value> - ``` + **All item types can be found [here](https://mccteam.github.io/r/item/#L12).** -- **Examples:** + </div> - ``` - /set abc=123 - ``` +- **Example:** -### `setrnd` + ``` + /dropitem diamond + ``` -- **Description:** +</details> - Set a `%variable%` randomly to one of the provided values +<details> +<summary><code>enchant</code></summary> -- **Usage:** +<div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /setrnd <variable> string1 "\"string2\" string3" - ``` +**You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** -- **Examples:** +</div> - ``` - /setrnd <variable> -7 to 10 - ``` +- **Description:** - (Set a `%variable%` to a number from -7 to 10) + Allows you to enchant items in an enchanting table. -### `sneak` + You need to first open an enchanting table and then place and item that you want to enchant and lapis in the enchanting table, and then you can execute the command. -- **Description:** + To open an enchanting table you can use the [`useblock`](#useblock) command. - Toggle sneaking. +- **Usage:** -- **Usage:** + Basic usage: - ``` - /Sneak - ``` + ``` + /enchant <top|middle|bottom> + ``` -### `tps` +</details> -- **Description:** +<details> +<summary><code>effects</code></summary> - Get the server TPS (Ticks Per Second). +- **Description:** -- **Usage:** + Lists the status effects currently applied to your player. - ``` - /tps - ``` +- **Usage:** -### `useitem` + ``` + /effects + ``` -- **Description:** +</details> - Use item in the hand, this can be used to do a right click on items which open menus on servers. +<details> +<summary><code>entity</code></summary> - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +- **Description:** - **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** + Attack an entity, use an entity or get a list of entities around you. - </div> + <div class="custom-container note"><p class="custom-container-title">Note</p> - <div class="custom-container warning"><p class="custom-container-title">Warning</p> + **You need to have [Inventory Handling](configuration.md#inventoryhandling) and [Entity Handling](configuration.md#entityhandling) enabled in order for this to work.** - **The [Inventory Handling](configuration.md#inventoryhandling) is currently not supported in `1.4.6 - 1.9`** + </div> - </div> +- **Usage:** -- **Usage:** + Basic usage: - ``` - /useitem - ``` + ``` + /entity <id|entitytype> <attack|use> + ``` -### `useblock` + Get a list of entities around you: -- **Description:** + ``` + /entity + ``` - Place a block from a hand on a specific coordinate or open an inventory: + <div class="custom-container note"><p class="custom-container-title">Note</p> - - chest/trap chest - - furnace - - brewing stand - - dispenser/dropper - - hopper - - shulker - - loom + **All entity types can be found [here](https://mccteam.github.io/r/entity/#L15).** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + </div> - **You need to have [Inventory Handling](configuration.md#inventoryhandling) and [Terrain and Movements](configuration.md#terrainandmovements) enabled in order for this to work.** +- **Examples:** - </div> + Attack a Zombie: - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /entity Zombie attack + ``` - **Not all inventories have a GUI representation in an ASCII art format.** +</details> - </div> +<details> +<summary><code>execif</code></summary> - <div class="custom-container warning"><p class="custom-container-title">Warning</p> +- **Description:** - **The [Inventory Handling](configuration.md#inventoryhandling) is currently not supported in `1.4.6 - 1.9`.** + Allows you to execute a command if a specific condition is met. - </div> + The condition is a C# expression and the local variables you set using [`set`](#set), [`setrnd`](#setrnd) or the configuration file can be used. -- **Usage:** + The condition is always returned as a boolean, so only comparison can be done, if needed cast the expression result to bool. - ``` - /useblock <x> <y> <z> - ``` + Also the instance of MCC is available with `MCC`. -- **Example:** + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /useblock 43 72 7 - ``` + **All local variables are treated as strings in the app, when comparing their values, you can use `<variable> == "<value>"`, or better use [`.Equals`](https://www.programiz.com/csharp-programming/library/string/equals) method** -### `follow` + </div> -- **Description:** +- **Usage:** - Make the bot follow a player. + Basic usage: `/execif <condition (C# expression)> <command>` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +- **Examples:** - **This command is avaliable only with [Follow Player](chat-bots.md#follow-player) Chat Bot enabled.** + Setting a variable and using it: - </div> + ``` + /set test=Something + /execif 'test == "Something"' "send Success!" + ``` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /set test2=1 + /execif 'test2 == "1"' "send Success 2!" + ``` - **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** + Basic C# expression: - </div> + ``` + /execif "1 + 2 + 3 == 6" "send Success!" + ``` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + Using MCC class: - **You need to have [Enity Handling](configuration.md#entityhandling) enabled in order for this to work.** + ``` + /execif "MCC.GetHealth() == 20.0" "send Success!" + ``` - </div> + Using in combination with [`execmulti`](#execmulti): -- **Usage:** + ``` + /execif "1 == 1" "execmulti send 1 -> send 2 -> send 3" + ``` - ``` - /follow <player name|stop> - ``` +- **Note** -- **Example:** + **You can use single quote (`'`) to wrap your expression if the expression contains double quote (`"`)** - ``` - /follow milutinke - ``` + **Adding back-slash (`\`) before the double quote will also work (`/execif "test == \"Something\"" "send Success!"`)** -### `wait` +</details> -- **Description:** +<details> +<summary><code>execmulti</code></summary> - Wait X ticks (10 ticks = ~1 second. Only for scripts) +- **Description:** -- **Usage:** + Allows you to execute multiple commands in succession on a single line, useful for debugging or when using [`execif`](#execif) - Fixed time: + Commands are separated by `->` - ``` - /wait <time> - ``` +- **Usage:** - Random time: + Basic usage: `execmulti <command 1> -> <command 2> -> <command 3> -> ...` - ``` - /wait <minimum time> to <maximum time> - ``` +- **Examples:** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /execmulti send 1 -> send 2 -> send 3 -> sneak + ``` - **You can use `-` instead of `to`** +</details> - </div> +<details> +<summary><code>quit</code></summary> -- **Examples:** +- **Alias:** `exit` - ``` - /wait 20 - ``` +- **Description:** - ``` - /wait 20 to 100 - ``` + Disconnect from the server and close the application - ``` - /wait 20-35 - ``` +</details> -### `move` +<details> +<summary><code>reco</code></summary> -- **Description:** +- **Description:** - Used for moving when terrain and movements feature is enabled. + Disconnect and reconnect to the server - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +- **Usage:** - **You need to have [Terrain and Movements](configuration.md#terrainandmovements) enabled in order for this to work.** + ``` + /reco [account] + ``` - </div> + <div class="custom-container note"><p class="custom-container-title">Note</p> - <div class="custom-container warning"><p class="custom-container-title">Warning</p> + **`[account]` is an account alias defined in accounts file, for more info check out [accountlist](configuration.html#accountlist)** - **The [Terrain and Movements](configuration.md#terrainandmovements) is currently not supported in `1.4.6 - 1.6`.** + </div> - </div> +</details> -- **Usage:** +<details> +<summary><code>reload</code></summary> - ``` - /move <on|off|get|up|down|east|west|north|south|center|x y z|gravity [on|off]> [-f]: walk or start walking. "-f": force unsafe movements like falling or touching fire - ``` +- **Description:** -- **Examples:** + Reloads the active configuration file and chat bots. - Enable gravity + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /move gravity on - ``` + **Some settings are not reloaded because they are used before client initialization. Settings passed on the command line also override file values.** - Move to coordinates: + </div> - ``` - /move 125 72 34 - ``` +- **Usage:** - Move to a center of a block: + ``` + /reload + ``` - ``` - /move center - ``` +</details> -### `nameitem` +<details> +<summary><code>recipebook</code></summary> -- **Description:** +- **Description:** - This command allows you to name an item when you have an Anvil inventory open and an item in the first slot (slot number 0), + List unlocked recipe book entries and ask the server to place one of them into the active crafting inventory. - After you place an item in the first slot of the anvil, use this command, and then do a click on the slot 2 to get an item from the anvil, then do a click on an empty slot in your inventory. + <div class="custom-container note"><p class="custom-container-title">Note</p> -- **Usage:** + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this command to work.** - ``` - /nameitem <name of the item> - ``` + </div> -- **Example:** + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /nameitem My super duper sword 2000 - ``` + **`craft` and `craftall` need an active player crafting grid, crafting table, furnace, blast furnace, smoker, or stonecutter inventory.** -- **Full Example with anvil:** + </div> - ``` - # Open an anvil - /useblock 12 74 321 + <div class="custom-container warning"><p class="custom-container-title">Warning</p> - # Click on an axe in slot 12 - /inventory container click 12 + **Recipe book crafting is supported on Minecraft `1.13+`.** - # Put an axe to the slot 0 in anvil - /inventory container click 0 + </div> - # Set the new name - /nameitem My fancy axe + `list` shows the recipe book entries MCC is currently tracking. - # Click on the axe in slot 2 in the anvil - /inventory container click 2 + On newer versions, the list can contain numeric display ids instead of plain recipe names. If you see something like `838: Oak Planks`, use `838` with `craft` or `craftall`. - # Put the axe back in your inventory in slot 12 - /inventory container click 12 + `craft` and `craftall` send a recipe-book request to the server. They do not automatically take the result item for you. After the recipe appears in the active inventory, take the output slot the same way you would handle any other inventory action. - # Close the anvil - /inventory container close - ``` +- **Usage:** -### `look` + ``` + /recipebook list + ``` -- **Description:** + ``` + /recipebook craft <recipe id> + ``` - Used for looking at direction when terrain and movements is enabled + ``` + /recipebook craftall <recipe id> + ``` -- **Usage:** +- **Examples:** - ``` - /look <x y z|yaw pitch|up|down|east|west|north|south> - ``` + Show the currently tracked recipe book entries: -- **Examples:** + ``` + /recipebook list + ``` - ``` - /look up - ``` + Request one recipe placement: - ``` - /look east - ``` + ``` + /recipebook craft minecraft:oak_planks + ``` -### `inventory` + On newer versions, use the numeric id shown by `/recipebook list`: -- **Description:** + ``` + /recipebook craftall 838 + ``` - Used for inventory manipulation. + If the recipe is placed in the player crafting grid, take the result from slot `0`: - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /inventory player click 0 + ``` - **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** +</details> - </div> +<details> +<summary><code>clear-console</code></summary> - <div class="custom-container warning"><p class="custom-container-title">Warning</p> +- **Description:** - **The [Inventory Handling](configuration.md#inventoryhandling) is currently not supported in `1.4.6 - 1.9`.** + Clear the visible console output. - </div> + In TUI mode this clears the log panel. In the classic console it clears the terminal screen. - MCC defines inventories as containers internally, so player's inventory, chests, droppers, dispensers, hoppers, chest minecarts, barrels, furnaces, etc... are all considered a container, and each one of them has it's ID, the words container and inventory can be used interchangeably. +- **Usage:** - Inventory has slots and each one of them has an id. + ``` + /clear-console + /cc + ``` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +</details> - **This command DOES NOT physically open a container (eg. chest), for that you need to use [`useblock`](#useblock) command first.** +<details> +<summary><code>connect</code></summary> - </div> +- **Description:** - An example of player inventory with annotated IDs in ASCII art and a list of items: + Go to the given server and resume the script - ![Player Inventory](/images/guide/PlayerInventory.png "Player Inventory") +- **Usage:** -- **Usage:** + ``` + /connect <server> [account] + ``` - Basic usage: + <div class="custom-container note"><p class="custom-container-title">Note</p> - ``` - /inventory <player|container|<id>> <action> [action parameters] | /inventory <inventories/i> | /inventory <search/s> <item type> [amount] - ``` + **`<server>` is either a server IP or a server alias defined in servers file, for more info check out [serverlist](configuration.html#serverlist)** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + </div> - **player and container can be simplified with p and c accordingly** + <div class="custom-container note"><p class="custom-container-title">Note</p> - </div> + **`[account]` is an account alias defined in accounts file, for more info check out [accountlist](configuration.html#accountlist)** - Actions: + </div> - - `click` - - `drop` +</details> - Show/Preview items in an inventory: +<details> +<summary><code>script</code></summary> - ``` - /inventory <player|id> - ``` +- **Description:** - Click/Shift-Click on an item in an inventory: + Run a script containing a list of commands - ``` - /inventory <player|container|<id>> <click> <slot id> [left|right|middle|Shift|ShiftRight] - ``` +- **Usage:** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /script <script name> + ``` - **The default click is left click** +</details> - </div> +<details> +<summary><code>send</code></summary> - Close an inventory: +- **Description:** - ``` - /inventory <player|container|<id>> close - ``` + Send a message or a command to the server - Drop item(s) from an inventory: +- **Usage:** - ``` - /inventory <player|id> drop <slot id> <number of items|all> - ``` + ``` + /send <text> + ``` - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +</details> - **To drop all items from a slot, you can use: `all`** +<details> +<summary><code>respawn</code></summary> - </div> +- **Description:** - Give an item to the player inventory from a creative menu when in the creative mode: + Use this to respawn if you are dead (like clicking "respawn" in-game) - ``` - /inventory creativegive <slot id> <item type> <amount> - ``` +- **Usage:** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /respawn + ``` - **To find item types, check out [this list](https://mccteam.github.io/r/item/#L12)** +</details> - </div> +<details> +<summary><code>log</code></summary> - Delete an item from a player's inventory when in the creative mode: +- **Description:** - ``` - /inventory creativedelete <slot id> - ``` + Display some text in the console (useful for scripts) - Show all available inventories: +- **Usage:** - ``` - /inventory inventories - ``` + ``` + /log <text> + ``` - Search for an item of specified type in available inventories: +- Example: - ``` - /inventory search <item type> - ``` + ``` + /log this is some text + ``` -- **Examples:** +</details> - Show player's inventory: +<details> +<summary><code>list</code></summary> - ``` - /inventory player - ``` +- **Description:** - Show/Preview items in an inventory using an id: + List players logged in to the server (uses tab list info sent by server) - ``` - /inventory 3 - ``` +- **Usage:** - Click on an item in player's inventory in slot number/id `36`: + ``` + /list + ``` - ``` - /inventory player click 36 - ``` +</details> - Right-Click on an item in slot number/id `4` in an inventory with an id `2`: +<details> +<summary><code>tab</code></summary> - ``` - /inventory 2 click 4 right - ``` +- **Description:** - Close an inventory with an id `2`: + Show the current player tab list in a more detailed format than `/list`. - ``` - /inventory 2 close - ``` + In the classic console, `/tab` prints a colored table with ping and player names. Team prefixes, suffixes, and display names are applied when the server sends them. - Drop a single item from a player's inventory in slot number/id `36`: + In TUI mode, `/tab` opens a live overlay that refreshes automatically while it is visible. Press `Esc` to close it. - ``` - /inventory player drop 36 1 - ``` + If you want a separate team column, enable [Console.TabList.ShowTeams](configuration.md#showteams). - Drop all items from a player's inventory in slot number/id `37`: +- **Usage:** - ``` - /inventory player drop 37 all - ``` + ``` + /tab + ``` - Give an item to the player inventory from a creative menu when in the creative mode: +- **Notes:** - ``` - /inventory creativegive 36 diamondblock 64 - ``` + - `/tab` uses the tab list information sent by the server, so players hidden from the server tab list will not appear here. + - The TUI overlay follows the live player list, so joins, leaves, ping updates, and scoreboard team updates show up without reopening it. - <div class="custom-container tip"><p class="custom-container-title">Tip</p> +</details> - **To find item types, check out [this list](https://mccteam.github.io/r/item/#L12)** +<details> +<summary><code>set</code></summary> - </div> +- **Description:** - Delete an item from a player's inventory in slot number/id `36` when in the creative mode: + Set a value which can be used as `%variable%` in further commands - ``` - /inventory creativedelete 36 - ``` +- **Usage:** - Search for 10 Slime Blocks in available inventories: + ``` + /set <variable>=<value> + ``` - ``` - /inventory s SlimeBlock 10 - ``` +- **Examples:** -### `debug` + ``` + /set abc=123 + ``` -- **Description:** +</details> - Toggle debug messages, useful for chatbot developers. +<details> +<summary><code>setrnd</code></summary> -### `help` +- **Description:** -- **Description:** + Set a `%variable%` randomly to one of the provided values - Show commands help. +- **Usage:** - <div class="custom-container tip"><p class="custom-container-title">Tip</p> + ``` + /setrnd <variable> string1 "\"string2\" string3" + ``` - **Use "/send /help" for server help** +- **Examples:** - </div> + ``` + /setrnd <variable> -7 to 10 + ``` + + (Set a `%variable%` to a number from -7 to 10) + +</details> + +<details> +<summary><code>sneak</code></summary> + +- **Description:** + + Toggle sneaking. + +- **Usage:** + + ``` + /Sneak + ``` + +</details> + +<details> +<summary><code>tps</code></summary> + +- **Description:** + + Get the server TPS (Ticks Per Second). + +- **Usage:** + + ``` + /tps + ``` + +</details> + +<details> +<summary><code>teams</code></summary> + +- **Description:** + + List all scoreboard teams the server has sent, along with their members and settings. + +- **Usage:** + + ``` + /teams + ``` + +- **Example output:** + + ``` + Team 'RedTeam' (display: RedTeam, color: 12, prefix: '', suffix: '', nameTagVisibility: always, collisionRule: always, friendlyFire: True, seeInvisibles: True) + Members (2): Steve, Alex + Team 'BlueTeam' (display: BlueTeam, color: 9, prefix: '', suffix: '', nameTagVisibility: always, collisionRule: always, friendlyFire: True, seeInvisibles: True) + No members. + ``` + +</details> + +<details> +<summary><code>useitem</code></summary> + +- **Description:** + + Use the item in your hand, including use-on-block actions like shovel flattening. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** + + </div> + + <div class="custom-container warning"><p class="custom-container-title">Warning</p> + + **The [Inventory Handling](configuration.md#inventoryhandling) is currently supported on `1.8+` and is unavailable on `1.4.6 - 1.7.10`.** + + </div> + +- **Usage:** + + ``` + /useitem + ``` + + Use the item from a specific hand: + + ``` + /useitem <mainhand|offhand> + ``` + + Use the item on a specific block: + + ``` + /useitem <x> <y> <z> + ``` + + Use the item from a specific hand on a specific block: + + ``` + /useitem <x> <y> <z> <mainhand|offhand> + ``` + +</details> + +<details> +<summary><code>useblock</code></summary> + +- **Description:** + + Place a block from a hand on a specific coordinate or open an inventory: + + - chest/trap chest + - furnace + - brewing stand + - dispenser/dropper + - hopper + - shulker + - loom + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Inventory Handling](configuration.md#inventoryhandling) and [Terrain and Movements](configuration.md#terrainandmovements) enabled in order for this to work.** + + </div> + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **Not all inventories have a GUI representation in an ASCII art format.** + + </div> + + <div class="custom-container warning"><p class="custom-container-title">Warning</p> + + **The [Inventory Handling](configuration.md#inventoryhandling) is currently supported on `1.8+` and is unavailable on `1.4.6 - 1.7.10`.** + + </div> + +- **Usage:** + + ``` + /useblock <x> <y> <z> + ``` + +- **Example:** + + ``` + /useblock 43 72 7 + ``` + +</details> + +<details> +<summary><code>follow</code></summary> + +- **Description:** + + Make the bot follow a player. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **This command is available only when the [Follow Player](chat-bots.md#follow-player) chat bot is enabled.** + + </div> + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** + + </div> + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Entity Handling](configuration.md#entityhandling) enabled in order for this to work.** + + </div> + +- **Usage:** + + ``` + /follow <player name|stop> + ``` + +- **Example:** + + ``` + /follow milutinke + ``` + +</details> + +<details> +<summary><code>wait</code></summary> + +- **Description:** + + Wait X ticks (20 ticks = ~1 second. Only for scripts) + +- **Usage:** + + Fixed time: + + ``` + /wait <time> + ``` + + Random time: + + ``` + /wait <minimum time> to <maximum time> + ``` + + <div class="custom-container tip"><p class="custom-container-title">Tip</p> + + **You can use `-` instead of `to`** + + </div> + +- **Examples:** + + ``` + /wait 20 + ``` + + ``` + /wait 20 to 100 + ``` + + ``` + /wait 20-35 + ``` + +</details> + +<details> +<summary><code>move</code></summary> + +- **Description:** + + Used for moving when terrain and movements feature is enabled. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Terrain and Movements](configuration.md#terrainandmovements) enabled in order for this to work.** + + </div> + + <div class="custom-container warning"><p class="custom-container-title">Warning</p> + + **The [Terrain and Movements](configuration.md#terrainandmovements) is currently not supported in `1.4.6 - 1.6`.** + + </div> + +- **Usage:** + + ``` + /move <on|off|get|up|down|east|west|north|south|center|x y z|gravity [on|off]> [-f]: walk or start walking. "-f": force unsafe movements like falling or touching fire + ``` + +- **Examples:** + + Enable gravity + + ``` + /move gravity on + ``` + + Move to coordinates: + + ``` + /move 125 72 34 + ``` + + Move to a center of a block: + + ``` + /move center + ``` + +</details> + +<details> +<summary><code>nameitem</code></summary> + +- **Description:** + + This command allows you to name an item when you have an Anvil inventory open and an item in the first slot (slot number 0), + + After you place an item in the first slot of the anvil, use this command, and then do a click on the slot 2 to get an item from the anvil, then do a click on an empty slot in your inventory. + +- **Usage:** + + ``` + /nameitem <name of the item> + ``` + +- **Example:** + + ``` + /nameitem My super duper sword 2000 + ``` + +- **Full Example with anvil:** + + ``` + # Open an anvil + /useblock 12 74 321 + + # Click on an axe in slot 12 + /inventory container click 12 + + # Put an axe to the slot 0 in anvil + /inventory container click 0 + + # Set the new name + /nameitem My fancy axe + + # Click on the axe in slot 2 in the anvil + /inventory container click 2 + + # Put the axe back in your inventory in slot 12 + /inventory container click 12 + + # Close the anvil + /inventory container close + ``` + +</details> + +<details> +<summary><code>look</code></summary> + +- **Description:** + + Used for looking at direction when terrain and movements is enabled + +- **Usage:** + + ``` + /look <x y z|yaw pitch|up|down|east|west|north|south> + ``` + +- **Examples:** + + ``` + /look up + ``` + + ``` + /look east + ``` + +</details> + +<details> +<summary><code>inventory</code></summary> + +- **Description:** + + Used for inventory manipulation. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **You need to have [Inventory Handling](configuration.md#inventoryhandling) enabled in order for this to work.** + + </div> + + <div class="custom-container warning"><p class="custom-container-title">Warning</p> + + **The [Inventory Handling](configuration.md#inventoryhandling) is currently supported on `1.8+` and is unavailable on `1.4.6 - 1.7.10`.** + + </div> + + MCC defines inventories as containers internally, so player's inventory, chests, droppers, dispensers, hoppers, chest minecarts, barrels, furnaces, etc... are all considered a container, and each one of them has it's ID, the words container and inventory can be used interchangeably. + + Inventory has slots and each one of them has an id. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **This command DOES NOT physically open a container (eg. chest), for that you need to use [`useblock`](#useblock) command first.** + + </div> + + An example of player inventory with annotated IDs in ASCII art and a list of items: + + ![Player Inventory](/images/guide/PlayerInventory.png "Player Inventory") + +- **Usage:** + + Basic usage: + + ``` + /inventory <player|container|<id>> <action> [action parameters] | /inventory <inventories/i> | /inventory <search/s> <item type> [amount] + ``` + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **player and container can be simplified with p and c accordingly** + + </div> + + Actions: + + - `click` + - `drop` + + Show/Preview items in an inventory: + + ``` + /inventory <player|id> + ``` + + Click/Shift-Click on an item in an inventory: + + ``` + /inventory <player|container|<id>> <click> <slot id> [left|right|middle|Shift|ShiftRight] + ``` + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **The default click is left click** + + </div> + + Close an inventory: + + ``` + /inventory <player|container|<id>> close + ``` + + Drop item(s) from an inventory: + + ``` + /inventory <player|id> drop <slot id> <number of items|all> + ``` + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **To drop all items from a slot, you can use: `all`** + + </div> + + Give an item to the player inventory from a creative menu when in the creative mode: + + ``` + /inventory creativegive <slot id> <item type> <amount> + ``` + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **To find item types, check out [this list](https://mccteam.github.io/r/item/#L12)** + + </div> + + Delete an item from a player's inventory when in the creative mode: + + ``` + /inventory creativedelete <slot id> + ``` + + Show all available inventories: + + ``` + /inventory inventories + ``` + + Search for an item of specified type in available inventories: + + ``` + /inventory search <item type> + ``` + +- **Examples:** + + Show player's inventory: + + ``` + /inventory player + ``` + + Show/Preview items in an inventory using an id: + + ``` + /inventory 3 + ``` + + Click on an item in player's inventory in slot number/id `36`: + + ``` + /inventory player click 36 + ``` + + Right-Click on an item in slot number/id `4` in an inventory with an id `2`: + + ``` + /inventory 2 click 4 right + ``` + + Close an inventory with an id `2`: + + ``` + /inventory 2 close + ``` + + Drop a single item from a player's inventory in slot number/id `36`: + + ``` + /inventory player drop 36 1 + ``` + + Drop all items from a player's inventory in slot number/id `37`: + + ``` + /inventory player drop 37 all + ``` + + Give an item to the player inventory from a creative menu when in the creative mode: + + ``` + /inventory creativegive 36 diamondblock 64 + ``` + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **To find item types, check out [this list](https://mccteam.github.io/r/item/#L12)** + + </div> + + Delete an item from a player's inventory in slot number/id `36` when in the creative mode: + + ``` + /inventory creativedelete 36 + ``` + + Search for 10 Slime Blocks in available inventories: + + ``` + /inventory s SlimeBlock 10 + ``` + +</details> + +<details> +<summary><code>console-chat</code></summary> + +- **Description:** + + Temporarily show or hide chat in the console. + + This changes the current session only. If you want the same behavior every time MCC starts, use [`Console.General.Display_Chat`](configuration.md#display_chat). + +- **Usage:** + + ``` + /console-chat + /console-chat on + /console-chat off + ``` + +- **Examples:** + + Hide chat until you turn it back on: + + ``` + /console-chat off + ``` + + Show chat again: + + ``` + /console-chat on + ``` + +</details> + +<details> +<summary><code>dialog</code></summary> + +- **Description:** + + Browse and interact with dialogs sent by the server. Dialogs are popups with a title, body text, and buttons. TUI mode gives the best experience -- use `/dialog open` to view the dialog in a full-screen overlay. + + When a server shows a dialog, MCC prints: + + ``` + [MCC] Server showed custom dialog: Server Notice. Use dialog show. + ``` + + Run `/dialog show` to see the full dialog. Buttons show up like this: + + ``` + Actions: + [1] OK (close) + [2] Visit (command) + [3] Rules (show dialog) + ``` + + The text in parentheses tells you what the button does: `(close)` closes the dialog, `(command)` sends a chat command, `(show dialog)` opens another dialog. + +- **Dialog types:** + + Dialogs come in a few shapes. You might see these in the output of `/dialog show`: + + **Notice** -- A popup with a title, optional body, and one button. + + ``` + Type: Notice + + Welcome to the server! + + Body: Read the rules before playing. + + Actions: + [1] OK (close) + ``` + + **Confirmation** -- A choice between two buttons. + + ``` + Type: Confirmation + + Reset your progress? + + Actions: + [1] Yes (close) + [2] No (close) + ``` + + **Multi-action** -- A grid of buttons. + + ``` + Type: Multi-action + + Choose a destination + + Actions: + [1] Spawn (close) + [2] Shop (close) + [3] Arena (close) + ``` + + **Dialog list** -- A list of sub-dialogs. Clicking one opens another dialog. + + ``` + Type: Dialog list + + Help Topics + + Actions: + [1] Rules (show dialog) + [2] Commands (show dialog) + ``` + + **Server links** -- Shows the server's configured links as buttons. May be empty. + + ``` + Type: Server links + + Server Links + ``` + +- **Usage:** + + ``` + /dialog + /dialog show + /dialog open + /dialog click <index> + /dialog click-label <label> + /dialog set <input> <value> + /dialog input <input> <value> + /dialog cancel + /dialog dismiss + ``` + +- **Examples:** + + ``` + /dialog show + /dialog click 1 + /dialog click-label Teleport + /dialog set name MyPlayer + /dialog dismiss + ``` + + See what dialog is currently shown, click the first button, click a button by its text, fill in an input, or close the dialog. + +</details> + +<details> +<summary><code>debug</code></summary> + +- **Description:** + + Toggle debug messages, useful for chatbot developers. + +</details> + +<details> +<summary><code>help</code></summary> + +- **Description:** + + Show commands help. + + <div class="custom-container note"><p class="custom-container-title">Note</p> + + **Use "/send /help" for server help** + + </div> + +</details> diff --git a/docs/guide/websocket/Commands.md b/docs/guide/websocket/Commands.md index d6754445..38db437e 100644 --- a/docs/guide/websocket/Commands.md +++ b/docs/guide/websocket/Commands.md @@ -1,1413 +1,529 @@ -# Web Socket Commands +# WebSocket Commands -## Important +Commands are JSON objects sent over the WebSocket connection. +Each command produces a response through the [`OnWsCommandResponse`](Events.md#onwscommandresponse) event. -**I'll try to include a full list of commands here with full examples, but you will have to take a look at the source code from time to time to see the types you can send in more details.** - -**The source code of the WebSocket Chat Bot:** [Click here](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/ChatBots/WebSocketBot.cs#L484) +```json +{ + "command": "CommandName", + "requestId": "unique-id", + "parameters": [] +} +``` ## Protocol Commands -Protocol commands are commands to manipulate the protocol. +These commands manage the WebSocket session itself. ### `Authenticate` - This command is used to authenticate if there is a password set in the Web Socket chat bot settings. +Authenticate with the configured password. +Must be called before any other command (except `ChangeSessionId`). - **Parameters:** +**Parameters:** - It takes a single parameters of a string type that contains a password. +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | string | Password | - **Example:** +**Example:** - ```json - { - "command": "Authenticate", - "requestId": "a08rt980u15j890", - "parameters": ["wspass12345"] - } - ``` +```json +{ + "command": "Authenticate", + "requestId": "auth-001", + "parameters": ["your-password-here"] +} +``` ### `ChangeSessionId` - This command is used to change the name/alias/id of a session. - - **Parameters:** - - It takes a single parameters of a string type that contains a name. - - **Example:** - - ```json - { - "command": "ChangeSessionId", - "requestId": "9845eybjb8936j0i3", - "parameters": ["My Custom Session Name"] - } - ``` - -## Procedures - -Procedures are the methods/functions you can execute on the MCC itself to interact with the minecraft server. - -### - `LogToConsole` - -**Description:** - -Log stuff in to the MCC console. +Rename the current session. Can be called without authentication. +The new ID must be 1-32 characters and not already taken. **Parameters:** -- `message` - - **Type:** `string` - -**Return type:** `boolean` +| Index | Type | Description | +| ----- | ------ | -------------- | +| 0 | string | New session ID | **Example:** ```json { - "command": "LogToConsole", - "requestId": "9qaeuitgng", - "parameters": ["Some text to log..."] + "command": "ChangeSessionId", + "requestId": "rename-001", + "parameters": ["my-bot"] } ``` -### - `LogDebugToConsole` +## Logging Commands -**Description:** +### `LogToConsole` -Log stuff in to the MCC debug console channel. +Log a message to the MCC console. **Parameters:** -- `message` +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | string | Message | - **Type:** `string` +### `LogDebugToConsole` -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "LogDebugToConsole", - "requestId": "yt30j83g-uq", - "parameters": ["Some text to log..."] -} -``` - -### - `LogToConsoleTranslated` - -**Description:** - -Log a translated string in to the MCC console. +Log a debug message to the MCC console (only visible in debug mode). **Parameters:** -- `message` +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | string | Message | - **Type:** `string` +### `LogToConsoleTranslated` -**Return type:** `boolean` - -```json -{ - "command": "LogToConsoleTranslated", - "requestId": "qt089t1jh1t1t", - "parameters": ["ChatBot.WebSocketBot.DebugMode"] -} -``` - -### - `LogDebugToConsoleTranslated` - -**Description:** - -Log a translated string in to the MCC debug console channel. +Log a translated message using an MCC translation key. **Parameters:** -- `message` +| Index | Type | Description | +| ----- | ------ | --------------- | +| 0 | string | Translation key | - **Type:** `string` +### `LogDebugToConsoleTranslated` -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "LogDebugToConsoleTranslated", - "requestId": "gpiqahjgpag", - "parameters": ["ChatBot.WebSocketBot.DebugMode"] -} -``` - -### - `ReconnectToTheServer` - -**Description:** - -Reconnect to the server the MCC is connected to. +Log a translated debug message. **Parameters:** -- `extraAttempts` +| Index | Type | Description | +| ----- | ------ | --------------- | +| 0 | string | Translation key | - **Type:** `integer` +## Session Commands - **Note:** Use -1 for unlimited attempts number. +### `ReconnectToTheServer` -- `delaySeconds` - - **Type:** `integer` - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "ReconnectToTheServer", - "requestId": "098uqh3r2w0qt9", - "parameters": [60, 360] -} -``` - -### - `DisconnectAndExit` - -**Description:** - -Disconnect MCC from the server and close the program. +Reconnect to the Minecraft server. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ---- | ------------------------------------ | +| 0 | int | Extra reconnect attempts (default 3) | +| 1 | int | Delay in seconds (default 0) | -**Example:** +### `DisconnectAndExit` -```json -{ - "command": "DisconnectAndExit", - "requestId": "89seut02349wjk", - "parameters": [] -} -``` +Disconnect from the server and shut down MCC. +No parameters. -### - `RunScript` +## Chat Commands -**Description:** +### `SendPrivateMessage` -Run a MCC C# script. +Send a private message to a player. **Parameters:** -- `scriptName` +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | string | Player name | +| 1 | string | Message | - **Type:** `string` +## Script Commands -**Return type:** `boolean` +### `RunScript` -**Example:** - -```json -{ - "command": "RunScript", - "requestId": "q3r098qhtqj-0", - "parameters": ["testScript.cs"] -} -``` - -### - `GetTerrainEnabled` - -**Description:** - -Check if the Terrain Handling is enabled. +Run an MCC script file. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | string | File name | -**Return type:** `boolean` +## World and Terrain Commands -**Example:** +### `GetTerrainEnabled` -```json -{ - "command": "GetTerrainEnabled", - "requestId": "089wqejru", - "parameters": [] -} -``` +Check if terrain handling is enabled. +No parameters. Returns `{ "enabled": true/false }`. -### - `SetTerrainEnabled` +### `SetTerrainEnabled` -**Description:** - -Try enabling the Terrain Handling. +Enable or disable terrain handling. **Parameters:** -- `enabled` +| Index | Type | Description | +| ----- | ---- | ----------- | +| 0 | bool | Enabled | - **Type:** `boolean` +### `GetWorld` -**Return type:** `boolean` +Check if world data is available. +No parameters. Returns `{ "available": true }` if terrain is enabled. -**Example:** +### `DigBlock` -```json -{ - "command": "SetTerrainEnabled", - "requestId": "9uW4HT9A", - "parameters": [true] -} -``` - -### - `GetEntityHandlingEnabled` - -**Description:** - -Check if the Entity Handling is enabled. +Break a block at the given coordinates. +Validates the block is within 6 blocks and is not air. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ------ | --------------------------------- | +| 0 | double | X coordinate | +| 1 | double | Y coordinate | +| 2 | double | Z coordinate | +| 3 | string | Direction (optional, e.g. "Down") | -**Return type:** `boolean` +The `Direction` parameter accepts string names: `Down`, `Up`, `North`, `South`, `West`, `East`. -**Example:** +## Entity Commands -```json -{ - "command": "GetEntityHandlingEnabled", - "requestId": "ua5yht9-a8u", - "parameters": [] -} -``` +### `GetEntityHandlingEnabled` -### - `Sneak` +Check if entity handling is enabled. +No parameters. Returns `{ "enabled": true/false }`. -**Description:** +### `GetEntities` -Toggle sneak. +Get all tracked entities. +No parameters. Returns a dictionary of entity ID to entity object. -**Parameters:** +Entity types are serialized as string names (e.g., `"Zombie"`, `"Player"`). -- `toggle` - - **Type:** `boolean` - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "Sneak", - "requestId": "iurwt8h97", - "parameters": [true] -} -``` - -### - `SendEntityAction` - -**Description:** - -Send an entity action. - -**Parameters:** - -- `actionType` - - **Type:** [`EntityActionType` as a an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Protocol/EntityActionType.cs#L3) - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "SendEntityAction", - "requestId": "0j5t3yb89j-q5b9j8", - "parameters": [1] -} -``` - -### - `DigBlock` - -**Description:** - -Dig a block in the world. - -**Parameters:** - -- `X` - - **Type:** `double` - -- `Y` - - **Type:** `double` - -- `Z` - - **Type:** `double` - -- `swingArms` (optional, default `true`) - - **Type:** `boolean` - -- `lookAtBlock` (optional, default `true`) - - **Type:** `boolean` - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "DigBlock", - "requestId": "89q58u9qb", - "parameters": [12.5, 72, 12.5, true, true] -} -``` - -### - `SetSlot` - -**Description:** - -Set the current active hot bar slot. - -**Parameters:** - -- `slotId` - - **Type:** `integer` - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "SetSlot", - "requestId": "9hu43tv9hu4tv", - "parameters": [1] -} -``` - -### - `GetWorld` - -**Description:** - -Get world info. - -**Parameters:** - -- No parameters - -**Return type:** [`json encoded object with world info`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/World.cs) - -**Example:** - -```json -{ - "command": "GetWorld", - "requestId": "89753q6bh756b", - "parameters": [] -} -``` - -### - `GetEntities` - -**Description:** - -Get a list of entities around the player. - -**Parameters:** - -- No parameters - -**Return type:** [`json encoded array of Entity`](https://github.com/milutinke/MCC.js/blob/dc5ccfecb65284f021c94c8381c3d7fb4f36a2c3/src/MccTypes/Entity.ts#L130) - -**Example:** - -```json -{ - "command": "GetEntities", - "requestId": "9ujrte9ujp", - "parameters": [] -} -``` - -### - `GetPlayersLatency` - -**Description:** - -Get a list of players and their latencies. - -**Parameters:** - -- No parameters - -**Return type:** `json encoded array of player object with { "<nick>": <latency> }` - -**Example:** - -```json -{ - "command": "GetPlayersLatency", - "requestId": "9uj53ybwj8945sby6", - "parameters": [] -} -``` - -### - `GetCurrentLocation` - -**Description:** - -Get the current bot location in the world. - -**Parameters:** - -- No parameters - -**Return type:** [`json encoded Location object`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/Location.cs) - -**Example:** - -```json -{ - "command": "GetCurrentLocation", - "requestId": "8953ybu896b539j8056b3", - "parameters": [] -} -``` - -### - `MoveToLocation` - -**Description:** - -Move to a location in the world. - -**Parameters:** - -- `X` - - **Type:** `double` - -- `Y` - - **Type:** `double` - -- `Z` - - **Type:** `double` - -- `allowUnsafe` (optional, default: `true`) - - **Type:** `boolean` - - **Description:** Allow the bot to go through unsafe areas, warning: it might get hurt. - -- `allowDirectTeleport` (optional, default: `false`) - - **Type:** `boolean` - - **Description:** Allow bot to send a teleport packet. - -- `maxOffset` (optional, default: `0`) - - **Type:** `integer` - - **Description:** Maximum number of blocks from the location where the bot can stop. - -- `minOfset` (optional, default: `0`) - - **Type:** `integer` - - **Description:** Minimum number of blocks from the location where the bot can stop. - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "MoveToLocation", - "requestId": "853yb8u,6b589uj", - "parameters": [12.5, 71, 142.5] -} -``` - -### - `ClientIsMoving` - -**Description:** - -Check if the bot is currently moving. - -**Parameters:** - -- No parameters - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "ClientIsMoving", - "requestId": "539ayg88a9u63", - "parameters": [] -} -``` - -### - `LookAtLocation` - -**Description:** - -Make the bot look at a specific location. - -**Parameters:** - -- `X` - - **Type:** `double` - -- `Y` - - **Type:** `double` - -- `Z` - - **Type:** `double` - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "LookAtLocation", - "requestId": "a45g90unhu9a5t", - "parameters": [12, 71, 134] -} -``` - -### - `GetTimestamp` - -**Description:** - -Get current time in `yyyy-MM-dd HH:mm:ss` format. - -**Parameters:** - -- No parameters - -**Return type:** `string` - -**Example:** - -```json -{ - "command": "GetTimestamp", - "requestId": "87htgqq76y8g", - "parameters": [] -} -``` - -### - `GetServerPort` - -**Description:** - -Get the current server port. - -**Parameters:** - -- No parameters - -**Return type:** `int` - -**Example:** - -```json -{ - "command": "GetServerPort", - "requestId": "89u53ybq89uqb", - "parameters": [] -} -``` - -### - `GetServerHost` - -**Description:** - -Get the current server IPv4 address. - -**Parameters:** - -- No parameters - -**Return type:** `string` - -**Example:** - -```json -{ - "command": "GetServerHost", - "requestId": "hu3ay5u9h35", - "parameters": [] -} -``` - -### - `GetUsername` - -**Description:** - -Get current logged in account username. - -**Parameters:** - -- No parameters - -**Return type:** `string` - -**Example:** - -```json -{ - "command": "GetUsername", - "requestId": "8t7fhq87q6yw", - "parameters": [] -} -``` - -### - `GetGamemode` - -**Description:** - -Get the current game mode in which the bot is. - -**Parameters:** - -- No parameters - -**Return type:** `string` - -**Example:** - -```json -{ - "command": "GetGamemode", - "requestId": "5ta309h7835ty89j70", - "parameters": [] -} -``` - -### - `GetYaw` - -**Description:** - -Get current bot yaw. - -**Parameters:** - -- No parameters - -**Return type:** `double` - -**Example:** - -```json -{ - "command": "GetYaw", - "requestId": "B9Q5G380UJQ", - "parameters": [] -} -``` - -### - `GetPitch` - -**Description:** - -Get the current bot pitch. - -**Parameters:** - -- No parameters - -- **Return type:** `double` - -**Example:** - -```json -{ - "command": "GetPitch", - "requestId": "7hm4rtv2q5Y74", - "parameters": [] -} -``` - -### - `GetUserUUID` - -**Description:** - -Get the UUID of the current account. - -**Parameters:** - -- No parameters - -**Return type:** `string` - -**Example:** - -```json -{ - "command": "GetUserUUID", - "requestId": "34tva89hq986h", - "parameters": [] -} -``` - -### - `GetOnlinePlayers` - -**Description:** - -Get a list of online players on the server. - -**Parameters:** - -- No parameters - -**Return type:** `json encoded array of string` - -**Example:** - -```json -{ - "command": "GetOnlinePlayers", - "requestId": "894tvu2u8qv6", - "parameters": [] -} -``` - -### - `GetOnlinePlayersWithUUID` - -**Description:** - -Get a list of online players on the server with their nicknames and UUIDs. - -**Parameters:** - -- No parameters - -**Return type:** `json encoded array of object in the following format: { "<uuid string>": "<name string>" }` - -**Example:** - -```json -{ - "command": "GetOnlinePlayersWithUUID", - "requestId": "903fy5tv8qwu89", - "parameters": [] -} -``` - -### - `GetServerTPS` - -**Description:** - -Get the current server TPS. - -**Parameters:** - -- No parameters - -**Return type:** `integer` - -**Example:** - -```json -{ - "command": "GetServerTPS", - "requestId": "70atv4fy7890", - "parameters": [] -} -``` - -### - `InteractEntity` - -**Description:** +### `InteractEntity` Interact with an entity. **Parameters:** -- `entityId` +| Index | Type | Description | +| ----- | ------ | ----------------------------------------------------- | +| 0 | int | Entity ID | +| 1 | string | Interaction type (`Interact`, `Attack`, `InteractAt`) | +| 2 | string | Hand (optional, `MainHand` or `OffHand`) | - **Type:** `integer` +### `SendEntityAction` -- `interactionType` - - **Type:** [`InteractType` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/InteractType.cs) - -- `hand` (optional) - - **Type:** [`Hand` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/Hand.cs) - - **Default value:** `0` (Main Hand) - - You can omit this parameter if you want to interact with the main hand. - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "InteractEntity", - "requestId": "a34890u hgtv90h", - "parameters": [1452, 1] -} -``` - -### - `CreativeGive` - -**Description:** - -Give an item from the Creative Inventory. +Send an entity action. **Parameters:** -- `slot` +| Index | Type | Description | +| ----- | ------ | -------------------------------------------------- | +| 0 | string | Action type (e.g. `StartSneaking`, `StopSneaking`) | - **Type:** `integer` +### `Sneak` - **Description:** The slot id in which the items will be added to. - -- `itemType` - - **Type:** [`ItemType` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/ItemType.cs) - -- `count` - - **Type:** `integer` - - **Description** The number of items you want to give. - -- `nbt` (optional) - - **Type:** `string with json of nbt object` - - **Description** The item NBT data - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "CreativeGive", - "requestId": "sedoiuneag87", - "parameters": [12, 1, 64] -} -``` - -### - `CreativeDelete` - -**Description:** - -Clear an inventory slot of items in the Creative Mode. +Toggle sneaking. **Parameters:** -- `slot` +| Index | Type | Description | +| ----- | ---- | ---------------------------- | +| 0 | bool | true to sneak, false to stop | - **Type:** `integer` +## Movement Commands - **Description:** The slot id from which the items will be deleted from. +### `GetCurrentLocation` -**Return type:** `boolean` +Get the player's current location. +No parameters. Returns a location object with `x`, `y`, `z`. -**Example:** +### `MoveToLocation` -```json -{ - "command": "CreativeDelete", - "requestId": "09hfgq9qui0gq", - "parameters": [12] -} -``` - -### - `SendAnimation` - -**Description:** - -Send an animation, for example a hand swing. +Move the player to a location using pathfinding. **Parameters:** -- `hand` +| Index | Type | Description | +| ----- | ------ | -------------------------------- | +| 0 | double | X coordinate | +| 1 | double | Y coordinate | +| 2 | double | Z coordinate | +| 3 | bool | Allow unsafe (optional, false) | +| 4 | bool | Allow direct teleport (optional) | +| 5 | int | Max offset (optional, 0) | +| 6 | int | Min offset (optional, 0) | - **Type:** [`Hand` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/Hand.cs) +### `ClientIsMoving` - **Default value:** `0` (Main Hand) +Check if the client is currently moving. +No parameters. Returns `{ "moving": true/false }`. - You can omit this parameter if you want to interact with the main hand. +### `LookAtLocation` -**Return type:** `boolean` - - -**Example:** - -```json -{ - "command": "SendAnimation", - "requestId": "0ig09ug0iwq", - "parameters": [] -} -``` - -### - `SendPlaceBlock` - -**Description:** - -Place a block somewhere in the world. +Make the player look at coordinates. **Parameters:** -- `X` +| Index | Type | Description | +| ----- | ------ | ----------- | +| 0 | double | X | +| 1 | double | Y | +| 2 | double | Z | - **Type:** `double` +## Player Info Commands -- `Y` +### `GetUsername` - **Type:** `double` +Get the player's username. +No parameters. Returns `{ "username": "..." }`. -- `Z` +### `GetUserUUID` - **Type:** `double` +Get the player's UUID. +No parameters. Returns `{ "uuid": "..." }`. -- `direction` +### `GetGamemode` - **Type:** [`Direction` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/Direction.cs) +Get the current gamemode. +No parameters. Returns `{ "gamemode": 0 }`. -- `hand` (optional) +### `GetYaw` - **Type:** [`Hand` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/Hand.cs) +Get the player's yaw rotation. +No parameters. Returns `{ "yaw": 0.0 }`. - **Default value:** `0` (Main Hand) +### `GetPitch` -**Return type:** `boolean` +Get the player's pitch rotation. +No parameters. Returns `{ "pitch": 0.0 }`. -**Example:** +### `GetOnlinePlayers` -```json -{ - "command": "SendPlaceBlock", - "requestId": "zibgweybuini9o", - "parameters": [12, 72, 134, 4] -} -``` +Get a list of online player names. +No parameters. Returns a string array. -### - `UseItemInHand` +### `GetOnlinePlayersWithUUID` -**Description:** +Get online players with their UUIDs. +No parameters. Returns a dictionary of UUID to player name. -Use an item in the hand. +### `GetPlayersLatency` + +Get latency information for online players. +No parameters. + +## Server Info Commands + +### `GetServerHost` + +Get the server hostname. +No parameters. Returns `{ "host": "..." }`. + +### `GetServerPort` + +Get the server port. +No parameters. Returns `{ "port": 25565 }`. + +### `GetServerTPS` + +Get the server TPS (ticks per second). +No parameters. Returns `{ "tps": 20.0 }`. + +### `GetTimestamp` + +Get the current timestamp. +No parameters. Returns `{ "timestamp": "..." }`. + +### `GetProtocolVersion` + +Get the Minecraft protocol version. +No parameters. Returns `{ "protocolVersion": 769 }`. + +### `GetMaxChatMessageLength` + +Get the maximum chat message length. +No parameters. Returns `{ "length": 256 }`. + +## Inventory Commands + +### `GetInventoryEnabled` + +Check if inventory handling is enabled. +No parameters. Returns `{ "enabled": true/false }`. + +### `GetPlayerInventory` + +Get the player's inventory. +No parameters. Returns the full inventory container with items. + +Item types are serialized as string names (e.g., `"DiamondSword"`, `"Stone"`). + +### `GetInventories` + +Get all open inventories. +No parameters. + +### `WindowAction` + +Perform a window/inventory action. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ------ | ------------------------------------------------------------- | +| 0 | int | Inventory ID | +| 1 | int | Slot ID | +| 2 | string | Action type (e.g. `LeftClick`, `RightClick`, `DropItemStack`) | -**Return type:** `boolean` +### `ChangeSlot` -**Example:** - -```json -{ - "command": "UseItemInHand", - "requestId": "qat0qtg90gqtn", - "parameters": [] -} -``` - -### - `GetInventoryEnabled` - -**Description:** - -Check if the inventory is enabled. +Change the active hotbar slot. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ----- | ----------------- | +| 0 | short | Slot number (0-8) | -**Return type:** `boolean` +### `GetCurrentSlot` -**Example:** +Get the currently selected hotbar slot. +No parameters. Returns `{ "slot": 0 }`. -```json -{ - "command": "GetInventoryEnabled", - "requestId": "2t4q0j9qwg8h", - "parameters": [] -} -``` +### `SetSlot` -### - `GetPlayerInventory` - -**Description:** - -Get the items in the player inventory. +Set the active slot (legacy command). **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ---- | ----------- | +| 0 | int | Slot number | -**Return type:** [`json encoded inventory/container object`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/Container.cs) +### `ClearInventories` -**Example:** +Clear tracked inventory state. +No parameters. -```json -{ - "command": "GetPlayerInventory", - "requestId": "gbugabuiga", - "parameters": [] -} -``` +### `CloseInventory` -### - `GetInventories` - -**Description:** - -Get opened inventories list and items in them. +Close an inventory window. **Parameters:** -- No parameters +| Index | Type | Description | +| ----- | ---- | ------------ | +| 0 | int | Inventory ID | -**Return type:** [`json encoded array of inventory/container objects`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/Container.cs) +## Creative Mode Commands -**Example:** +### `CreativeGive` -```json -{ - "command": "GetPlayerInventory", - "requestId": "awgpawighago0ia", - "parameters": [] -} -``` - -### - `WindowAction` - -**Description:** - -Send an inventory action, for example a click. +Give an item in creative mode. **Parameters:** -- `windowId` +| Index | Type | Description | +| ----- | ------ | ------------------------------------------ | +| 0 | int | Slot ID | +| 1 | string | Item type (e.g. `"DiamondSword"` or `798`) | +| 2 | int | Count | - **Type:** `integer` +### `CreativeDelete` - **Description:** An id of an inventory - -- `slotId` - - **Type:** `integer` - - **Description** An id of an inventory slot - -- `windowActionType` - - **Type:** [`WindowActionType` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Inventory/WindowActionType.cs) - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "WindowAction", - "requestId": "agpoigjawg0iawg", - "parameters": [2, 14, 1] -} -``` - -### - `ChangeSlot` - -**Description:** - -Change the currently selected hot bar slot. - -**Parameters:** - `slotId` - -**Type:** `integer` - -**Description** An id of an inventory slot. - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "ChangeSlot", - "requestId": "awdadiajh0fgi", - "parameters": [2] -} -``` - -### - `GetCurrentSlot` - -**Description:** - -Get the currently selected hot bar slot. - -**Parameters:** - -- No Parameters - -**Return type:** `integer` - -**Example:** - -```json -{ - "command": "GetCurrentSlot", - "requestId": "sadg0as8h", - "parameters": [] -} -``` - -### - `ClearInventories` - -**Description:** - -Clear the list of opened inventories. - -**Parameters:** - -- No Parameters - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "ClearInventories", - "requestId": "2ouniuowaseghbnew", - "parameters": [] -} -``` - -### - `UpdateSign` - -**Description:** - -Update the text in signs. +Delete an item from a slot in creative mode. **Parameters:** -- `X` +| Index | Type | Description | +| ----- | ---- | ----------- | +| 0 | int | Slot ID | - **Type:** `double` +## Block Interaction Commands -- `Y` +### `SendPlaceBlock` - **Type:** `double` +Place a block. -- `Z` +**Parameters:** - **Type:** `double` +| Index | Type | Description | +| ----- | ------ | -------------------------------------------- | +| 0 | double | X coordinate | +| 1 | double | Y coordinate | +| 2 | double | Z coordinate | +| 3 | string | Direction (e.g. `"Up"`) | +| 4 | string | Hand (optional, `"MainHand"` or `"OffHand"`) | -- `line1` +### `SendAnimation` - **Type:** `string` +Play arm swing animation. -- `line2` +**Parameters:** - **Type:** `string` +| Index | Type | Description | +| ----- | ------ | ------------------------------------- | +| 0 | string | Hand (optional, default `"MainHand"`) | -- `line3` +### `UseItemInHand` - **Type:** `string` +Use the item currently held. +No parameters. -- `line4` +### `UpdateSign` - **Type:** `string` +Update text on a sign. -**Return type:** `boolean` +**Parameters:** -**Example:** +| Index | Type | Description | +| ----- | ------ | ------------ | +| 0 | double | X coordinate | +| 1 | double | Y coordinate | +| 2 | double | Z coordinate | +| 3 | string | Line 1 | +| 4 | string | Line 2 | +| 5 | string | Line 3 | +| 6 | string | Line 4 | -```json -{ - "command": "UpdateSign", - "requestId": "gsisgsuig0gs", - "parameters": [145, 67, 1234, "This is line 1", "This is line 2", "This is line 3", "This is line 4"] -} -``` +### `UpdateCommandBlock` -### - `SelectTrade` +Update a command block. + +**Parameters:** + +| Index | Type | Description | +| ----- | ------ | ------------------------------------------------ | +| 0 | double | X coordinate | +| 1 | double | Y coordinate | +| 2 | double | Z coordinate | +| 3 | string | Command | +| 4 | string | Mode (e.g. `"Sequence"`, `"Auto"`, `"Redstone"`) | +| 5 | string | Flags | + +## Trading Commands + +### `SelectTrade` -**Description:** Select a villager trade. **Parameters:** -- `selectedSlot` +| Index | Type | Description | +| ----- | ---- | ----------- | +| 0 | int | Trade index | - **Type:** `integer` +### `Respawn` -**Return type:** `boolean` +Respawn after death. +No parameters. -**Example:** +## Mapping Commands (New) -```json -{ - "command": "SelectTrade", - "requestId": "awdpa[9doujwapdi]", - "parameters": [2] -} -``` +These commands let clients query enum mappings dynamically at runtime, so they do not need to maintain hardcoded numeric ID tables that break across MCC versions. For background, see [issue #2805](https://github.com/MCCTeam/Minecraft-Console-Client/issues/2805). -### - `UpdateCommandBlock` +### `GetItemTypeMappings` -**Description:** +Get a dictionary of all ItemType names to their numeric IDs. +No parameters. Returns `{ "DiamondSword": 798, "Stone": 1, ... }`. -Update the command block. +### `GetEntityTypeMappings` -**Parameters:** - -- `X` - - **Type:** `double` - -- `Y` - - **Type:** `double` - -- `Z` - - **Type:** `double` - -- `command` - - **Type:** `string` - -- `mode` - - **Type:** [`CommandBlockMode` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/CommandBlockMode.cs) - -- `flags` - - **Type:** [`CommandBlockFlags` as an integer](https://github.com/MCCTeam/Minecraft-Console-Client/blob/5de84d7e5927062d867585d7fe0a0bba937ec039/MinecraftClient/Mapping/CommandBlockFlags.cs) - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "UpdateCommandBlock", - "requestId": "aw[apkda=-pd]", - "parameters": [56, 122, 34, "say This is a command", 4, 2] -} -``` - -### - `CloseInventory` - -**Description:** - -Close an inventory id. - -**Parameters:** - -- `windowId` - - **Type:** `integer` - - **Description:** Inventory Id - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "CloseInventory", - "requestId": "awpkfa0phiawd", - "parameters": [5] -} -``` - -### - `GetMaxChatMessageLength` - -**Description:** - -Get the max chat message length. - -**Parameters:** - -- No parameters - -**Return type:** `integer` - -**Example:** - -```json -{ - "command": "GetMaxChatMessageLength", - "requestId": "foajfja0fajf0i", - "parameters": [] -} -``` - -### - `Respawn` - -**Description:** - -Respawn the bot when it's dead. - -**Parameters:** - -- No parameters - -**Return type:** `boolean` - -**Example:** - -```json -{ - "command": "Respawn", - "requestId": "qawepifaihopafhio", - "parameters": [] -} -``` - -### - `GetProtocolVersion` - -**Description:** - -Get the current protocol version - -**Parameters:** - -No parameters - -**Return type:** `integer` - -**Example:** - -```json -{ - "command": "GetProtocolVersion", - "requestId": "219u2wqt-q9j-t9ujq", - "parameters": [] -} -``` +Get a dictionary of all EntityType names to their numeric IDs. +No parameters. Returns `{ "Player": 128, "Zombie": 119, ... }`. diff --git a/docs/guide/websocket/Events.md b/docs/guide/websocket/Events.md index 3a6b6cec..be86068e 100644 --- a/docs/guide/websocket/Events.md +++ b/docs/guide/websocket/Events.md @@ -1,1524 +1,578 @@ -# Web Socket Events (Web Socket Chat Bot protocol events) +# WebSocket Events -## `OnWsCommandResponse` +Events are JSON messages pushed to all authenticated WebSocket clients. +Each event has this structure: - **Description:** - - Sent by the WebSocket Chat Bot when a command was executed. +```json +{ + "event": "EventName", + "data": "{ ... serialized payload ... }" +} +``` - **Response body:** +The `data` field is a JSON string. Parse it to access the event payload. - - `success` +All enum values are serialized as **string names** (e.g., `"Zombie"` instead of `119`). - **Type:** `boolean` +## Protocol Events - **Description:** Flags the command execution as either successful if `true` or not successful if `false`. +### `OnWsCommandResponse` - - `requestId` +Sent after every command execution. - **Type:** `string` +**Payload:** - **Description:** The request Id that was sent when the command was sent to the WebSocket Chat Bot, used to track commands. (Randomly generated on each command sending) +```json +{ + "success": true, + "requestId": "your-request-id", + "message": "optional result or error message" +} +``` - - `command` +Match the `requestId` to track which command produced this response. - **Type:** `string` +### `OnMccCommandResponse` - **Description:** The command that was sent. +Sent when a plain-text MCC command (starting with `/`) is executed. - - `result` +**Payload:** - **Type:** `object` +```json +{ + "command": "move north", + "status": "Done", + "result": "" +} +``` - **Description:** The value that the command has returned. +### `OnGameJoined` - **Example:** +Sent after the client joins the server and the game session starts. +Payload: `"N/A"` - ```json - { - "event": "OnWsCommandResponse", - "data": { - "success": true, - "requestId": "ZLxcOhfMyf4SzNCqwMTx", - "command": "LogToConsole", - "result": true - } +### `OnWsRestarting` + +Sent when the WebSocket server is restarting (e.g., on reconnect). +Payload: `"N/A"` + +### `OnWsConnectionClose` + +Sent when the WebSocket server is shutting down. +Payload: `"N/A"` + +## Chat Events + +### `OnChatRaw` + +Sent for every incoming chat message, including the raw JSON. + +**Payload:** + +```json +{ + "text": "Formatted text content", + "json": "{ raw JSON from server }" +} +``` + +### `OnChatPublic` + +Sent when a public chat message is detected. + +**Payload:** + +```json +{ + "sender": "PlayerName", + "message": "Hello world", + "rawText": "<PlayerName> Hello world" +} +``` + +### `OnChatPrivate` + +Sent when a private message is detected. + +**Payload:** + +```json +{ + "sender": "PlayerName", + "message": "Secret message", + "rawText": "PlayerName whispers to you: Secret message" +} +``` + +### `OnTeleportRequest` + +Sent when a teleport request is detected. + +**Payload:** + +```json +{ + "sender": "PlayerName", + "rawText": "PlayerName has requested to teleport to you" +} +``` + +## Connection Events + +### `OnDisconnect` + +Sent when MCC disconnects from the server. + +**Payload:** + +```json +{ + "reason": "ConnectionLost", + "message": "Connection has been lost." +} +``` + +Reason values: `ConnectionLost`, `UserLogout`, `InGameKick`, `LoginRejected`. + +## Entity Events + +Entity objects include their `type` as a string name (e.g., `"Zombie"`, `"Player"`). + +### `OnEntitySpawn` + +Sent when an entity spawns. + +**Payload:** Full entity object. + +### `OnEntityDespawn` + +Sent when an entity despawns. + +**Payload:** Full entity object. + +### `OnEntityMove` + +Sent when an entity moves. + +**Payload:** Full entity object with updated location. + +### `OnEntityAnimation` + +Sent when an entity plays an animation. + +**Payload:** + +```json +{ + "entity": { ... }, + "animation": 0 +} +``` + +### `OnEntityHealth` + +Sent when an entity's health changes. + +**Payload:** + +```json +{ + "entity": { ... }, + "health": 20.0 +} +``` + +### `OnEntityMetadata` + +Sent when entity metadata updates. + +**Payload:** + +```json +{ + "entity": { ... }, + "metadata": { "0": ..., "1": ... } +} +``` + +### `OnEntityEquipment` + +Sent when an entity's equipment changes. + +**Payload:** + +```json +{ + "entity": { ... }, + "slot": 0, + "item": { "type": "DiamondSword", "count": 1, ... } +} +``` + +Item types are string names (e.g., `"DiamondSword"`). + +### `OnEntityEffect` + +Sent when an entity gets an effect. + +**Payload:** + +```json +{ + "entity": { ... }, + "effect": "Speed", + "amplifier": 1, + "duration": 600, + "flags": 0 +} +``` + +### `OnBlockBreakAnimation` + +Sent when a block break animation plays. + +**Payload:** + +```json +{ + "entity": { ... }, + "location": { "x": 10, "y": 64, "z": -20 }, + "stage": 5 +} +``` + +## Player Events + +### `OnPlayerJoin` + +Sent when a player joins the server. + +**Payload:** + +```json +{ + "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "name": "PlayerName" +} +``` + +### `OnPlayerLeave` + +Sent when a player leaves the server. + +**Payload:** + +```json +{ + "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "name": "PlayerName" +} +``` + +### `OnPlayerProperty` + +Sent when player properties update (e.g., speed, attack damage). + +**Payload:** Dictionary of property name to value. + +### `OnPlayerStatus` + +Sent when the player's status changes. + +**Payload:** + +```json +{ + "statusId": 0 +} +``` + +### `OnDeath` + +Sent when the player dies. +Payload: `"N/A"` + +### `OnRespawn` + +Sent when the player respawns. +Payload: `"N/A"` + +## Health and Experience Events + +### `OnHealthUpdate` + +Sent when the player's health or food level changes. + +**Payload:** + +```json +{ + "health": 20.0, + "food": 20 +} +``` + +### `OnSetExperience` + +Sent when experience updates. + +**Payload:** + +```json +{ + "experienceBar": 0.5, + "level": 10, + "totalExperience": 200 +} +``` + +## Game Events + +### `OnGamemodeUpdate` + +Sent when a player's gamemode changes. + +**Payload:** + +```json +{ + "playerName": "Steve", + "uuid": "...", + "gamemode": 1 +} +``` + +### `OnLatencyUpdate` + +Sent when a player's latency changes. + +**Payload:** + +```json +{ + "playerName": "Steve", + "uuid": "...", + "latency": 42 +} +``` + +### `OnHeldItemChange` + +Sent when the held item slot changes. + +**Payload:** + +```json +{ + "slot": 0 +} +``` + +### `OnExplosion` + +Sent when an explosion occurs. + +**Payload:** + +```json +{ + "location": { "x": 10, "y": 64, "z": -20 }, + "strength": 4.0, + "recordcount": 12 +} +``` + +### `OnTitle` + +Sent when a title, subtitle, or action bar message is displayed. + +**Payload:** + +```json +{ + "action": 0, + "titleText": "Welcome", + "subtitleText": "", + "actionBarText": "", + "fadeIn": 10, + "stay": 70, + "fadeOut": 20, + "json": "..." +} +``` + +## Server Events + +### `OnServerTpsUpdate` + +Sent when the server TPS updates. + +**Payload:** + +```json +{ + "tps": 20.0 +} +``` + +### `OnTimeUpdate` + +Sent when the world time updates. + +**Payload:** + +```json +{ + "worldAge": 1000000, + "timeOfDay": 6000 +} +``` + +### `OnInternalCommand` + +Sent when an MCC internal command is executed. + +**Payload:** + +```json +{ + "commandName": "move", + "commandParams": "north", + "result": { + "status": "Done", + "result": "" } - ``` +} +``` -# MCC Events +## Inventory Events -## `OnBlockBreakAnimation` +### `OnInventoryUpdate` - **Description:** +Sent when an inventory's contents change. - Sent when a block is broken in the world. +**Payload:** - **Parameters:** +```json +{ + "inventoryId": 0 +} +``` - - `Entity` +### `OnInventoryOpen` - **Type:** `Entity json encoded object` +Sent when an inventory window opens. - - `Location` +**Payload:** - **Type:** `Location json encoded object` +```json +{ + "inventoryId": 1 +} +``` - - `stage` +### `OnInventoryClose` - **Type:** `integer` - - **Example:** +Sent when an inventory window closes. - ```json - { - "event": "OnBlockBreakAnimation", - "data": {} - } - ``` +**Payload:** -## `OnEntityAnimation` +```json +{ + "inventoryId": 1 +} +``` - **Description:** +## Scoreboard Events - Sent when an entity does an animation. +### `OnScoreboardObjective` - **Parameters:** +Sent when a scoreboard objective updates. - - `Entity` +**Payload:** - **Type:** `Entity json encoded object` +```json +{ + "objectiveName": "health", + "mode": 0, + "objectiveValue": "Health", + "type": 0, + "json": "...", + "numberFormat": 0 +} +``` - - `animation` +### `OnUpdateScore` - **Type:** `integer` - - **Example:** +Sent when a scoreboard score updates. - ```json - { - "event": "OnEntityAnimation", - "data": { - "entity": { - "ID":8, - "UUID":"8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "Name":"someplayer", - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":77, - "Location":{ - "X":-46.08784180879593, - "Y":68, - "Z":147.68046873807907, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":9, - "ChunkBlockX":1, - "ChunkBlockY":4, - "ChunkBlockZ":3 - }, - "Yaw":178.59375, - "Pitch":28.125, - "ObjectData":-1, - "Health":20, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":{ - "6":0 - }, - "Equipment": {} - }, - "animation":0 - } - } - ``` +**Payload:** -## `OnChatPrivate` +```json +{ + "entityName": "Steve", + "action": 0, + "objectiveName": "health", + "objectiveDisplayName": "Health", + "value": 20, + "numberFormat": 0 +} +``` - **Description:** +## Map and Trade Events - Sent when the MCC receives a private chat message. +### `OnMapData` - **Parameters:** +Sent when map data updates. - - `sender` +**Payload:** - **Type:** `string` +```json +{ + "mapId": 0, + "scale": 1, + "trackingPosition": true, + "locked": false, + "icons": [], + "columnsUpdated": 128, + "rowsUpdated": 128, + "mapColumnX": 0, + "mapRowZ": 0, + "colors": "base64-encoded-string" +} +``` - - `message` +Note: `colors` is base64-encoded when present, `null` otherwise. - **Type:** `string` +### `OnTradeList` - - `rawText` +Sent when a villager trade list is received. - **Type:** `string` - - **Example:** +**Payload:** - ```json - { - "event": "OnChatPublic", - "data": { - "sender":"milutinke", - "message":"hey there", - "rawText":"milutinke whispers to you: hey there" - } - } - ``` +```json +{ + "windowId": 1, + "trades": [...], + "villagerInfo": { ... } +} +``` -## `OnChatPublic` +## Network Events - **Description:** +### `OnNetworkPacket` - Sent when a public message was sent in the chat. +Sent for every network packet (when subscribed). - **Parameters:** +**Payload:** - - `username` +```json +{ + "packetID": 42, + "data": "base64-encoded-packet-data", + "isLogin": false, + "isInbound": true +} +``` - **Type:** `string` - - - `message` - - **Type:** `string` - - - `rawText` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnChatPublic", - "data": { - "username":"milutinke", - "message":"hello world", - "rawText":"<milutinke> hello world" - } - } - ``` - -## `OnTeleportRequest` - - **Description:** - - Sent when the bot gets a teleport request - - **Parameters:** - - - `sender` - - **Type:** `string` - - - `rawText` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnTeleportRequest", - "data": { - "sender": "milutinke", - "rawText": "Milutinke want's to teleport to you. Type /tpaccept to accept the teleport request." - } - } - ``` - -## `OnChatRaw` - - **Description:** - - Sent when any kind of chat message was received by the MCC. Can contain JSON. - - **Parameters:** - - - `text` - - **Type:** `string` - - - `json` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnChatRaw", - "data": { - "text":"someplayer has made the advancement §a[§aCover Me with Diamonds]", - "json":"{\"translate\":\"chat.type.advancement.task\",\"with\":[{\"insertion\":\"someplayer\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/tell someplayer \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":{\"type\":\"minecraft:player\",\"id\":\"8c0e3dc3-9bcc-3e03-a138-53348330d4ee\",\"name\":{\"text\":\"someplayer\"}}},\"text\":\"someplayer\"},{\"color\":\"green\",\"translate\":\"chat.square_brackets\",\"with\":[{\"hoverEvent\":{\"action\":\"show_text\",\"contents\":{\"color\":\"green\",\"extra\":[{\"text\":\"\\n\"},{\"translate\":\"advancements.story.shiny_gear.description\"}],\"translate\":\"advancements.story.shiny_gear.title\"}},\"translate\":\"advancements.story.shiny_gear.title\"}]}]}" - } - } - ``` - -## `OnDisconnect` - - **Description:** - - Sent when the bot has disconnected from a server. At this point you can't send commands to the MCC. - - **Parameters:** - - - `reason` - - **Type:** `string` - - - `message` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnDisconnect", - "data": { - "reason": "<reason json encoded object>", - "message": "<message json encoded object>" - } - } - ``` - -## `OnPlayerProperty` - - **Description:** - - Sent when the server need to update a player property - - **Parameters:** - - - `prop` - - **Type:** `json encoded object of { string key: double/number value }` - - **Example:** - - ```json - { - "event": "OnPlayerProperty", - "data": { - "minecraft:generic.movement_speed": 0.10000000149011612 - } - } - ``` - -## `OnServerTpsUpdate` - - **Description:** - - Sent when the server TPS changes/updates. - - **Parameters:** - - - `tps` - - **Type:** `double` - - **Example:** - - ```json - { - "event": "OnServerTpsUpdate", - "data": { - "tps": 20.0 - } - } - ``` - -## `OnTimeUpdate` - - **Description:** - - Sent when the world time changes. - - **NOTE: Sent quite frequently.** - - **Parameters:** - - - `worldAge` - - **Type:** `long` - - - `timeOfDay` - - **Type:** `long` - - **Example:** - - ```json - { - "event": "OnTimeUpdate", - "data": { - "worldAge": 1719192, - "timeOfDay": -1132 - } - } - ``` - -## `OnEntityMove` - - **Description:** - - Sent when an entity moves. - - **NOTE: Sent quite frequently.** - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` - - **Example:** - - ```json - { - "event": "OnEntityMove", - "data": { - "ID":16, - "UUID":"00000000-0000-0000-0000-000000000000", - "Name":null, - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":14, - "Location":{ - "X":5.5, - "Y":-47.9375, - "Z":204.5, - "Status":0, - "ChunkX":0, - "ChunkY":1, - "ChunkZ":12, - "ChunkBlockX":5, - "ChunkBlockY":0, - "ChunkBlockZ":12 - }, - "Yaw":0, - "Pitch":0, - "ObjectData":0, - "Health":1, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":null, - "Equipment": {} - } - } - ``` - -## `OnInternalCommand` - - **Description:** - - Sent when an internal MCC command has been executed. - - **Parameters:** - - - `command` - - **Type:** `string` - - - `parameters` - - **Type:** `string` - - - `result` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnInternalCommand", - "data": { - "command": "dig -115 74 -19", - "parameters": "-115 74 -19", - "result": "Attempting to dig block at -114,5 74 -18,5 (Grass Block)" - } - } - ``` - -## `OnEntitySpawn` - - **Description:** - - Sent when an entity is spawned or enters the player radius. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` - - **Example:** - - ```json - { - "event": "OnEntitySpawn", - "data": { - "ID":78, - "UUID":"00000000-0000-0000-0000-000000000000", - "Name":null, - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":15, - "Location":{ - "X":-47.5, - "Y":68, - "Z":146.5, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":9, - "ChunkBlockX":0, - "ChunkBlockY":4, - "ChunkBlockZ":2 - }, - "Yaw":30.9375, - "Pitch":0, - "ObjectData":0, - "Health":1, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":null, - "Equipment":{ } - } - } - ``` - -## `OnEntityDespawn` - - **Description:** - - Sent when an entity is de-spawned or leaves the player radius. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` - - **Example:** - - ```json - { - "event": "OnEntityDespawn", - "data": { - "ID":15, - "UUID":"00000000-0000-0000-0000-000000000000", - "Name":null, - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":56, - "Location":{ - "X":-38.818737210380526, - "Y":68, - "Z":194.05856433486986, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":12, - "ChunkBlockX":9, - "ChunkBlockY":4, - "ChunkBlockZ":2 - }, - "Yaw":0, - "Pitch":0, - "ObjectData":0, - "Health":1, - "Item":{ - "Type":396, - "Count":1, - "NBT":{ }, - "IsEmpty":false, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":{ - "8":{ - "Type":396, - "Count":1, - "NBT":{ - - }, - "IsEmpty":false, - "DisplayName":null, - "Lores":null, - "Damage":0 - } - }, - "Equipment":{ } - } - } - ``` - -### - `OnHeldItemChange` - - **Description:** - - Sent when a held item is changed. - - **Parameters:** - - - `itemSlot` - - **Type:** `integer` - - **Example:** - - ```json - { - "event": "OnHeldItemChange", - "data": { - "itemSlot": 1 - } - } - ``` - -### - `OnHealthUpdate` - - **Description:** - - Sent when player's health is updated. - - **Parameters:** - - - `health` - - **Type:** `float` - - - `food` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnHealthUpdate", - "data": { - "health": 18, - "food": 7 - } - } - ``` - -### - `OnExplosion` - - **Description:** - - Sent when there is an explosion. - - **Parameters:** - - - `Location` - - **Type:** `Location json encoded object` - - - `strength` - - **Type:** `float` - - - `recordCount` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnExplosion", - "data": { - "location": { - "X": -117.49000000953674, - "Y": 66.0612500011921, - "Z": -26.490000009536743, - "Status": 0, - "ChunkX": -8, - "ChunkY": 8, - "ChunkZ": -2, - "ChunkBlockX": 10, - "ChunkBlockY": 2, - "ChunkBlockZ": 5 - }, - "strength": 4, - "recordCount": 139 - } - } - ``` - -### - `OnSetExperience` - - **Description:** - - Sent when the player's experience is updated. - - **Parameters:** - - - `experienceBar` - - **Type:** `float` - - - `level` - - **Type:** `int` - -- `totalExperience` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnSetExperience", - "data": { - "experienceBar": 0.60504204, - "level": 7, - "totalExperience": 120 - } - } - ``` - -### - `OnGamemodeUpdate` - - **Description:** - - Sent when the player's game mode has changed. - - **Parameters:** - - - `playerName` - - **Type:** `string` - - - `uuid` - - **Type:** `string with UUID` - - - `gameMode` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnGamemodeUpdate", - "data": { - "playerName": "milutinke", - "uuid": "8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "gameMode": "creative" - } - } - ``` - -### - `OnLatencyUpdate` - - **Description:** - - Sent when the player's ping has changed. - - **Parameters:** - -- `playerName` - - **Type:** `string` - -- `uuid` - - **Type:** `string with UUID` - -- `latency` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnLatencyUpdate", - "data": { - "playerName": "someplayer", - "uuid":"baa6eda2-cbc5-5119-870d-1960ce60574d", - "latency": 14 - } - } - ``` - -### - `OnMapData` - - **Description:** - - Sent when map data is received. - - **Parameters:** - - - `mapId` - - **Type:** `int` - - - `scale` - - **Type:** `integer` - - - `trackingPosition` - - **Type:** `bool` - - - `locked` - - **Type:** `bool` - - - `icons` - - **Type:** `array of map icon object` - - - `columnsUpdated` - - **Type:** `integer` - - - `rowsUpdated` - - **Type:** `integer` - - - `mapColumnX` - - **Type:** `integer` - - - `mapRowZ` - - **Type:** `integer` - - - `colors` - - **Type:** `base 64 encoded string of colors` - - **Example:** - - ```json - { - "event": "OnMapData", - "data": { - "mapId": 1, - "scale": 0, - "trackingPosition": true, - "locked": false, - "icons": [], - "columnsUpdated": 128, - "rowsUpdated": 128, - "mapColumnX": 0, - "mapRowZ": 0, - "colors": null // ommited in this example, too long - } - } - ``` - -### - `OnTradeList` - - **Description:** - - Sent when villager's trade list has been received/updated. - - **Parameters:** - - - `windowId` - - **Type:** `int` - - - `trades` - - **Type:** `List<VillagerTrade>` - - - `villagerInfo` - - **Type:** `VillagerInfo` - - -### - `OnTitle` - - **Description:** - - Sent when a title action has been received. - - **Parameters:** - - `action` - - **Type:** `int` - - - `titleText` - - **Type:** `string` - - - `subtitleText` - - **Type:** `string` - - - `actionBarText` - - **Type:** `string` - - - `fadeIn` - - **Type:** `int` - - - `stay` - - **Type:** `int` - - - `fadeout` - - **Type:** `int` - - - `json_` - - **Type:** `string` - -### - `OnEntityEquipment` - - **Description:** - - Sent when entity has changed or equipped equipment. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` (nullable) - - - `slot` - - **Type:** `int` - - - `item` - - **Type:** `Item?` - - **Example:** - - ```json - { - "event": "OnEntityEquipment", - "data": { - "entity":{ - "ID":8, - "UUID":"8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "Name":"someplayer", - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":77, - "Location":{ - "X":-46.88311344939438, - "Y":68, - "Z":146.96050249975414, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":9, - "ChunkBlockX":1, - "ChunkBlockY":4, - "ChunkBlockZ":2 - }, - "Yaw":178.59375, - "Pitch":28.125, - "ObjectData":-1, - "Health":20, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":{ - "6":0 - }, - "Equipment":{ - "0":{ - "Type":368, - "Count":1, - "NBT":{ - "Damage":0 - }, - "IsEmpty":false, - "DisplayName":null, - "Lores":null, - "Damage":0 - } - } - }, - "slot":0, - "item":{ - "Type":368, - "Count":1, - "NBT":{ - "Damage":0 - }, - "IsEmpty":false, - "DisplayName":null, - "Lores":null, - "Damage":0 - } - } - } - ``` - -### - `OnEntityEffect` - **Description:** - Sent when there are effects applied to an entity. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` - - - `effect` - - **Type:** `Effects` - - - `amplifier` - - **Type:** `int` - - - `duration` - - **Type:** `int` - - - `flags` - - **Type:** `integer` - - **Example:** - - ```json - { - "event": "OnEntityEffect", - "data": { - "entity": { - "ID": 50, - "UUID": "8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "Name": "milutinke", - "CustomNameJson": null, - "IsCustomNameVisible": false, - "CustomName": null, - "Latency": 0, - "Type": 77, - "Location": { - "X": -116.15188604696566, - "Y": 74.79847191937456, - "Z": -22.679173221632723, - "Status": 0, - "ChunkX": -8, - "ChunkY": 8, - "ChunkZ": -2, - "ChunkBlockX": 11, - "ChunkBlockY": 10, - "ChunkBlockZ": 9 - }, - "Yaw": 330.46875, - "Pitch": 9.84375, - "ObjectData": -1, - "Health": 20, - "Item": { - "Type": 18, - "Count": 0, - "NBT": null, - "IsEmpty": true, - "DisplayName": null, - "Lores": null, - "Damage": 0 - }, - "Pose": 0, - "Metadata": { - "9": 20, - "11": true, - "16": 122, - "17": 127 - }, - "Equipment": {} - }, - "effect": 33, - "amplifier": 0, - "duration": 77, - "flags": 0 - } - } - ``` - -### - `OnScoreboardObjective` - - **Description:** - - Sent when scoreboard objective has been added. - - **Parameters:** - - - `objectiveName` - - **Type:** `string` - - - `mode` - - **Type:** `integer` - - - `objectiveValue` - - **Type:** `string` - - - `type` - - **Type:** `int` - - - `json_` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnScoreboardObjective", - "data": { - "objectiveName": "testObj", - "mode": 0, - "objectiveValue": "Test Objective", - "type": 0, - "rawJson": "{\"text\":\"Testobj\"}" - } - } - ``` - -### - `OnUpdateScore` - - **Description:** - - Sent when scoreboard objective has been update/changed for an entity. - - **Parameters:** - - - `entityName` - - **Type:** `string` - - - `action` - - **Type:** `int` - - - `objectiveName` - - **Type:** `string` - - - `type` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnUpdateScore", - "data": { - "entityName": "test entity", - "action": 1, - "objectiveName": "test_objective", - "type": 1 - } - } - ``` - -### - `OnInventoryUpdate` - - **Description:** - - Sent when the an inventory has been updated. - - **Parameters:** - - - `inventoryId` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnInventoryUpdate", - "data": { - "inventoryId": 4 - } - } - ``` - -### - `OnInventoryOpen` - - **Description:** - - Sent when a player opens an inventory. - - **Parameters:** - - - `inventoryId` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnInventoryOpen", - "data": { - "inventoryId": 5 - } - } - ``` - -### - `OnInventoryClose` - - **Description:** - - Sent when a player/server closes an inventory. - - **Parameters:** - - - `inventoryId` - - **Type:** `int` - - **Example:** - - ```json - { - "event": "OnInventoryClose", - "data": { - "inventoryId": 4 - } - } - ``` - -### - `OnPlayerJoin` - - **Description:** - - Sent when a player joins the server. (Not the bot) - - **Parameters:** - - - `uuid` - - **Type:** `string with UUID` - - - `name` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnPlayerJoin", - "data": { - "uuid": "8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "name": "milutinke" - } - } - ``` - -### - `OnPlayerLeave` - - **Description:** - - Sent when a player leaves the server. (Not the bot) - - **Parameters:** - - - `uuid` - - **Type:** `string with UUID` - - - `name` - - **Type:** `string` - - **Example:** - - ```json - { - "event": "OnPlayerLeave", - "data": { - "uuid":"8c0e3dc3-9bcc-3e03-a138-53348330d4ee", - "name":"milutinke" - } - } - ``` - -### - `OnDeath` - - **Description:** - - Sent when the bot dies. - - **Parameters:** None - - **Example:** - - ```json - { - "event": "OnDeath", - "data": null - } - ``` - -### - `OnRespawn` - - **Description:** - - Sent when the bot respawns. - - **Parameters:** None - - **Example:** - - ```json - { - "event": "OnRespawn", - "data": null - } - ``` - -### - `OnEntityHealth` - - **Description:** - - Sent when an entity health changes/updates. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` (nullable) - - - `health` - - **Type:** `float` - - **Example:** - - ```json - { - "event": "OnEntityHealth", - "data": { - "entity":{ - "ID":78, - "UUID":"00000000-0000-0000-0000-000000000000", - "Name":null, - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":15, - "Location":{ - "X":-47.5, - "Y":68, - "Z":146.5, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":9, - "ChunkBlockX":0, - "ChunkBlockY":4, - "ChunkBlockZ":2 - }, - "Yaw":30.9375, - "Pitch":0, - "ObjectData":0, - "Health":3, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":{ - "9":4 - }, - "Equipment":{ - - } - }, - "health":3 - } - } - ``` - -### - `OnEntityMetadata` - - **Description:** - - Sent when entity's metadata has been received/updated/changed. - - **Parameters:** - - - `Entity` - - **Type:** `Entity json encoded object` - - - `metadata` - - **Type:** `Object of number as a key and object as value` (nullable) - - **Example:** - - ```json - { - "event": "OnEntityMetadata", - "data": { - "entity":{ - "ID":78, - "UUID":"00000000-0000-0000-0000-000000000000", - "Name":null, - "CustomNameJson":null, - "IsCustomNameVisible":false, - "CustomName":null, - "Latency":0, - "Type":15, - "Location":{ - "X":-47.5, - "Y":68, - "Z":146.5, - "Status":0, - "ChunkX":-3, - "ChunkY":8, - "ChunkZ":9, - "ChunkBlockX":0, - "ChunkBlockY":4, - "ChunkBlockZ":2 - }, - "Yaw":30.9375, - "Pitch":0, - "ObjectData":0, - "Health":3, - "Item":{ - "Type":18, - "Count":0, - "NBT":null, - "IsEmpty":true, - "DisplayName":null, - "Lores":null, - "Damage":0 - }, - "Pose":0, - "Metadata":{ - "9":3 - }, - "Equipment":{ - - } - }, - "metadata":{ - "9":3 - } - } - } - ``` - -### - `OnPlayerStatus` - - **Description:** - - Sent when player's status has been updated/changed. - - **Parameters:** - - - `statusId` - - **Type:** `integer` - - **Example:** - - ```json - { - "event": "OnPlayerStatus", - "data": { - "statusId": 5 - } - } - ``` - -### - `OnNetworkPacket` - - **Description:** - - Sent when player's status has been updated/changed. - - **Parameters:** - - - `packetId` - - **Type:** `integer` - - - `isLogin` - - **Type:** `boolean` - - **Description:** Is the packet sent during the `login` phase. (Always `false`) - - - `isInbound` - - **Type:** `integer` - - **Description:** Is the packet sent from the server or by the MCC. - - - `packetData` - - **Type:** `array of bytes` - - **Description:** A raw byte array. \ No newline at end of file +Note: `data` is base64-encoded. This event generates heavy traffic and is mainly useful for debugging. diff --git a/docs/guide/websocket/README.md b/docs/guide/websocket/README.md index b5eff123..1f4af2f1 100644 --- a/docs/guide/websocket/README.md +++ b/docs/guide/websocket/README.md @@ -1,142 +1,132 @@ -# Web Socket Chat Bot documentation +# WebSocket Bot -This is a documentation page on the Web Socket chat bot and on how to make a library that uses web socket to execute commands in the MCC and processes events sent by the MCC. +The WebSocket Bot is an **external example bot** that lets you remotely control MCC over WebSocket. +It runs a local WebSocket server inside your MCC session, accepts commands as JSON messages, and pushes game events back to connected clients in real time. -Please read the [Important things](#important-things) before everything. +::: warning External Bot +This bot is **not** built into MCC. +You load it as a standalone script with `/script ChatBots/WebSocketBot.cs`. +::: -# Page index +## Quick Start -- [Important things](#important-things) - - [Prerequisites](#prerequisites) - - [Limitations](#limitations) - - [Precision of information](#precisionvalidity-of-the-information-in-this-guide) -- [How does it work?](#how-does-it-work) -- [Sending commands](#sending-commands-to-mcc) -- [Websocket Commands](Commands.md) -- [Websocket Events](Events.md) -- [Reference Implementation](#reference-implementation) +1. Copy `config/ChatBots/WebSocketBot.cs` into your MCC `config/ChatBots/` folder (it ships in the repo under that path). +2. Open the file and edit the line near the top: + ```csharp + MCC.LoadBot(new WebSocketBot("127.0.0.1", 8043, "CHANGE_THIS_PASSWORD")); + ``` + - Replace `127.0.0.1` with the IP to bind (use `+` or `*` for all interfaces). + - Replace `8043` with your preferred port. + - Replace `CHANGE_THIS_PASSWORD` with a strong, unique password. +3. Optionally enable debug logging: + ```csharp + MCC.LoadBot(new WebSocketBot("127.0.0.1", 8043, "mypassword", debugMode: true)); + ``` +4. In MCC, run: `/script ChatBots/WebSocketBot.cs` -## Reference implementation +The bot starts a WebSocket server. Connect to `ws://127.0.0.1:8043/` with any WebSocket client. -I have made a reference implementation in TypeScript/JavaScript, it is avaliable here: +## Protocol Overview -[https://github.com/milutinke/MCC.js](https://github.com/milutinke/MCC.js) +All communication uses JSON over WebSocket text frames. -It is great for better understanding how this works. - -## Important things - -### Prerequisites - -This guide/documentation assumes that you have enough of programming knowledge to know: - - - What Web Socket is - - Basics of networking and concurency - - What JSON is - - What are the various data types such as boolean, integer, long, float, double, object, dictionary/hash map - -Without knowing those, I highly recommend learning about those concepts before trying to implement your own library. - -### Limitations - -The Web Socket chat bot should be considered experimental and prone to change, it has not been fully tested and might change, keep an eye on updates on our official Discord server. - -### Precision/Validity of the information in this guide - -This guide has been mostly generated from the code itself, so the types are C# types, except in few cases where I have manually changed them. - -For some thing you will have to dig in to the MCC C# code of the Chat Bot and various helper classes. - -**Some information sent by the MCC, for example entity metadata, block ids, item ids, or various other data is different for each Minecraft Version, thus you need to map it for each minecraft version.** - -Some events might not be that useful, eg. `OnNetworkPacket` - -## How does it work? - -So, basically, this Web Socket Chat Bot is a chat bot that has a Web Socket server running while you're connected to a minecraft server. - -It sends events, and listens for commands and responds to commands. - -It has build in authentication, which requires you to send a command to authenticate if the the password is set, if it is not set, it should automatically authenticate you on the first command. - -You also can name every connection (session) with an alias. - -The flow of the protocol is the following: +### Authentication Flow ``` -Connect to the chat bot via web socket - - | - | - \ / - ` - -Optionally set a session alias/name with "ChangeSessionId" command -(this can be done multiple times at any point) - - | - | - \ / - ` - -Send an "Authenticate" command if there is a password set - - | - | - \ / - ` - -Send commands and listen for events +Connect via WebSocket + | + v +(Optional) Send "ChangeSessionId" to set a friendly session name + | + v +Send "Authenticate" with the configured password + | + v +Send commands and receive events ``` -In order to implement a library that communicates witht this chat bot, you need to make a way to send commands, remember the sent commands via the `requestId` value, and listen for `OnWsCommandResponse` event in which you need to detect if your command has been executed by looking for the `requestId` that matches the one you've sent. I also recommend you put a 5-10 seconds command execution timeout, where you discard the command if it has not been executed in the given timeout range. +### Sending Commands -## Sending commands to MCC - -You can send text in the chat, execute client commands or execute remote procedures (WebSocket Chat Bot commands). - -Each thing that is sent to the chat bot results in a response through the [`OnWsCommandResponse`](#onwscommandresponse) event. - -### Sending chat messages - -To send a chat message just send a plain text with your message to via the web socket. - -### Executing client commands - -To execute a client command, just send plain text with your command. - -Example: `/move suth` - -### Execution remote procedures (WebSocket Chat Bot commands) - -In order to execute a remote procedure, you need to send a json encoded string in the following format: +Commands are JSON objects with this shape: ```json { - "command": "<command name here>", - "requestId": "<randomly generated string for identification>", - "parameters": [ 1, "some string", true, "etc.." ] + "command": "CommandName", + "requestId": "any-unique-string", + "parameters": [1, "text", true] } ``` -#### `command` +- `command` - the procedure name (case-sensitive) +- `requestId` - a client-generated ID so you can match responses to requests +- `parameters` - an ordered array of arguments (types depend on the command) - Refers to the name of the command +Every command produces an `OnWsCommandResponse` event with `success`, `requestId`, and optionally `message`. -#### `requestId` +### Sending Plain Text - Is a unique indentifier you generate on each command, it will be returned in the response of the command execution ([`OnWsCommandResponse`](#onwscommandresponse)), use it to track if a command has been successfully executed or not, and to get the return value if it has been successfully executed. (*It's recommended to generate at least 7 characters to avoid collision, best to use an UUID format*). +You can also send plain text directly: -#### `parameters` - - Are parameters (attibutes) of the procedure you're executing, they're sent as an array of data of various types, the Web Socket chat bot does parsing and conversion and returns an error if you have sent a wrong type for the given parameters, of if you haven't send enough of them. +- Text starting with `/` is forwarded to MCC as an internal command (e.g., `/move north`). +- Other text is sent as chat. - **Example:** +### Receiving Events - ```json - { - "command": "Authenticate", - "requestId": "8w9u60-q39ik", - "parameters": ["wspass12345"] - } - ``` \ No newline at end of file +Events arrive as JSON: + +```json +{ + "event": "EventName", + "data": "{ ... serialized payload ... }" +} +``` + +The `data` field is a JSON string that you parse separately to get the event payload. + +## Enum Serialization (String Names) + +All enum values (ItemType, EntityType, Direction, Hand, etc.) are serialized as **string names**, not numeric IDs. + +For example, an entity of type `Zombie` appears as: + +```json +{ "type": "Zombie", "location": { "x": 10, "y": 64, "z": -20 } } +``` + +When sending commands that accept enum parameters, you can pass **either** a string name or a numeric value: + +```json +{ "command": "InteractEntity", "requestId": "abc", "parameters": [42, "Interact", "MainHand"] } +``` + +or: + +```json +{ "command": "InteractEntity", "requestId": "abc", "parameters": [42, 0, 0] } +``` + +Two dedicated commands let you query the full mapping tables: + +- `GetItemTypeMappings` returns `{ "DiamondSword": 798, "Stone": 1, ... }` +- `GetEntityTypeMappings` returns `{ "Player": 128, "Zombie": 119, ... }` + +These are useful if your client needs a name-to-ID lookup for the current MCC version. + +## Reference + +- [Commands](Commands.md) - full list of available commands +- [Events](Events.md) - full list of emitted events + +<div class="custom-container tip"><p class="custom-container-title">⭐ Reference Implementation: MCC.js</p> + +[MCC.js](https://github.com/milutinke/MCC.js) is a Node.js/TypeScript library built for this bot. It handles authentication, JSON serialization, event subscriptions, and typed command wrappers out of the box. + +If you're writing a client in JavaScript or TypeScript, start there. + +</div> + +## Compatibility + +- Requires any MCC version that supports `/script` (standalone MCCScript 1.0 bots). +- Uses only `System.Text.Json` (built into .NET), so no extra DLLs are needed. +- Compatible with [MCC.js](https://github.com/milutinke/MCC.js) and any WebSocket client library. diff --git a/docs/package.json b/docs/package.json index b032f2eb..46d50a0b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,18 +7,23 @@ "license": "CDDL-1.0", "private": false, "scripts": { - "docs:build": "vuepress-cli build --clean-cache", + "docs:build": "vuepress build . --clean-cache", "docs:clean": "rimraf .vuepress/.temp .vuepress/.cache .vuepress/dist", - "docs:dev": "vuepress-cli dev --clean-cache", + "docs:dev": "vuepress dev . --clean-cache", "docs:serve": "anywhere -s -h localhost -d .vuepress/dist" }, "devDependencies": { - "@vuepress/bundler-webpack": "^2.0.0-beta.53", - "@vuepress/plugin-search": "^2.0.0-beta.53", - "@vuepress/plugin-shiki": "^2.0.0-beta.53", - "vuepress": "^2.0.0-beta.53", - "vuepress-plugin-mermaidjs": "2.0.0-beta.2", - "vuepress-plugin-redirect": "^2.0.0-beta.120" + "@vuepress/bundler-vite": "2.0.0-rc.26", + "@vuepress/bundler-webpack": "2.0.0-rc.26", + "@vuepress/plugin-markdown-chart": "2.0.0-rc.125", + "@vuepress/plugin-redirect": "2.0.0-rc.125", + "@vuepress/plugin-search": "2.0.0-rc.125", + "@vuepress/plugin-shiki": "2.0.0-rc.125", + "@vuepress/theme-default": "2.0.0-rc.125", + "mermaid": "11.15.0", + "sass-embedded": "1.98.0", + "sass-loader": "16.0.7", + "vuepress": "2.0.0-rc.26" }, "dependencies": { "anywhere": "^1.6.0" diff --git a/docs/yarn.lock b/docs/yarn.lock index 7c3cb3be..e3295e07 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@antfu/install-pkg@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz#78fa036be1a6081b5a77a5cf59f50c7752b6ba26" + integrity sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== + dependencies: + package-manager-detector "^1.3.0" + tinyexec "^1.0.1" + "@babel/code-frame@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -9,11 +17,21 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + "@babel/helper-validator-identifier@^7.18.6": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -23,25 +41,337 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.16.4": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.1.tgz#3e045a92f7b4623cafc2425eddcb8cf2e54f9cc5" - integrity sha512-hp0AYxaZJhxULfM1zyp7Wgr+pSUKBcP3M+PHnSzWGdXOzg/kHWIgiUWARvubhUKGOEw3xqY4x+lyZ9ytBVcELw== +"@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" -"@braintree/sanitize-url@^3.1.0": +"@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@braintree/sanitize-url@^7.1.1": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz#ca2035b0fefe956a8676ff0c69af73e605fcd81f" + integrity sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA== + +"@bufbuild/protobuf@^2.5.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.11.0.tgz#3ec3985c9074b23aea337957225fe15a0e845f8e" + integrity sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ== + +"@chevrotain/types@~11.1.1": + version "11.1.2" + resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-11.1.2.tgz#e83a1a2704f0c5e49e7592b214031a0f4a34d7e5" + integrity sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw== + +"@esbuild/aix-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" + integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== + +"@esbuild/aix-ppc64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz#4c585002f7ad694d38fe0e8cbf5cfd939ccff327" + integrity sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q== + +"@esbuild/android-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" + integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== + +"@esbuild/android-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz#7625d0952c3b402d3ede203a16c9f2b78f8a4827" + integrity sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw== + +"@esbuild/android-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" + integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== + +"@esbuild/android-arm@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.4.tgz#9a0cf1d12997ec46dddfb32ce67e9bca842381ac" + integrity sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ== + +"@esbuild/android-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" + integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== + +"@esbuild/android-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.4.tgz#06e1fdc6283fccd6bc6aadd6754afce6cf96f42e" + integrity sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw== + +"@esbuild/darwin-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== + +"@esbuild/darwin-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz#6c550ee6c0273bcb0fac244478ff727c26755d80" + integrity sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ== + +"@esbuild/darwin-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" + integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== + +"@esbuild/darwin-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz#ed7a125e9f25ce0091b9aff783ee943f6ba6cb86" + integrity sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw== + +"@esbuild/freebsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" + integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== + +"@esbuild/freebsd-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz#597dc8e7161dba71db4c1656131c1f1e9d7660c6" + integrity sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw== + +"@esbuild/freebsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" + integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== + +"@esbuild/freebsd-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz#ea171f9f4f00efaa8e9d3fe8baa1b75d757d1b36" + integrity sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ== + +"@esbuild/linux-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" + integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== + +"@esbuild/linux-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz#e52d57f202369386e6dbcb3370a17a0491ab1464" + integrity sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA== + +"@esbuild/linux-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" + integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== + +"@esbuild/linux-arm@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz#5e0c0b634908adbce0a02cebeba8b3acac263fb6" + integrity sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg== + +"@esbuild/linux-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" + integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== + +"@esbuild/linux-ia32@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz#5f90f01f131652473ec06b038a14c49683e14ec7" + integrity sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA== + +"@esbuild/linux-loong64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" + integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== + +"@esbuild/linux-loong64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz#63bacffdb99574c9318f9afbd0dd4fff76a837e3" + integrity sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA== + +"@esbuild/linux-mips64el@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" + integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== + +"@esbuild/linux-mips64el@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz#c4b6952eca6a8efff67fee3671a3536c8e67b7eb" + integrity sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw== + +"@esbuild/linux-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" + integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== + +"@esbuild/linux-ppc64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz#6dea67d3d98c6986f1b7769e4f1848e5ae47ad58" + integrity sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA== + +"@esbuild/linux-riscv64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" + integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== + +"@esbuild/linux-riscv64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz#9ad2b4c3c0502c6bada9c81997bb56c597853489" + integrity sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw== + +"@esbuild/linux-s390x@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" + integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== + +"@esbuild/linux-s390x@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz#c43d3cfd073042ca6f5c52bb9bc313ed2066ce28" + integrity sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA== + +"@esbuild/linux-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== + +"@esbuild/linux-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz#45fa173e0591ac74d80d3cf76704713e14e2a4a6" + integrity sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA== + +"@esbuild/netbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" + integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== + +"@esbuild/netbsd-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz#366b0ef40cdb986fc751cbdad16e8c25fe1ba879" + integrity sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q== + +"@esbuild/netbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" + integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== + +"@esbuild/netbsd-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz#e985d49a3668fd2044343071d52e1ae815112b3e" + integrity sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg== + +"@esbuild/openbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" + integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== + +"@esbuild/openbsd-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz#6fb4ab7b73f7e5572ce5ec9cf91c13ff6dd44842" + integrity sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow== + +"@esbuild/openbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" + integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== + +"@esbuild/openbsd-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz#641f052040a0d79843d68898f5791638a026d983" + integrity sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ== + +"@esbuild/openharmony-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" + integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== + +"@esbuild/openharmony-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz#fc1d33eac9d81ae0a433b3ed1dd6171a20d4e317" + integrity sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg== + +"@esbuild/sunos-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" + integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== + +"@esbuild/sunos-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz#af2cd5ca842d6d057121f66a192d4f797de28f53" + integrity sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g== + +"@esbuild/win32-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" + integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== + +"@esbuild/win32-arm64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz#78ec7e59bb06404583d4c9511e621db31c760de3" + integrity sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg== + +"@esbuild/win32-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" + integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== + +"@esbuild/win32-ia32@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz#0e616aa488b7ee5d2592ab070ff9ec06a9fddf11" + integrity sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw== + +"@esbuild/win32-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" + integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== + +"@esbuild/win32-x64@0.27.4": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz#1f7ba71a3d6155d44a6faa8dbe249c62ab3e408c" + integrity sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg== + +"@iconify/types@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@iconify/types/-/types-2.0.0.tgz#ab0e9ea681d6c8a1214f30cd741fe3a20cc57f57" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== + +"@iconify/utils@^3.0.2": version "3.1.0" - resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-3.1.0.tgz#8ff71d51053cd5ee4981e5a501d80a536244f7fd" - integrity sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg== + resolved "https://registry.yarnpkg.com/@iconify/utils/-/utils-3.1.0.tgz#fb41882915f97fee6f91a2fbb8263e8772ca0438" + integrity sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw== + dependencies: + "@antfu/install-pkg" "^1.1.0" + "@iconify/types" "^2.0.0" + mlly "^1.8.0" -"@esbuild/android-arm@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.12.tgz#e548b10a5e55b9e10537a049ebf0bc72c453b769" - integrity sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA== +"@jest/pattern@30.0.1": + version "30.0.1" + resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" + integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== + dependencies: + "@types/node" "*" + jest-regex-util "30.0.1" -"@esbuild/linux-loong64@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz#475b33a2631a3d8ca8aa95ee127f9a61d95bf9c1" - integrity sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw== +"@jest/schemas@30.0.5": + version "30.0.5" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" + integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== + dependencies: + "@sinclair/typebox" "^0.34.0" + +"@jest/types@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.3.0.tgz#cada800d323cb74945c24ac74615fdb312a6c85f" + integrity sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw== + dependencies: + "@jest/pattern" "30.0.1" + "@jest/schemas" "30.0.5" + "@types/istanbul-lib-coverage" "^2.0.6" + "@types/istanbul-reports" "^3.0.4" + "@types/node" "*" + "@types/yargs" "^17.0.33" + chalk "^4.1.2" "@jridgewell/gen-mapping@^0.3.0": version "0.3.2" @@ -52,16 +382,35 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" @@ -70,12 +419,38 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -83,102 +458,722 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jsonjoy.com/base64@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-17.67.0.tgz#7eeda3cb41138d77a90408fd2e42b2aba10576d7" + integrity sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw== + +"@jsonjoy.com/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/buffers@17.67.0", "@jsonjoy.com/buffers@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz#5c58dbcdeea8824ce296bd1cfce006c2eb167b3d" + integrity sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw== + +"@jsonjoy.com/buffers@^1.0.0", "@jsonjoy.com/buffers@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz#8d99c7f67eaf724d3428dfd9826c6455266a5c83" + integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== + +"@jsonjoy.com/codegen@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz#3635fd8769d77e19b75dc5574bc9756019b2e591" + integrity sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q== + +"@jsonjoy.com/codegen@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz#5c23f796c47675f166d23b948cdb889184b93207" + integrity sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g== + +"@jsonjoy.com/fs-core@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-core/-/fs-core-4.57.1.tgz#03c0d7a7bf96030376f7194b9c5c815cb7bf71d7" + integrity sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.57.1" + "@jsonjoy.com/fs-node-utils" "4.57.1" + thingies "^2.5.0" + +"@jsonjoy.com/fs-fsa@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.1.tgz#87ffa6cd695b363b58b9ccddc87a66212a1b25fd" + integrity sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA== + dependencies: + "@jsonjoy.com/fs-core" "4.57.1" + "@jsonjoy.com/fs-node-builtins" "4.57.1" + "@jsonjoy.com/fs-node-utils" "4.57.1" + thingies "^2.5.0" + +"@jsonjoy.com/fs-node-builtins@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.1.tgz#a6793654d6ffaead81f040e3becc063a265deb7c" + integrity sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og== + +"@jsonjoy.com/fs-node-to-fsa@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.1.tgz#9011872df67ac302f0b0f7fd13502993a026c306" + integrity sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg== + dependencies: + "@jsonjoy.com/fs-fsa" "4.57.1" + "@jsonjoy.com/fs-node-builtins" "4.57.1" + "@jsonjoy.com/fs-node-utils" "4.57.1" + +"@jsonjoy.com/fs-node-utils@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.1.tgz#e9d030b676f7f4074eb90a42927bac708dc4312c" + integrity sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.57.1" + +"@jsonjoy.com/fs-node@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node/-/fs-node-4.57.1.tgz#3dae969fe02d9450f5dfc7c12bfe3b859cb1038e" + integrity sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ== + dependencies: + "@jsonjoy.com/fs-core" "4.57.1" + "@jsonjoy.com/fs-node-builtins" "4.57.1" + "@jsonjoy.com/fs-node-utils" "4.57.1" + "@jsonjoy.com/fs-print" "4.57.1" + "@jsonjoy.com/fs-snapshot" "4.57.1" + glob-to-regex.js "^1.0.0" + thingies "^2.5.0" + +"@jsonjoy.com/fs-print@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-print/-/fs-print-4.57.1.tgz#59359be175145cd44e83f7cdfba06cb1fed23313" + integrity sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw== + dependencies: + "@jsonjoy.com/fs-node-utils" "4.57.1" + tree-dump "^1.1.0" + +"@jsonjoy.com/fs-snapshot@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.1.tgz#54cd9073a97e290a1650070f2ee9529a0accdb93" + integrity sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg== + dependencies: + "@jsonjoy.com/buffers" "^17.65.0" + "@jsonjoy.com/fs-node-utils" "4.57.1" + "@jsonjoy.com/json-pack" "^17.65.0" + "@jsonjoy.com/util" "^17.65.0" + +"@jsonjoy.com/json-pack@^1.11.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz#93f8dd57fe3a3a92132b33d1eb182dcd9e7629fa" + integrity sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg== + dependencies: + "@jsonjoy.com/base64" "^1.1.2" + "@jsonjoy.com/buffers" "^1.2.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/json-pointer" "^1.0.2" + "@jsonjoy.com/util" "^1.9.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pack@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz#8dd8ff65dd999c5d4d26df46c63915c7bdec093a" + integrity sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w== + dependencies: + "@jsonjoy.com/base64" "17.67.0" + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" + "@jsonjoy.com/json-pointer" "17.67.0" + "@jsonjoy.com/util" "17.67.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pointer@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz#74439573dc046e0c9a3a552fb94b391bc75313b8" + integrity sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA== + dependencies: + "@jsonjoy.com/util" "17.67.0" + +"@jsonjoy.com/json-pointer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz#049cb530ac24e84cba08590c5e36b431c4843408" + integrity sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg== + dependencies: + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/util" "^1.9.0" + +"@jsonjoy.com/util@17.67.0", "@jsonjoy.com/util@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-17.67.0.tgz#7c4288fc3808233e55c7610101e7bb4590cddd3f" + integrity sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew== + dependencies: + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" + +"@jsonjoy.com/util@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.9.0.tgz#7ee95586aed0a766b746cd8d8363e336c3c47c46" + integrity sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ== + dependencies: + "@jsonjoy.com/buffers" "^1.0.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@mdit-vue/plugin-component@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-component/-/plugin-component-0.11.1.tgz#0ffd542a6ef26655a6c48c8f255fe1ac4f3db6fc" - integrity sha512-fCqyYPwEXFa182/Vz6g8McDi3SCIwm3yHWkWddHx+QNn0gMGFqkhJVcz/wjCIA3oCoWUBWM80aZ09ZuoQiOmvQ== +"@mdit-vue/plugin-component@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-component/-/plugin-component-3.0.2.tgz#bf26d37a770811943a38a9758605e918e523ff4d" + integrity sha512-Fu53MajrZMOAjOIPGMTdTXgHLgGU9KwTqKtYc6WNYtFZNKw04euSfJ/zFg8eBY/2MlciVngkF7Gyc2IL7e8Bsw== dependencies: - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/plugin-frontmatter@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-0.11.1.tgz#4e4e013bf151fa54525f4e9c7c0a829912364ccb" - integrity sha512-AdZJInjD1pTJXlfhuoBS5ycuIQ3ewBfY0R/XHM3TRDEaDHQJHxouUCpCyijZmpdljTU45lFetIowaKtAi7GBog== +"@mdit-vue/plugin-frontmatter@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-3.0.2.tgz#2f0b73e8b103b6aa79253e143407ce9299f839ff" + integrity sha512-QKKgIva31YtqHgSAz7S7hRcL7cHXiqdog4wxTfxeQCHo+9IP4Oi5/r1Y5E93nTPccpadDWzAwr3A0F+kAEnsVQ== dependencies: - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" gray-matter "^4.0.3" - markdown-it "^13.0.1" + markdown-it "^14.1.0" -"@mdit-vue/plugin-headers@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-headers/-/plugin-headers-0.11.1.tgz#246c56102f3ab197afa2a8c87fe669afb87df735" - integrity sha512-eBUonsEkXP2Uf2MIXSWZGCcLCIMSA1XfThJwhzSAosoa7fO5aw52LKCweddmn7zLQvgQh7p7382sFAhCc2KXog== +"@mdit-vue/plugin-headers@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-headers/-/plugin-headers-3.0.2.tgz#17f41ce4f461ff3d3d52f72fc697ce5b00ab3a00" + integrity sha512-Z3PpDdwBTO5jlW2r617tQibkwtCc5unTnj/Ew1SCxTQaXjtKgwP9WngdSN+xxriISHoNOYzwpoUw/1CW8ntibA== dependencies: - "@mdit-vue/shared" "0.11.0" - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@mdit-vue/shared" "3.0.2" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/plugin-sfc@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-sfc/-/plugin-sfc-0.11.1.tgz#1e7102ea3f67f0761e482ac50c413f7e10e1ba41" - integrity sha512-3AjQXqExzT9FWGNOeTBqK1pbt1UA5anrZvjo7OO2PJ3lrfZd0rbjionFkmW/VW1912laHUraIP6n74mUNqPuWw== +"@mdit-vue/plugin-sfc@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-sfc/-/plugin-sfc-3.0.2.tgz#80084da4a19d387e75048ea54dc4c030609255e7" + integrity sha512-dhxIrCGu5Nd4Cgo9JJHLjdNy2lMEv+LpimetBHDSeEEJxJBC4TPN0Cljn+3/nV1uJdGyw33UZA86PGdgt1LsoA== dependencies: - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/plugin-title@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-title/-/plugin-title-0.11.1.tgz#98e116bc64d59b380a529f22d077dc105f6e862f" - integrity sha512-lvgR1pSgwX5D3tmLGyYBsfd3GbEoscqYsLTE8Vg+rCY8LfSrHdwrOD3Eg+SM2KyS5+gn+Zw4nS0S1yxOIVZBCQ== +"@mdit-vue/plugin-title@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-title/-/plugin-title-3.0.2.tgz#265f942794466105a680a327c69503e43e34630d" + integrity sha512-KTDP7s68eKTwy4iYp5UauQuVJf+tDMdJZMO6K4feWYS8TX95ItmcxyX7RprfBWLTUwNXBYOifsL6CkIGlWcNjA== dependencies: - "@mdit-vue/shared" "0.11.0" - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@mdit-vue/shared" "3.0.2" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/plugin-toc@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-toc/-/plugin-toc-0.11.1.tgz#81394518fd48e54a94e6c41d804270c2b37761bf" - integrity sha512-1tkGb1092ZgLhoSmE5hkC6U0IRGG5bWhUY4p14npV4cwqntciXEoXRqPA1jGEDh5hnofZC0bHbeS3uKxsmAEew== +"@mdit-vue/plugin-toc@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-toc/-/plugin-toc-3.0.2.tgz#a768b3b22e1045669463ea5b3f44b6421da3ae2a" + integrity sha512-Dz0dURjD5wR4nBxFMiqb0BTGRAOkCE60byIemqLqnkF6ORKKJ8h5aLF5J5ssbLO87hwu81IikHiaXvqoiEneoQ== dependencies: - "@mdit-vue/shared" "0.11.0" - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@mdit-vue/shared" "3.0.2" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/shared@0.11.0", "@mdit-vue/shared@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@mdit-vue/shared/-/shared-0.11.0.tgz#c4b2554795fd1924302fe7f7fee2b5fb412aa578" - integrity sha512-eiGe42y7UYpjO6/8Lg6OpAtzZrRU9k8dhpX1e/kJMTcL+tn+XkqRMJJ8I2pdrOQMSkgvIva5FNAriykqFzkdGg== +"@mdit-vue/shared@3.0.2", "@mdit-vue/shared@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/shared/-/shared-3.0.2.tgz#dbe3ab5bad165c0b9cdba62dbd3c3a8116e2997b" + integrity sha512-anFGls154h0iVzUt5O43EaqYvPwzfUxQ34QpNQsUQML7pbEJMhcgkRNvYw9hZBspab+/TP45agdPw5joh6/BBA== dependencies: - "@mdit-vue/types" "0.11.0" - "@types/markdown-it" "^12.2.3" - markdown-it "^13.0.1" + "@mdit-vue/types" "3.0.2" + "@types/markdown-it" "^14.1.2" + markdown-it "^14.1.0" -"@mdit-vue/types@0.11.0", "@mdit-vue/types@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@mdit-vue/types/-/types-0.11.0.tgz#ab9c6f4e69d9c9eaabf1a73e59dc699875b224ef" - integrity sha512-ygCGP7vFpqS02hpZwEe1uz8cfImWX06+zRs08J+tCZRKb6k+easIaIHFtY9ZSxt7j9L/gAPLDo/5RmOT6z0DPQ== +"@mdit-vue/types@3.0.2", "@mdit-vue/types@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@mdit-vue/types/-/types-3.0.2.tgz#bcc569c5435ecb38b750a58ce69e555cf0021120" + integrity sha512-00aAZ0F0NLik6I6Yba2emGbHLxv+QYrPH00qQ5dFKXlAo1Ll2RHDXwY7nN2WAfrx2pP+WrvSRFTGFCNGdzBDHw== -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== +"@mdit/helper@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@mdit/helper/-/helper-0.23.1.tgz#c7ee7ce42f26ff7e61febc978288099a4fb3bc15" + integrity sha512-ifWDG3VbUAx1ia7eBWEHm5vpv5QFUPY3kFLPPZzYBr15A7/d5w7D+8ZBg8xxqkvyC73Ys+zF14EQCq7eQAXYxg== dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" + "@types/markdown-it" "^14.1.2" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== +"@mdit/plugin-alert@^0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@mdit/plugin-alert/-/plugin-alert-0.23.1.tgz#c017f37619796b5428b275c78c318305f01086b3" + integrity sha512-vbWxewra32hfZKF+XeeWK/eoAzQbe0cSRfSattX9oxGOcaEbcVx2/g7nmI9//ItsOKO7XNRy7ZKLdnm+CaMPvg== dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" + "@types/markdown-it" "^14.1.2" + +"@mdit/plugin-container@^0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@mdit/plugin-container/-/plugin-container-0.23.1.tgz#d5b0a44f21c6aecabeb9fb2721b769f01dcdae35" + integrity sha512-mHTp4+zvuE6uqhG6honfR6F5wLgAIcLlGVCu8xHIoO6H8Oc23lrjl+8Ieyr+PKLH3Lz0QFQf0fWdwNi44EsYSg== + dependencies: + "@types/markdown-it" "^14.1.2" + +"@mdit/plugin-plantuml@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@mdit/plugin-plantuml/-/plugin-plantuml-0.24.1.tgz#80750e6098c61bc7ac8f1ae9611b8efbd8295ee3" + integrity sha512-tRPAnofSMjrrCypghiBDyqyF0cH/wBzS0zjSVjfc+RfMgURt3B4OKvXDc+PsXU6MvJPXVKuMW1ngM4nddPtUyg== + dependencies: + "@mdit/plugin-uml" "0.24.1" + "@types/markdown-it" "^14.1.2" + +"@mdit/plugin-tab@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@mdit/plugin-tab/-/plugin-tab-0.24.1.tgz#db23ce9a692627ac1a2b048a3e43a6b60d9bc2cb" + integrity sha512-DSRNyGEBnEgqd1Pw3gt1ropVJv0n5AMCJREY4iq2GNUtxdzNP8jGO7UdXqdnmUPXTWSUZkE7pPu7tvL+38dBHQ== + dependencies: + "@mdit/helper" "0.23.1" + "@types/markdown-it" "^14.1.2" + +"@mdit/plugin-uml@0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@mdit/plugin-uml/-/plugin-uml-0.24.1.tgz#15616891fb9f7a36281092255c9988c07aa6381e" + integrity sha512-e/aStB1zb9HwV0KtBIkh7z68ZRW9TnmLTZ+kCZt7HbNywGQvRlHv8myZ0BWVAe5Gbo5LH+aFRSVE72pJ9QP1Xg== + dependencies: + "@mdit/helper" "0.23.1" + "@types/markdown-it" "^14.1.2" + +"@mermaid-js/parser@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-1.1.1.tgz#30f3ab68d816912e43f245a72a0d4081bf69d966" + integrity sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw== + dependencies: + "@chevrotain/types" "~11.1.1" + +"@noble/hashes@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@parcel/watcher-android-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" + integrity sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A== + +"@parcel/watcher-darwin-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz#88d3e720b59b1eceffce98dac46d7c40e8be5e8e" + integrity sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA== + +"@parcel/watcher-darwin-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz#bf05d76a78bc15974f15ec3671848698b0838063" + integrity sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg== + +"@parcel/watcher-freebsd-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz#8bc26e9848e7303ac82922a5ae1b1ef1bdb48a53" + integrity sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng== + +"@parcel/watcher-linux-arm-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz#1328fee1deb0c2d7865079ef53a2ba4cc2f8b40a" + integrity sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ== + +"@parcel/watcher-linux-arm-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz#bad0f45cb3e2157746db8b9d22db6a125711f152" + integrity sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg== + +"@parcel/watcher-linux-arm64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz#b75913fbd501d9523c5f35d420957bf7d0204809" + integrity sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA== + +"@parcel/watcher-linux-arm64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz#da5621a6a576070c8c0de60dea8b46dc9c3827d4" + integrity sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA== + +"@parcel/watcher-linux-x64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz#ce437accdc4b30f93a090b4a221fd95cd9b89639" + integrity sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ== + +"@parcel/watcher-linux-x64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz#02400c54b4a67efcc7e2327b249711920ac969e2" + integrity sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg== + +"@parcel/watcher-win32-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz#caae3d3c7583ca0a7171e6bd142c34d20ea1691e" + integrity sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q== + +"@parcel/watcher-win32-ia32@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz#9ac922550896dfe47bfc5ae3be4f1bcaf8155d6d" + integrity sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g== + +"@parcel/watcher-win32-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz#73fdafba2e21c448f0e456bbe13178d8fe11739d" + integrity sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw== + +"@parcel/watcher@^2.4.1": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.6.tgz#3f932828c894f06d0ad9cfefade1756ecc6ef1f1" + integrity sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ== + dependencies: + detect-libc "^2.0.3" + is-glob "^4.0.3" + node-addon-api "^7.0.0" + picomatch "^4.0.3" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.5.6" + "@parcel/watcher-darwin-arm64" "2.5.6" + "@parcel/watcher-darwin-x64" "2.5.6" + "@parcel/watcher-freebsd-x64" "2.5.6" + "@parcel/watcher-linux-arm-glibc" "2.5.6" + "@parcel/watcher-linux-arm-musl" "2.5.6" + "@parcel/watcher-linux-arm64-glibc" "2.5.6" + "@parcel/watcher-linux-arm64-musl" "2.5.6" + "@parcel/watcher-linux-x64-glibc" "2.5.6" + "@parcel/watcher-linux-x64-musl" "2.5.6" + "@parcel/watcher-win32-arm64" "2.5.6" + "@parcel/watcher-win32-ia32" "2.5.6" + "@parcel/watcher-win32-x64" "2.5.6" + +"@peculiar/asn1-cms@^2.6.0", "@peculiar/asn1-cms@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz#cb5445c1bad9197d176073bf142a5c035b460640" + integrity sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-csr@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz#9629d403bc5a61254f28ed0b90e99cee61c0e8be" + integrity sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-ecc@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz#d29c4af671508a9934edc78e7c9419fbf7bc9870" + integrity sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pfx@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz#75cddd14d43ef875109e91ea150377d679c8fbc1" + integrity sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-rsa" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs8@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz#bd56b4bb9e8a3702369049713a89134c87c6931a" + integrity sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs9@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz#ddc5222952f25b59a0562a6f8cabdb72f586a496" + integrity sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pfx" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-rsa@^2.6.0", "@peculiar/asn1-rsa@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz#2cdf9f9ea6d6fdbaae214b9fed6de0534b654437" + integrity sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-schema@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz#0dca1601d5b0fed2a72fed7a5f1d0d7dbe3a6f82" + integrity sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg== + dependencies: + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509-attr@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz#6425008b8099476010aace5b8ae9f9cbc41db0ab" + integrity sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509@^2.6.0", "@peculiar/asn1-x509@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz#4e8995659e16178e0e90fe90519aa269045af262" + integrity sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/x509@^1.14.2": + version "1.14.3" + resolved "https://registry.yarnpkg.com/@peculiar/x509/-/x509-1.14.3.tgz#2c44c2b89474346afec38a0c2803ec4fb8ce959e" + integrity sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA== + dependencies: + "@peculiar/asn1-cms" "^2.6.0" + "@peculiar/asn1-csr" "^2.6.0" + "@peculiar/asn1-ecc" "^2.6.0" + "@peculiar/asn1-pkcs9" "^2.6.0" + "@peculiar/asn1-rsa" "^2.6.0" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.0" + pvtsutils "^1.3.6" + reflect-metadata "^0.2.2" + tslib "^2.8.1" + tsyringe "^4.10.0" + +"@pkgr/core@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== + +"@rolldown/pluginutils@1.0.0-rc.2": + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz#10324e74cb3396cb7b616042ea7e9e6aa7d8d458" + integrity sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw== + +"@rollup/rollup-android-arm-eabi@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz#7e158ddfc16f78da99c0d5ccbae6cae403ef3284" + integrity sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A== + +"@rollup/rollup-android-arm64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz#49f4ae0e22b6f9ffbcd3818b9a0758fa2d10b1cd" + integrity sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw== + +"@rollup/rollup-darwin-arm64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz#bb200269069acf5c1c4d79ad142524f77e8b8236" + integrity sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA== + +"@rollup/rollup-darwin-x64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz#1bf7a92b27ebdd5e0d1d48503c7811160773be1a" + integrity sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw== + +"@rollup/rollup-freebsd-arm64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz#5ccf537b99c5175008444702193ad0b1c36f7f16" + integrity sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw== + +"@rollup/rollup-freebsd-x64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz#1196ecd7bf4e128624ef83cd1f9d785114474a77" + integrity sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA== + +"@rollup/rollup-linux-arm-gnueabihf@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz#cc147633a4af229fee83a737bf2334fbac3dc28e" + integrity sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g== + +"@rollup/rollup-linux-arm-musleabihf@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz#3559f9f060153ea54594a42c3b87a297bedcc26e" + integrity sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ== + +"@rollup/rollup-linux-arm64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz#e91f887b154123485cfc4b59befe2080fcd8f2df" + integrity sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A== + +"@rollup/rollup-linux-arm64-musl@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz#660752f040df9ba44a24765df698928917c0bf21" + integrity sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ== + +"@rollup/rollup-linux-loong64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz#cb0e939a5fa479ccef264f3f45b31971695f869c" + integrity sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw== + +"@rollup/rollup-linux-loong64-musl@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz#42f86fbc82cd1a81be2d346476dd3231cf5ee442" + integrity sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog== + +"@rollup/rollup-linux-ppc64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz#39776a647a789dc95ea049277c5ef8f098df77f9" + integrity sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ== + +"@rollup/rollup-linux-ppc64-musl@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz#466f20029a8e8b3bb2954c7ddebc9586420cac2c" + integrity sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg== + +"@rollup/rollup-linux-riscv64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz#cff9877c78f12e7aa6246f6902ad913e99edb2b7" + integrity sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA== + +"@rollup/rollup-linux-riscv64-musl@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz#9a762fb99b5a82a921017f56491b7e892b9fb17d" + integrity sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ== + +"@rollup/rollup-linux-s390x-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz#9d25ad8ac7dab681935baf78ac5ea92d14629cdf" + integrity sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ== + +"@rollup/rollup-linux-x64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz#5e5139e11819fa38a052368da79422cb4afcf466" + integrity sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg== + +"@rollup/rollup-linux-x64-musl@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz#b6211d46e11b1f945f5504cc794fce839331ed08" + integrity sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw== + +"@rollup/rollup-openbsd-x64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz#e6e09eebaa7012bb9c7331b437a9e992bd94ca35" + integrity sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw== + +"@rollup/rollup-openharmony-arm64@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz#f7d99ae857032498e57a5e7259fb7100fd24a87e" + integrity sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA== + +"@rollup/rollup-win32-arm64-msvc@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz#41e392f5d9f3bf1253fdaf2f6d6f6b1bfc452856" + integrity sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ== + +"@rollup/rollup-win32-ia32-msvc@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz#f41b0490be0e5d3cf459b4dc076a192b532adea9" + integrity sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w== + +"@rollup/rollup-win32-x64-gnu@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz#0fcf9f1fcb750f0317b13aac3b3231687e6397a5" + integrity sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA== + +"@rollup/rollup-win32-x64-msvc@4.60.0": + version "4.60.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz#3afdb30405f6d4248df5e72e1ca86c5eab55fab8" + integrity sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w== + +"@shikijs/core@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-4.0.2.tgz#386a00acc6965ced582e9066bfb237de7ee99174" + integrity sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw== + dependencies: + "@shikijs/primitive" "4.0.2" + "@shikijs/types" "4.0.2" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.5" + +"@shikijs/engine-javascript@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz#d49b766c23fb6e71c19b9a797ff5357c8a61db5e" + integrity sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag== + dependencies: + "@shikijs/types" "4.0.2" + "@shikijs/vscode-textmate" "^10.0.2" + oniguruma-to-es "^4.3.4" + +"@shikijs/engine-oniguruma@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz#41ed06adcc4a4e6f49e05643dfe0d772dbb19c2b" + integrity sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg== + dependencies: + "@shikijs/types" "4.0.2" + "@shikijs/vscode-textmate" "^10.0.2" + +"@shikijs/langs@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-4.0.2.tgz#1ac31a223d74729cf230441f9bb7d7975384101f" + integrity sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg== + dependencies: + "@shikijs/types" "4.0.2" + +"@shikijs/primitive@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/primitive/-/primitive-4.0.2.tgz#4efa1efab1b828c20563c2097d2effa5ac79bf04" + integrity sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw== + dependencies: + "@shikijs/types" "4.0.2" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + +"@shikijs/themes@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-4.0.2.tgz#24c5c059e89a8e7630fb40a240bc6b5a336bb080" + integrity sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA== + dependencies: + "@shikijs/types" "4.0.2" + +"@shikijs/transformers@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/transformers/-/transformers-4.0.2.tgz#aefcf084326b3c8a218fc9aa33950ab2e5aacd5a" + integrity sha512-1+L0gf9v+SdDXs08vjaLb3mBFa8U7u37cwcBQIv/HCocLwX69Tt6LpUCjtB+UUTvQxI7BnjZKhN/wMjhHBcJGg== + dependencies: + "@shikijs/core" "4.0.2" + "@shikijs/types" "4.0.2" + +"@shikijs/types@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-4.0.2.tgz#75180a19acf124b37f48b53a9e6373de2e2e4f28" + integrity sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg== + dependencies: + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + +"@sinclair/typebox@^0.34.0": + version "0.34.48" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.48.tgz#75b0ead87e59e1adbd6dccdc42bad4fddee73b59" + integrity sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA== "@types/body-parser@*": version "1.19.2" @@ -188,17 +1183,17 @@ "@types/connect" "*" "@types/node" "*" -"@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== +"@types/bonjour@^3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" -"@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" @@ -210,38 +1205,243 @@ dependencies: "@types/node" "*" -"@types/debug@^4.1.7": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" - integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== +"@types/d3-array@*": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.2.tgz#e02151464d02d4a1b44646d0fcdb93faf88fde8c" + integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== + +"@types/d3-axis@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795" + integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-brush@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c" + integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-chord@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d" + integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-contour@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231" + integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== + dependencies: + "@types/d3-array" "*" + "@types/geojson" "*" + +"@types/d3-delaunay@*": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1" + integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw== + +"@types/d3-dispatch@*": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz#ef004d8a128046cfce434d17182f834e44ef95b2" + integrity sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA== + +"@types/d3-drag@*": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" + integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-dsv@*": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17" + integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== + +"@types/d3-ease@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-fetch@*": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980" + integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== + dependencies: + "@types/d3-dsv" "*" + +"@types/d3-force@*": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.10.tgz#6dc8fc6e1f35704f3b057090beeeb7ac674bff1a" + integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== + +"@types/d3-format@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90" + integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== + +"@types/d3-geo@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440" + integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== + dependencies: + "@types/geojson" "*" + +"@types/d3-hierarchy@*": + version "3.1.7" + resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b" + integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== + +"@types/d3-interpolate@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" + integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== + +"@types/d3-polygon@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c" + integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== + +"@types/d3-quadtree@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f" + integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== + +"@types/d3-random@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb" + integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== + +"@types/d3-scale-chromatic@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39" + integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== + +"@types/d3-scale@*": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" + integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== + dependencies: + "@types/d3-time" "*" + +"@types/d3-selection@*": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" + integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== + +"@types/d3-shape@*": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.8.tgz#d1516cc508753be06852cd06758e3bb54a22b0e3" + integrity sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time-format@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2" + integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== + +"@types/d3-time@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" + integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + +"@types/d3-timer@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + +"@types/d3-transition@*": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" + integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-zoom@*": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" + integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + +"@types/d3@^7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2" + integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== + dependencies: + "@types/d3-array" "*" + "@types/d3-axis" "*" + "@types/d3-brush" "*" + "@types/d3-chord" "*" + "@types/d3-color" "*" + "@types/d3-contour" "*" + "@types/d3-delaunay" "*" + "@types/d3-dispatch" "*" + "@types/d3-drag" "*" + "@types/d3-dsv" "*" + "@types/d3-ease" "*" + "@types/d3-fetch" "*" + "@types/d3-force" "*" + "@types/d3-format" "*" + "@types/d3-geo" "*" + "@types/d3-hierarchy" "*" + "@types/d3-interpolate" "*" + "@types/d3-path" "*" + "@types/d3-polygon" "*" + "@types/d3-quadtree" "*" + "@types/d3-random" "*" + "@types/d3-scale" "*" + "@types/d3-scale-chromatic" "*" + "@types/d3-selection" "*" + "@types/d3-shape" "*" + "@types/d3-time" "*" + "@types/d3-time-format" "*" + "@types/d3-timer" "*" + "@types/d3-transition" "*" + "@types/d3-zoom" "*" + +"@types/debug@^4.1.12": + version "4.1.13" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.13.tgz#22d1cc9d542d3593caea764f974306ab36286ee7" + integrity sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw== dependencies: "@types/ms" "*" -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.4.9" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.9.tgz#f7371980148697f4b582b086630319b55324b5aa" - integrity sha512-jFCSo4wJzlHQLCpceUhUnXdrPuCNOjGFMQ8Eg6JXxlz3QaCKOb7eGi2cephQdM4XTYsNej69P9JDJ1zqNIbncQ== + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - -"@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": version "4.17.31" @@ -252,7 +1452,17 @@ "@types/qs" "*" "@types/range-parser" "*" -"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.14": +"@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": + version "4.19.8" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz#99b960322a4d576b239a640ab52ef191989b036f" + integrity sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": version "4.17.14" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== @@ -262,23 +1472,51 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/fs-extra@^9.0.13": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== +"@types/express@^4.17.23", "@types/express@^4.17.25": + version "4.17.25" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.25.tgz#070c8c73a6fee6936d65c195dbbfb7da5026649b" + integrity sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw== dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "^1" + +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" "@types/node" "*" -"@types/hash-sum@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/hash-sum/-/hash-sum-1.0.0.tgz#838f4e8627887d42b162d05f3d96ca636c2bc504" - integrity sha512-FdLBT93h3kcZ586Aee66HPCVJ6qvxVjBlDWNmxSGSbCZe9hTsjRKdSsl4y1T+3zfujxo9auykQMnFsfyHWD7wg== +"@types/geojson@*": + version "7946.0.16" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + +"@types/hash-sum@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/hash-sum/-/hash-sum-1.0.2.tgz#32e6e4343ee25914b2a3822f27e8e641ca534f63" + integrity sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw== + +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + "@types/http-proxy@^1.17.8": version "1.17.9" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" @@ -286,41 +1524,84 @@ dependencies: "@types/node" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== -"@types/linkify-it@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" - integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== - -"@types/markdown-it-emoji@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz#f12a97df2758f38b4b38f277b468780459faff14" - integrity sha512-2ln8Wjbcj/0oRi/6VnuMeWEHHuK8uapFttvcLmDIe1GKCsFBLOLBX+D+xhDa9oWOQV0IpvxwrSfKKssAqqroog== +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== dependencies: - "@types/markdown-it" "*" + "@types/node" "*" -"@types/markdown-it@*", "@types/markdown-it@^12.2.3": - version "12.2.3" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" - integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== +"@types/linkify-it@^5": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + +"@types/markdown-it-emoji@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/markdown-it-emoji/-/markdown-it-emoji-3.0.1.tgz#035d4d38110113ea0ce911f06bc2c2b03ca1ad42" + integrity sha512-cz1j8R35XivBqq9mwnsrP2fsz2yicLhB8+PDtuVkKOExwEdsVBNI+ROL3sbhtR5occRZ66vT0QnwFZCqdjf3pA== dependencies: - "@types/linkify-it" "*" - "@types/mdurl" "*" + "@types/markdown-it" "^14" -"@types/mdurl@*": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" - integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== +"@types/markdown-it@^14", "@types/markdown-it@^14.1.2": + version "14.1.2" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + dependencies: + "@types/linkify-it" "^5" + "@types/mdurl" "^2" + +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + +"@types/mdurl@^2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + "@types/ms@*": version "0.7.31" resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" @@ -331,10 +1612,17 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/node@^24.9.2": + version "24.12.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.0.tgz#6222e028210e5322e4f4f6767f8d88e5ea3b33d2" + integrity sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ== + dependencies: + undici-types "~7.16.0" + +"@types/picomatch@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-4.0.2.tgz#85a232bafed4121527cbf70c0ef461b46b2cc10b" + integrity sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA== "@types/qs@*": version "6.9.7" @@ -346,19 +1634,41 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== -"@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== +"@types/sax@^1.2.1": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.7.tgz#ba5fe7df9aa9c89b6dff7688a19023dd2963091d" + integrity sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A== + dependencies: + "@types/node" "*" + +"@types/send@*": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-1.2.1.tgz#6a784e45543c18c774c049bff6d3dbaf045c9c74" + integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== + dependencies: + "@types/node" "*" + +"@types/send@<1": + version "0.17.6" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.6.tgz#aeb5385be62ff58a52cd5459daa509ae91651d25" + integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.13.10": +"@types/serve-static@*": version "1.15.0" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== @@ -366,555 +1676,680 @@ "@types/mime" "*" "@types/node" "*" -"@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== +"@types/serve-static@^1", "@types/serve-static@^1.15.5": + version "1.15.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.10.tgz#768169145a778f8f5dfcb6360aead414a3994fee" + integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "<1" + +"@types/sockjs@^0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" -"@types/web-bluetooth@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8" - integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ== +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/webpack-env@^1.18.0": - version "1.18.0" - resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.0.tgz#ed6ecaa8e5ed5dfe8b2b3d00181702c9925f13fb" - integrity sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg== +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== -"@types/ws@^8.5.1": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/web-bluetooth@^0.0.21": + version "0.0.21" + resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63" + integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== + +"@types/webpack-env@^1.18.8": + version "1.18.8" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.8.tgz#71f083718c094204d7b64443701d32f1db3989e3" + integrity sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A== + +"@types/ws@^8.5.10": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" -"@vitejs/plugin-vue@^3.1.2": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz#a1484089dd85d6528f435743f84cdd0d215bbb54" - integrity sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw== +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@vue/compiler-core@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.41.tgz#fb5b25f23817400f44377d878a0cdead808453ef" - integrity sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw== +"@types/yargs@^17.0.33": + version "17.0.35" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" + integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== dependencies: - "@babel/parser" "^7.16.4" - "@vue/shared" "3.2.41" + "@types/yargs-parser" "*" + +"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@upsetjs/venn.js@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@upsetjs/venn.js/-/venn.js-2.0.0.tgz#3be192038cdda927aa4f8b22ab51af82abf47f34" + integrity sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw== + optionalDependencies: + d3-selection "^3.0.0" + d3-transition "^3.0.1" + +"@vitejs/plugin-vue@^6.0.1": + version "6.0.5" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz#20ebb46c4da069753d9cfb1309c4334213cc3f7b" + integrity sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg== + dependencies: + "@rolldown/pluginutils" "1.0.0-rc.2" + +"@vue/compiler-core@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.30.tgz#0f984da9207f24f9ddfb700052a43247a953fd9a" + integrity sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw== + dependencies: + "@babel/parser" "^7.29.0" + "@vue/shared" "3.5.30" + entities "^7.0.1" estree-walker "^2.0.2" - source-map "^0.6.1" + source-map-js "^1.2.1" -"@vue/compiler-dom@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz#dc63dcd3ce8ca8a8721f14009d498a7a54380299" - integrity sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw== +"@vue/compiler-dom@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz#a38dbdd520479244c8b673123b4bd06a82e733ee" + integrity sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g== dependencies: - "@vue/compiler-core" "3.2.41" - "@vue/shared" "3.2.41" + "@vue/compiler-core" "3.5.30" + "@vue/shared" "3.5.30" -"@vue/compiler-sfc@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz#238fb8c48318408c856748f4116aff8cc1dc2a73" - integrity sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w== +"@vue/compiler-sfc@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz#5c716d844f240154263e99b25fba6e1802c0c8c6" + integrity sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A== dependencies: - "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.41" - "@vue/compiler-dom" "3.2.41" - "@vue/compiler-ssr" "3.2.41" - "@vue/reactivity-transform" "3.2.41" - "@vue/shared" "3.2.41" + "@babel/parser" "^7.29.0" + "@vue/compiler-core" "3.5.30" + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" estree-walker "^2.0.2" - magic-string "^0.25.7" - postcss "^8.1.10" - source-map "^0.6.1" + magic-string "^0.30.21" + postcss "^8.5.8" + source-map-js "^1.2.1" -"@vue/compiler-ssr@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz#344f564d68584b33367731c04ffc949784611fcb" - integrity sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ== +"@vue/compiler-ssr@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz#e9b407d7e56be1e307a7621f2e8d2501267ff1d0" + integrity sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA== dependencies: - "@vue/compiler-dom" "3.2.41" - "@vue/shared" "3.2.41" + "@vue/compiler-dom" "3.5.30" + "@vue/shared" "3.5.30" -"@vue/devtools-api@^6.4.5": - version "6.4.5" - resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380" - integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ== +"@vue/devtools-api@^6.6.4": + version "6.6.4" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" + integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== -"@vue/reactivity-transform@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz#9ff938877600c97f646e09ac1959b5150fb11a0c" - integrity sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A== +"@vue/devtools-api@^8.0.2", "@vue/devtools-api@^8.0.7": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-8.1.0.tgz#a5b623e7a2f1c1339c560b4242a9308e29b1c3ea" + integrity sha512-O44X57jjkLKbLEc4OgL/6fEPOOanRJU8kYpCE8qfKlV96RQZcdzrcLI5mxMuVRUeXhHKIHGhCpHacyCk0HyO4w== dependencies: - "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.41" - "@vue/shared" "3.2.41" - estree-walker "^2.0.2" - magic-string "^0.25.7" + "@vue/devtools-kit" "^8.1.0" -"@vue/reactivity@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.41.tgz#0ad3bdf76d76822da1502dc9f394dafd02642963" - integrity sha512-9JvCnlj8uc5xRiQGZ28MKGjuCoPhhTwcoAdv3o31+cfGgonwdPNuvqAXLhlzu4zwqavFEG5tvaoINQEfxz+l6g== +"@vue/devtools-kit@^8.0.2", "@vue/devtools-kit@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-8.1.0.tgz#b29e9ddac45a222c2495e3fa36e110b6bd35b8a2" + integrity sha512-/NZlS4WtGIB54DA/z10gzk+n/V7zaqSzYZOVlg2CfdnpIKdB61bd7JDIMxf/zrtX41zod8E2/bbEBoW/d7x70Q== dependencies: - "@vue/shared" "3.2.41" + "@vue/devtools-shared" "^8.1.0" + birpc "^2.6.1" + hookable "^5.5.3" + perfect-debounce "^2.0.0" -"@vue/runtime-core@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.41.tgz#775bfc00b3fadbaddab77138f23322aee3517a76" - integrity sha512-0LBBRwqnI0p4FgIkO9q2aJBBTKDSjzhnxrxHYengkAF6dMOjeAIZFDADAlcf2h3GDALWnblbeprYYpItiulSVQ== +"@vue/devtools-shared@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-8.1.0.tgz#58bc97d235987b60ca81e6018718c46281163a0b" + integrity sha512-h8uCb4Qs8UT8VdTT5yjY6tOJ//qH7EpxToixR0xqejR55t5OdISIg7AJ7eBkhBs8iu1qG5gY3QQNN1DF1EelAA== + +"@vue/reactivity@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.30.tgz#1ff13f7d570b16b4f009f007772c7b71be1dd09d" + integrity sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q== dependencies: - "@vue/reactivity" "3.2.41" - "@vue/shared" "3.2.41" + "@vue/shared" "3.5.30" -"@vue/runtime-dom@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.41.tgz#cdf86be7410f7b15c29632a96ce879e5b4c9ab92" - integrity sha512-U7zYuR1NVIP8BL6jmOqmapRAHovEFp7CSw4pR2FacqewXNGqZaRfHoNLQsqQvVQ8yuZNZtxSZy0FFyC70YXPpA== +"@vue/runtime-core@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.30.tgz#abe448b25e88f583b1847323a2f19f5e4a21837d" + integrity sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg== dependencies: - "@vue/runtime-core" "3.2.41" - "@vue/shared" "3.2.41" - csstype "^2.6.8" + "@vue/reactivity" "3.5.30" + "@vue/shared" "3.5.30" -"@vue/server-renderer@3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.41.tgz#ca64552c05878f94e8d191ac439141c06c0fb2ad" - integrity sha512-7YHLkfJdTlsZTV0ae5sPwl9Gn/EGr2hrlbcS/8naXm2CDpnKUwC68i1wGlrYAfIgYWL7vUZwk2GkYLQH5CvFig== +"@vue/runtime-dom@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz#41d1b6424b754300f735c2ecb1a7457b4125dab3" + integrity sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw== dependencies: - "@vue/compiler-ssr" "3.2.41" - "@vue/shared" "3.2.41" + "@vue/reactivity" "3.5.30" + "@vue/runtime-core" "3.5.30" + "@vue/shared" "3.5.30" + csstype "^3.2.3" -"@vue/shared@3.2.41", "@vue/shared@^3.2.41": - version "3.2.41" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.41.tgz#fbc95422df654ea64e8428eced96ba6ad555d2bb" - integrity sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw== - -"@vuepress/bundler-vite@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/bundler-vite/-/bundler-vite-2.0.0-beta.53.tgz#6c425cccbe6f4d281a87dee320ded6f1e9eee329" - integrity sha512-zkqkV+EnoTi7cTRi6xjb0SRg0GzRYwceJu80/6q7Bd+h+VktqhapcHAZ8QaIsq8OxCXbg3sms/A9kg3UxBnRqg== +"@vue/server-renderer@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.30.tgz#116515063d609d3ceca1170f3b09122f24f187b5" + integrity sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ== dependencies: - "@vitejs/plugin-vue" "^3.1.2" - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - autoprefixer "^10.4.12" + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/shared@3.5.30", "@vue/shared@^3.5.29": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.30.tgz#5d7a0d3ca151647484303fd9f057e2e13ecb80ef" + integrity sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ== + +"@vuepress/bundler-vite@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/bundler-vite/-/bundler-vite-2.0.0-rc.26.tgz#99e0a3fe47dcc5d01036117307fd20d3c065187b" + integrity sha512-4+YfKs2iOxuVSMW+L2tFzu2+X2HiGAREpo1DbkkYVDa5GyyPR+YsSueXNZMroTdzWDk5kAUz2Z1Tz1lIu7TO2g== + dependencies: + "@vitejs/plugin-vue" "^6.0.1" + "@vuepress/bundlerutils" "2.0.0-rc.26" + "@vuepress/client" "2.0.0-rc.26" + "@vuepress/core" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + autoprefixer "^10.4.21" connect-history-api-fallback "^2.0.0" - postcss "^8.4.18" - postcss-load-config "^4.0.1" - rollup "^2.79.1" - vite "~3.1.8" - vue "^3.2.41" - vue-router "^4.1.6" + postcss "^8.5.6" + postcss-load-config "^6.0.1" + rollup "^4.52.4" + vite "~7.1.9" + vue "^3.5.22" + vue-router "^4.6.0" -"@vuepress/bundler-webpack@^2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/bundler-webpack/-/bundler-webpack-2.0.0-beta.53.tgz#9430f03aba4afb33ad296182073743ab8f0031c8" - integrity sha512-7J8GVabqiMMvRLMsWFlPf9LJ+xqvEUN8U7cnZ3Nm9Dbxd6hBk1kTE+tRa2JOEqJ6xjPHUxNLXTyP/IKgTAqQ7g== +"@vuepress/bundler-webpack@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/bundler-webpack/-/bundler-webpack-2.0.0-rc.26.tgz#66faf720a99e1c4297caadea785f869ed3ae8e03" + integrity sha512-6lkAnXB/ML7CIJHI8/9GDRHdu4p/Ap1eLRmj2+E4lHYHKpnwNzEDJoISaZWMwwNsr2satsb0iAUc/xvucUH5Kg== dependencies: - "@types/express" "^4.17.14" - "@types/webpack-env" "^1.18.0" - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - autoprefixer "^10.4.12" - chokidar "^3.5.3" - copy-webpack-plugin "^11.0.0" - css-loader "^6.7.1" - esbuild-loader "~2.20.0" - express "^4.18.2" - html-webpack-plugin "^5.5.0" - mini-css-extract-plugin "^2.6.1" - postcss "^8.4.18" - postcss-csso "^6.0.1" - postcss-loader "^7.0.1" - style-loader "^3.3.1" - vue "^3.2.41" - vue-loader "^17.0.0" - vue-router "^4.1.6" - webpack "^5.74.0" - webpack-chain "^6.5.1" - webpack-dev-server "^4.11.1" - webpack-merge "^5.8.0" + "@types/express" "^4.17.23" + "@types/webpack-env" "^1.18.8" + "@vuepress/bundlerutils" "2.0.0-rc.26" + "@vuepress/client" "2.0.0-rc.26" + "@vuepress/core" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + autoprefixer "^10.4.21" + copy-webpack-plugin "^13.0.1" + css-loader "^7.1.2" + css-minimizer-webpack-plugin "^7.0.2" + esbuild-loader "~4.4.0" + express "^4.21.2" + html-webpack-plugin "^5.6.4" + lightningcss "^1.30.2" + mini-css-extract-plugin "^2.9.4" + postcss "^8.5.6" + postcss-loader "^8.2.0" + style-loader "^4.0.0" + vue "^3.5.22" + vue-loader "^17.4.2" + vue-router "^4.6.0" + webpack "^5.102.1" + webpack-dev-server "^5.2.2" + webpack-merge "^6.0.1" + webpack-v5-chain "^1.0.0" -"@vuepress/cli@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/cli/-/cli-2.0.0-beta.53.tgz#5c8670abadb29797eb65071be93b0b6a76f444c0" - integrity sha512-MT2y6syOIP17hq/mWiZXTDEViDb3/k5eIVzlvpw4N8oiAr4hwwdCUzQ5vKVd7trn+83KvG5XYOLtjrj1hexlYg== +"@vuepress/bundlerutils@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/bundlerutils/-/bundlerutils-2.0.0-rc.26.tgz#abd85490414a6fb3001d66ee8878af1f9e4a1e56" + integrity sha512-OnhUvzuJFEzPBjivZX7j6EhPE6sAwAIfyi3pAFmOpQDHPP7/l0q2I4bNVVGK4t9EZDu4N7Dl40/oFHhIMy5New== dependencies: - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" + "@vuepress/client" "2.0.0-rc.26" + "@vuepress/core" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + vue "^3.5.22" + vue-router "^4.6.0" + +"@vuepress/cli@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/cli/-/cli-2.0.0-rc.26.tgz#aeffa6ddeda09d25351f690a0dfb6f99165cf9bd" + integrity sha512-63/4nIHrl9pbutUWs6SirWxmyykjvR9BWvu7bvczO1hAkWOyDQPcU18JXWy8q38CyMzPxCeedUfP3BQsZs3UgA== + dependencies: + "@vuepress/core" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" cac "^6.7.14" - chokidar "^3.5.3" - envinfo "^7.8.1" - esbuild "^0.15.12" + chokidar "^4.0.3" + envinfo "^7.18.0" + esbuild "^0.25.10" -"@vuepress/client@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-beta.53.tgz#c60fd217d01510ea62f57b8077940a51342f45f8" - integrity sha512-TDKxlrUvwfWu3QAY4SHeu9mVqBkEoKvuoy0WsKy7x9omEy8+HJG1O9y664bP9SotD52skcKL1iW38nQJR2+AkQ== +"@vuepress/client@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-rc.26.tgz#3fb1e38550b5deb2f39c05a996aa9e52ab77f397" + integrity sha512-+irF1HOTD6sAHdcTjp3yRcfuGlJYAW+YvDhq+7n3TPXeMH/wJbmGmAs2oRIDkx6Nlt3XkMMpFo7e9pOU22ut1w== dependencies: - "@vue/devtools-api" "^6.4.5" - "@vuepress/shared" "2.0.0-beta.53" - vue "^3.2.41" - vue-router "^4.1.6" + "@vue/devtools-api" "^8.0.2" + "@vue/devtools-kit" "^8.0.2" + "@vuepress/shared" "2.0.0-rc.26" + vue "^3.5.22" + vue-router "^4.6.0" -"@vuepress/core@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-beta.53.tgz#600da932f6ece8699580ecaf9937bc6bf6e7a71d" - integrity sha512-s642hM+PpiNphLm/KZvva45OYKX6hWRh2Y+C92TDGzCMxiONI8ZxGLqXRCw5bKw5NGh91s+P4sf3iaY+JxL1Ig== +"@vuepress/core@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-rc.26.tgz#3ca0d556fd4ea9571318a1786eed0068f96de192" + integrity sha512-Wyiv9oRvdT0lAPGU0Pj1HetjKicbX8/gqbBVYv2MmL7Y4a3r0tyQ92IdZ8LHiAgPvzctntQr/JXIELedvU1t/w== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/markdown" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - vue "^3.2.41" + "@vuepress/client" "2.0.0-rc.26" + "@vuepress/markdown" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + vue "^3.5.22" -"@vuepress/markdown@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-beta.53.tgz#8f9cc4a91e7bfb34d2606ffcde1d13526dc69308" - integrity sha512-ohIujGc0tVSsFTBD5kyB0asxLsDtctzrOOgHvaS2fDWqm0MQisjxnQKNFdbWk9bfddAyty0aKN+m/4l0f5lCDw== +"@vuepress/helper@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/helper/-/helper-2.0.0-rc.125.tgz#bca8d067fad528cb10c23b919d1a4e7eb24e2388" + integrity sha512-2NzP2HZCUYRfjcKI8c+Ml3hFdViBXZv88gaW1kNskuPM3P5/sSgjdM7997ZZPyuokANh8jwKwckA2PQ8UIRyiQ== dependencies: - "@mdit-vue/plugin-component" "^0.11.1" - "@mdit-vue/plugin-frontmatter" "^0.11.1" - "@mdit-vue/plugin-headers" "^0.11.1" - "@mdit-vue/plugin-sfc" "^0.11.1" - "@mdit-vue/plugin-title" "^0.11.1" - "@mdit-vue/plugin-toc" "^0.11.1" - "@mdit-vue/shared" "^0.11.0" - "@mdit-vue/types" "^0.11.0" - "@types/markdown-it" "^12.2.3" - "@types/markdown-it-emoji" "^2.0.2" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - markdown-it "^13.0.1" - markdown-it-anchor "^8.6.5" - markdown-it-emoji "^2.0.2" - mdurl "^1.0.1" + "@vue/shared" "^3.5.29" + "@vueuse/core" "^14.2.1" + cheerio "^1.2.0" + fflate "^0.8.2" + gray-matter "^4.0.3" + vue "^3.5.29" -"@vuepress/plugin-active-header-links@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-beta.53.tgz#08b4a196a659b06fe386d04e824ffaa31ddd0e58" - integrity sha512-rlDQ4CpF/awzHN6l6c5C4/bbiAZisZ2Z9cP2GJJBbxIb6QA6GOrIoHMt6L+9321Q+/jmntjoRJT4yHP/jg8OMA== - dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - ts-debounce "^4.0.0" - vue "^3.2.41" - vue-router "^4.1.6" +"@vuepress/highlighter-helper@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/highlighter-helper/-/highlighter-helper-2.0.0-rc.125.tgz#23224751933f7ebe3d4720f310954d8414ef7e74" + integrity sha512-v7dCssUGyaq1Ip8su0lWTb9QyXzhMQL6YjSds9BLqEpJIihmWrtZpAYDSvENineWGKzV+cr/2bPgHN5jBWaogw== -"@vuepress/plugin-back-to-top@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-beta.53.tgz#ef19c8a8b48643b9eaf9a0c3acffcb60958024a6" - integrity sha512-M7+WIA1e57yHbpUKksVDQdcHceslqeGn0/MldjmZHZ/xosxjM/ZIsw7AiSdmCcISEZBr60IXxDoLqJMNhMNQLQ== +"@vuepress/markdown@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-rc.26.tgz#1b191051763091d6bf6b1a8633ad0b5487b17694" + integrity sha512-ZAXkRxqPDjxqcG4j4vN2ZL5gmuRmgGH7n0s/7pcWIGFH3BJodp/PXMYCklnne1VwARIim9rqE3FKPB/ifJX0yA== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - ts-debounce "^4.0.0" - vue "^3.2.41" + "@mdit-vue/plugin-component" "^3.0.2" + "@mdit-vue/plugin-frontmatter" "^3.0.2" + "@mdit-vue/plugin-headers" "^3.0.2" + "@mdit-vue/plugin-sfc" "^3.0.2" + "@mdit-vue/plugin-title" "^3.0.2" + "@mdit-vue/plugin-toc" "^3.0.2" + "@mdit-vue/shared" "^3.0.2" + "@mdit-vue/types" "^3.0.2" + "@types/markdown-it" "^14.1.2" + "@types/markdown-it-emoji" "^3.0.1" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + markdown-it "^14.1.0" + markdown-it-anchor "^9.2.0" + markdown-it-emoji "^3.0.0" + mdurl "^2.0.0" -"@vuepress/plugin-container@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-container/-/plugin-container-2.0.0-beta.53.tgz#b112de6559af7fb82c42327bbe2be6969d810d70" - integrity sha512-kkEee5iGRHfGVFNBsF2b5vCfjC7dcmU2zqICJq8/UZbhWuyAavpmDovQYLCVh/yTfNE1FlRUOAFFI+jf3bvF9g== +"@vuepress/plugin-active-header-links@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-rc.125.tgz#fd5e3b761c857ffb73458d3254aad5afdd37eaba" + integrity sha512-sUuwJUi0pQxdQ1S63Srk2gP0pzN/rv4QAYOiz/mMmZW/iGoe6CY6RBvwLOQ0CNNUjJ5vGbgJWvZfZ8Fy7IjENA== dependencies: - "@types/markdown-it" "^12.2.3" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/markdown" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - markdown-it "^13.0.1" - markdown-it-container "^3.0.0" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" -"@vuepress/plugin-external-link-icon@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-external-link-icon/-/plugin-external-link-icon-2.0.0-beta.53.tgz#8ad4fe660192bc991ccf7051dd5fdc9476e6a0f9" - integrity sha512-S+IY1PK96Vbuf90IdZBe36kRpMCXrGr9TPaPm1aAQ9GA0Y5QQkTV876SXsb0n1B6Ae2AsSieulB2o4lyoL1LBQ== +"@vuepress/plugin-back-to-top@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.125.tgz#20d8b214ef8aaf988d19d41d3030afefc207bba5" + integrity sha512-tFXN7BtHr+jMVyJl6O6trpw2gFdE04sODDf/I1QMquOXl/Wezr4gdtl+OeBcBL/9zuduNYAb03hoAmWAQRtgLQ== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/markdown" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - vue "^3.2.41" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" -"@vuepress/plugin-git@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-git/-/plugin-git-2.0.0-beta.53.tgz#6fffbf178ec4ee41e0134198474b96af6d31d3bc" - integrity sha512-hefVEUhxTgvDcrsIutVBBfJvixR/L6iTQZ9eDAj2z71fOgnVNdN8PNZ9XRDm3nhZrye9X44AmJI82Wk9SlwgVw== +"@vuepress/plugin-copy-code@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.125.tgz#5539bc1e25af881eb11f700c6d08f265d742daa0" + integrity sha512-wm2EVnUmwEcu8boAbjYG+xdymr02kORdV18DsXwd/NpwlmbzcXUe8Qw/48qZBsa2bhxLTlnu3qjcARn1oFRQyQ== dependencies: - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - execa "^6.1.0" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" -"@vuepress/plugin-medium-zoom@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-beta.53.tgz#03a7b49bdcac4bdc8e813019f74d849e348d3540" - integrity sha512-hvmO40is/JrHDcSFp73qwX90nXUAaBBZHokZ0I3D61u6acFtI4HU/vpJpu+3oiqjXHQaUNqZO5eDr4EpypGjUg== +"@vuepress/plugin-git@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-git/-/plugin-git-2.0.0-rc.125.tgz#fd01d931ad174a2bcdc893294ed7903509554808" + integrity sha512-iki07M125tSSFpdADMfY0pAd+LtimuETqEv8OuHut4o1ZeY+TleyBVpsprgAu4UfpkisKQuM8pYjGagLXsj3rQ== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - medium-zoom "^1.0.6" - vue "^3.2.41" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + rehype-parse "^9.0.1" + rehype-sanitize "^6.0.0" + rehype-stringify "^10.0.1" + unified "^11.0.5" + vue "^3.5.29" -"@vuepress/plugin-nprogress@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-beta.53.tgz#7e83e959180b74e6026f3c15e4e92479ba1f72c3" - integrity sha512-xO8Dqw1yCttY6N+jDpuwE3RG+jQVPE0EieRafTWRO+fGCFobGa/6Zldc4x3+alB2xyXwFAy2495NYgPudNIWeQ== +"@vuepress/plugin-links-check@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.125.tgz#c11ea03dc32359a43007a015df3f1840c1ba2384" + integrity sha512-z44Ut/uDZMwexmyh/rpsqQg+AvqffvT6JPpVQs6gkj0jBTEvmGAMOdrImBGH7xLrtFz/gZ4eeZcIC3aCdCSOtw== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - vue "^3.2.41" - vue-router "^4.1.6" + "@vuepress/helper" "2.0.0-rc.125" -"@vuepress/plugin-palette@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-palette/-/plugin-palette-2.0.0-beta.53.tgz#ae9d40ce7e6f24a41d9758de277076cbcd376473" - integrity sha512-iYCb397nu/WacvXEaTmeex7lxkjHqRPXLAqBccrD4JWPshP2iu1ajM316jI8sUXSPTZZl4GOQ7+fqbr+UGHdEg== +"@vuepress/plugin-markdown-chart@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-markdown-chart/-/plugin-markdown-chart-2.0.0-rc.125.tgz#e0b4e5235c550387540c9eb6748bc4eb04f759f8" + integrity sha512-WG9PmFs7QO2ivbEeDKdPFL7Kap5zJC0PaWhf4wjLyOh2KaGqlz/YJufqdcQDVOLnvmszRk0t2WX7kgROaGv53A== dependencies: - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - chokidar "^3.5.3" + "@mdit/plugin-container" "^0.23.1" + "@mdit/plugin-plantuml" "^0.24.1" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" -"@vuepress/plugin-prismjs@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-prismjs/-/plugin-prismjs-2.0.0-beta.53.tgz#b6a0cec28306c6fa049ddc2624f606b27b49f493" - integrity sha512-8zAMHqSPJK8Nw9hP2V12BrAfT88Mmw37Lhi6cbc0S9Ub+wapzZkD9I1SuR1OEssqqMrHL2h1dWx25RqYstn7eA== +"@vuepress/plugin-markdown-hint@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-markdown-hint/-/plugin-markdown-hint-2.0.0-rc.125.tgz#b2f493f05197370391e66729a1a35e15c43e3079" + integrity sha512-0uZTI4GucVjoUUCUbV1jU6HaQfQCL41Zvm6UO2yhBqmlIVBxv+PGhr3p1U33165LN2bJve6Zj1JFiCsy6pjsyw== dependencies: - "@vuepress/core" "2.0.0-beta.53" - prismjs "^1.29.0" + "@mdit/plugin-alert" "^0.23.1" + "@mdit/plugin-container" "^0.23.1" + "@types/markdown-it" "^14.1.2" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" -"@vuepress/plugin-search@^2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-2.0.0-beta.53.tgz#6904650490dc4d1d5385cf288baf896f9fa40e73" - integrity sha512-x9FScY9aLTzlp6D5wO1d0kDkAO9TkzLwGueNx5F13Nkq589weq8uTTiNRA2oDM0l+H9BF6vDJ+XJlzE5W3u9gQ== +"@vuepress/plugin-markdown-tab@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-markdown-tab/-/plugin-markdown-tab-2.0.0-rc.125.tgz#aa294657d10fac4d19c9e14a3365e20e0220a925" + integrity sha512-GSEj7OKsry8dmG608XRYBo9NeDYmE5Z1b44e9/xxC1VQlgV+Egf7yzakb+YHIYAiLlzCbQrhm7XLTQnAu8EbKg== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - chokidar "^3.5.3" - vue "^3.2.41" - vue-router "^4.1.6" + "@mdit/plugin-tab" "^0.24.1" + "@types/markdown-it" "^14.1.2" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" -"@vuepress/plugin-shiki@^2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-shiki/-/plugin-shiki-2.0.0-beta.53.tgz#38f2ce16fd5af61bc41bc0ea2611e94f68cb2405" - integrity sha512-Bpcv7GZyvj1mk1PoYVJAB42B+4JuKZBho4iqfHGtPhqLg5jcVLgd/p4OscC7fTL2S94ubES4q8G1WXu8JGtJuQ== +"@vuepress/plugin-medium-zoom@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-rc.125.tgz#eaccfc050a1ce5b4b4942b00da0f6612c054fb67" + integrity sha512-lyiMEFvGGG88866EC2nOf8nJ9eQxVSstf/vGAqma13GrhTWLCQHdugdQDC6AmooWPranKd2X3O1LSL9Yd2BbOQ== dependencies: - "@vuepress/core" "2.0.0-beta.53" - shiki "^0.11.1" + "@vuepress/helper" "2.0.0-rc.125" + medium-zoom "^1.1.0" + vue "^3.5.29" -"@vuepress/plugin-theme-data@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-beta.53.tgz#b838a2afae815301c8b9d1ec3cfe865a72d4f302" - integrity sha512-fTOWrsO+ql2ZcN1UtF7Xc6+J/XfOAL+4+0Tq6fSky4Gv1HdC2Euey+r+RYgYkTdogdbL2VaUp3s+jhcow5WWAg== +"@vuepress/plugin-nprogress@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-rc.125.tgz#c81fc6456d68c3d127c5900b3b025228d73bd55e" + integrity sha512-RfD/MOYCeYYOZEC+rG+sHZjaw+OGt8dAwKOeviWcJiEONxdiD8uMPmAqdtek3q37zNrKb5yVfIDIIS3/qjsQwA== dependencies: - "@vue/devtools-api" "^6.4.5" - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - vue "^3.2.41" + "@vuepress/helper" "2.0.0-rc.125" + vue "^3.5.29" -"@vuepress/shared@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-beta.53.tgz#acf19da2dd23c09afd29cffb993644e29b91d229" - integrity sha512-B0qWorGxC3ruSHdZcJW24XtEDEU3L3uPr0xzTeKVfHjOM4b9hN83YzBtW4n/WPnmk1RXVE9266Ulh9ZL5okGOw== +"@vuepress/plugin-palette@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-palette/-/plugin-palette-2.0.0-rc.125.tgz#327491d14f10f9fb1051beb4fa330cb03ceb639f" + integrity sha512-prcq3bLD+pjtg9iXRl6nJov/k0cqAs83JlHvJEgo0anLr0a8zJyhxMtgUQFRxY2t01DL6fNYoVeMHQIGhyWceQ== dependencies: - "@mdit-vue/types" "^0.11.0" - "@vue/shared" "^3.2.41" + "@vuepress/helper" "2.0.0-rc.125" + chokidar "^5.0.0" -"@vuepress/theme-default@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-2.0.0-beta.53.tgz#0891d380360a4f4cd07b54953582cafb4ad174d0" - integrity sha512-FNzEgD2D+ZZRpgF4PfUMCVfKkpzHjmapMlho6Q74d1iqf5cbDeiTyUSWXM2SWHwyZDbdbemjcnfiztb1c215ow== +"@vuepress/plugin-prismjs@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-prismjs/-/plugin-prismjs-2.0.0-rc.125.tgz#5dd03dbe040175e449cdb1c61b99cdbedc5ff379" + integrity sha512-z5AvS88NIxChFELUftN5rdL2jF4zI1h1QweV60ou4l1eP3reru7hx3etNH+lqG4Yll31KYzFFjA6EOGgn7pN/g== dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/plugin-active-header-links" "2.0.0-beta.53" - "@vuepress/plugin-back-to-top" "2.0.0-beta.53" - "@vuepress/plugin-container" "2.0.0-beta.53" - "@vuepress/plugin-external-link-icon" "2.0.0-beta.53" - "@vuepress/plugin-git" "2.0.0-beta.53" - "@vuepress/plugin-medium-zoom" "2.0.0-beta.53" - "@vuepress/plugin-nprogress" "2.0.0-beta.53" - "@vuepress/plugin-palette" "2.0.0-beta.53" - "@vuepress/plugin-prismjs" "2.0.0-beta.53" - "@vuepress/plugin-theme-data" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - "@vueuse/core" "^9.3.1" - sass "^1.55.0" - vue "^3.2.41" - vue-router "^4.1.6" + "@vuepress/helper" "2.0.0-rc.125" + "@vuepress/highlighter-helper" "2.0.0-rc.125" + prismjs "^1.30.0" -"@vuepress/utils@2.0.0-beta.53": - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-beta.53.tgz#ac61235436a4c45e03e7e856ea59a55de0f890cc" - integrity sha512-cYqAspUJoY1J84kbDbPbrIcfaoID5Wb+BUrcWV7x8EFPXTn/KBLgc4/KBxWkdxk8O9V96/bXBDSLlalqLJCmJw== +"@vuepress/plugin-redirect@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-redirect/-/plugin-redirect-2.0.0-rc.125.tgz#30e05e3f973f835af5482baa506b2847919ca0c9" + integrity sha512-DYIr4Ay1PpbvIYNQFuhXtrCrjqtGfsMSxFThfyNeZ12Xw8VntowE0CSiONL7XaqCJ2RzsqH/MfYlk6rUJY6cjg== dependencies: - "@types/debug" "^4.1.7" - "@types/fs-extra" "^9.0.13" - "@types/hash-sum" "^1.0.0" - "@vuepress/shared" "2.0.0-beta.53" - chalk "^5.1.2" - debug "^4.3.4" - fs-extra "^10.1.0" - globby "^13.1.2" + "@vuepress/helper" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + commander "^14.0.3" + vue "^3.5.29" + +"@vuepress/plugin-search@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-2.0.0-rc.125.tgz#ad2fb6db5418b626987b229ffaa039fbda7755ff" + integrity sha512-yORJ3GoRVURw+E0+F84IIS2Z2oqZVVPS5sd1ldq61Sy52cdkCws5wGKa7ossVn6nBQpaEV4bLDccPccL5XciNQ== + dependencies: + "@vuepress/helper" "2.0.0-rc.125" + chokidar "^5.0.0" + vue "^3.5.29" + +"@vuepress/plugin-seo@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.125.tgz#f573cd08bfb089adf71ae5dc42d7f440356ceddc" + integrity sha512-m8NPIMCIi84DVg5h99PvmAy6raxBVbV8Ne4GPCIjhpU2gGG4IHyuAk3NBKWig2n6daGPph1uFZYx8FOeqyJObQ== + dependencies: + "@vuepress/helper" "2.0.0-rc.125" + +"@vuepress/plugin-shiki@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-shiki/-/plugin-shiki-2.0.0-rc.125.tgz#63c52e3418d8cabf2be619b887f03219d09b72d8" + integrity sha512-VaSfhMkJAs9i7qFgCay42CkS9eQjYHpu5bzAa4Ioxdt/WWX2tonc98ydUpVbpmFl177ZRdP2IRCYf8TulJ9xqA== + dependencies: + "@shikijs/transformers" "^4.0.1" + "@vuepress/helper" "2.0.0-rc.125" + "@vuepress/highlighter-helper" "2.0.0-rc.125" + nanoid "^5.1.6" + shiki "^4.0.1" + synckit "^0.11.12" + +"@vuepress/plugin-sitemap@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.125.tgz#eab6496dd50f8fd809e8083c3b52be0a44d57c92" + integrity sha512-Q1mJbDGVBZ560wsIEqVYQciHwZtNufTCQPejiF6+WfMfqJMpiFZJkF2dsGBmR7w586/vYfkHwEeRqwvJPoYxdg== + dependencies: + "@vuepress/helper" "2.0.0-rc.125" + sitemap "^9.0.1" + +"@vuepress/plugin-theme-data@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-rc.125.tgz#ad3807db957da35fa3bf071dec4c5656e7e0d29c" + integrity sha512-f+QX2MBDmrPWA66fPIbXb/mPKpBqmpsF9Z6VNiigreZy3DWfQImw3blOTl4e8fbA61u8O1KTI78UenMxdxAu7A== + dependencies: + "@vue/devtools-api" "^8.0.7" + vue "^3.5.29" + +"@vuepress/shared@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-rc.26.tgz#557ad6c7177529ae99a4c7e618de7b460f1607bd" + integrity sha512-Zl9XNG/fYenZqzuYYGOfHzjmp1HCOj68gcJnJABOX1db0H35dkPSPsxuMjbTljClUqMlfj70CLeip/h04upGVw== + dependencies: + "@mdit-vue/types" "^3.0.2" + +"@vuepress/theme-default@2.0.0-rc.125": + version "2.0.0-rc.125" + resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-2.0.0-rc.125.tgz#5d219bd12c0a9b113e61fbd222a1d5138c105ba5" + integrity sha512-sYUtniwfjU6Jwfq7GxQXLHDviah1rYUjtbWYiir1SIuz8m56SzPJxWza27ef/DL5OnrlLmG4Z4bgXUmgkZZocA== + dependencies: + "@vuepress/helper" "2.0.0-rc.125" + "@vuepress/plugin-active-header-links" "2.0.0-rc.125" + "@vuepress/plugin-back-to-top" "2.0.0-rc.125" + "@vuepress/plugin-copy-code" "2.0.0-rc.125" + "@vuepress/plugin-git" "2.0.0-rc.125" + "@vuepress/plugin-links-check" "2.0.0-rc.125" + "@vuepress/plugin-markdown-hint" "2.0.0-rc.125" + "@vuepress/plugin-markdown-tab" "2.0.0-rc.125" + "@vuepress/plugin-medium-zoom" "2.0.0-rc.125" + "@vuepress/plugin-nprogress" "2.0.0-rc.125" + "@vuepress/plugin-palette" "2.0.0-rc.125" + "@vuepress/plugin-prismjs" "2.0.0-rc.125" + "@vuepress/plugin-seo" "2.0.0-rc.125" + "@vuepress/plugin-sitemap" "2.0.0-rc.125" + "@vuepress/plugin-theme-data" "2.0.0-rc.125" + "@vueuse/core" "^14.2.1" + vue "^3.5.29" + +"@vuepress/utils@2.0.0-rc.26": + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-rc.26.tgz#e708f6d006929f36fbb95313ca4e6b1d5f91002a" + integrity sha512-RWzZrGQ0WLSWdELuxg7c6q1D9I22T5PfK/qNFkOsv9eD3gpUsU4jq4zAoumS8o+NRIWHovCJ9WnAhHD0Ns5zAw== + dependencies: + "@types/debug" "^4.1.12" + "@types/fs-extra" "^11.0.4" + "@types/hash-sum" "^1.0.2" + "@types/picomatch" "^4.0.2" + "@vuepress/shared" "2.0.0-rc.26" + debug "^4.4.3" + fs-extra "^11.3.2" hash-sum "^2.0.0" - ora "^6.1.2" + ora "^9.0.0" + picocolors "^1.1.1" + picomatch "^4.0.3" + tinyglobby "^0.2.15" upath "^2.0.1" -"@vueuse/core@^9.3.1": - version "9.4.0" - resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.4.0.tgz#afb30f9494b0954e51a489526566b14f1e2c5fb3" - integrity sha512-JzgenGj1ZF2BHOen5rsFiAyyI9sXAv7aKhNLlm9b7SwYQeKTcxTWdhudonURCSP3Egl9NQaRBzes2lv/1JUt/Q== +"@vueuse/core@^14.2.1": + version "14.2.1" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-14.2.1.tgz#b5cf36a07b4ea973381e18523ad0ed6ddc98a5be" + integrity sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ== dependencies: - "@types/web-bluetooth" "^0.0.16" - "@vueuse/metadata" "9.4.0" - "@vueuse/shared" "9.4.0" - vue-demi "*" + "@types/web-bluetooth" "^0.0.21" + "@vueuse/metadata" "14.2.1" + "@vueuse/shared" "14.2.1" -"@vueuse/metadata@9.4.0": - version "9.4.0" - resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.4.0.tgz#5c8eb105a8ad9eb7b47f78a226ff993560d0bd7f" - integrity sha512-7GKMdGAsJyQJl35MYOz/RDpP0FxuiZBRDSN79QIPbdqYx4Sd0sVTnIC68KJ6Oln0t0SouvSUMvRHuno216Ud2Q== +"@vueuse/metadata@14.2.1": + version "14.2.1" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-14.2.1.tgz#bd3338a565c2f651b9d18ac0f8825aa6077ee461" + integrity sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw== -"@vueuse/shared@9.4.0": - version "9.4.0" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.4.0.tgz#634022fe42b3d5ece1d81d749724966f5071c8c3" - integrity sha512-fTuem51KwMCnqUKkI8B57qAIMcFovtGgsCtAeqxIzH3i6nE9VYge+gVfneNHAAy7lj8twbkNfqQSygOPJTm4tQ== +"@vueuse/shared@14.2.1": + version "14.2.1" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-14.2.1.tgz#829a271147937f6b105bb1422d3171e6142f47ba" + integrity sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw== + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: - vue-demi "*" + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== - -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== - -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== - -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: - "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -927,7 +2362,7 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -935,12 +2370,22 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +acorn-import-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== -acorn@^8.5.0, acorn@^8.7.1: +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +acorn@^8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +acorn@^8.5.0: version "8.8.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== @@ -952,28 +2397,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv-keywords@^5.0.0: +ajv-keywords@^5.0.0, ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ajv@^8.0.0, ajv@^8.8.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" @@ -984,6 +2414,16 @@ ajv@^8.0.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -994,10 +2434,10 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.1: version "3.2.1" @@ -1034,6 +2474,11 @@ anywhere@^1.6.0: serve-index "^1.9.1" serve-static "^1.13.2" +arg@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1066,16 +2511,20 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== +asn1js@^3.0.6: + version "3.0.7" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.7.tgz#15f1f2f59e60f80d5b43ef14047a294a969f824f" + integrity sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ== + dependencies: + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -1086,27 +2535,21 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^10.4.12: - version "10.4.13" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" - integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== +autoprefixer@^10.4.21: + version "10.4.27" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.27.tgz#51ea301a5c3c5f8642f8e564759c4f573be486f2" + integrity sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA== dependencies: - browserslist "^4.21.4" - caniuse-lite "^1.0.30001426" - fraction.js "^4.2.0" - normalize-range "^0.1.2" - picocolors "^1.0.0" + browserslist "^4.28.1" + caniuse-lite "^1.0.30001774" + fraction.js "^5.3.4" + picocolors "^1.1.1" postcss-value-parser "^4.2.0" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== base@^0.11.1: version "0.11.2" @@ -1121,6 +2564,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +baseline-browser-mapping@^2.9.0: + version "2.9.19" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" + integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -1136,40 +2584,34 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" - integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== - dependencies: - buffer "^6.0.3" - inherits "^2.0.4" - readable-stream "^3.4.0" +birpc@^2.6.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/birpc/-/birpc-2.9.0.tgz#b59550897e4cd96a223e2a6c1475b572236ed145" + integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@~1.20.3: + version "1.20.4" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== dependencies: - bytes "3.1.2" + bytes "~3.1.2" content-type "~1.0.5" debug "2.6.9" depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.2" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.14.0" + raw-body "~2.5.3" type-is "~1.6.18" - unpipe "1.0.0" + unpipe "~1.0.0" -bonjour-service@^1.0.11: - version "1.0.14" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7" - integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ== +bonjour-service@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== dependencies: - array-flatten "^2.1.2" - dns-equal "^1.0.0" fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" @@ -1178,14 +2620,6 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -1209,39 +2643,39 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.14.5, browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserslist@^4.0.0, browserslist@^4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" + run-applescript "^7.0.0" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: +bytes@3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +bytestreamjs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bytestreamjs/-/bytestreamjs-2.0.1.tgz#a32947c7ce389a6fa11a09a9a563d0a45889535e" + integrity sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ== + cac@^6.7.14: version "6.7.14" resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" @@ -1262,13 +2696,21 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -call-bind@^1.0.0: +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" callsites@^3.0.0: version "3.1.0" @@ -1283,10 +2725,30 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: - version "1.0.30001429" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001429.tgz#70cdae959096756a85713b36dd9cb82e62325639" - integrity sha512-511ThLu1hF+5RRRt0zYCf2U2yRr9GPF6m5y90SBCWsvSoYoW7yAGlv/elyPaNfvGCkp6kj/KFZWU0BMA69Prsg== +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001774: + version "1.0.30001780" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz#0e413de292808868a62ed9118822683fa120a110" + integrity sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ== + +caniuse-lite@^1.0.30001759: + version "1.0.30001769" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz#1ad91594fad7dc233777c2781879ab5409f7d9c2" + integrity sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== chalk@^2.0.0: version "2.4.2" @@ -1297,7 +2759,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.0: +chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1305,15 +2767,54 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.0.0, chalk@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45" - integrity sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ== +chalk@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.2.0.tgz#f23b777c49021ead7475dcf3390d3535a7f896d6" + integrity sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.2.2" + encoding-sniffer "^0.2.1" + htmlparser2 "^10.1.0" + parse5 "^7.3.0" + parse5-htmlparser2-tree-adapter "^7.1.0" + parse5-parser-stream "^7.1.2" + undici "^7.19.0" + whatwg-mimetype "^4.0.0" + +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -1325,11 +2826,30 @@ chalk@^5.0.0, chalk@^5.1.2: optionalDependencies: fsevents "~2.3.2" +chokidar@^4.0.0, chokidar@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chokidar@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5" + integrity sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw== + dependencies: + readdirp "^5.0.0" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +ci-info@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" + integrity sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -1347,17 +2867,17 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" -cli-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" - integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: - restore-cursor "^4.0.0" + restore-cursor "^5.0.0" -cli-spinners@^2.6.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== +cli-spinners@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-3.4.0.tgz#1f11f6d48c4e5bc6849fcb4efa0dc98f9e7299ea" + integrity sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw== clone-deep@^4.0.1: version "4.0.1" @@ -1368,11 +2888,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1405,21 +2920,46 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + colorette@^2.0.10: version "2.0.19" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -commander@2, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +colorjs.io@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.5.2.tgz#63b20139b007591ebc3359932bef84628eb3fcef" + integrity sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw== + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== commander@7: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + +commander@^14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.3.tgz#425d79b48f9af82fcd9e4fc1ea8af6c5ec07bbc2" + integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -1430,30 +2970,30 @@ component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -compressible@~2.0.16: +compressible@~2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== +compression@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" + integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" + bytes "3.1.2" + compressible "~2.0.18" debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" + negotiator "~0.6.4" + on-headers "~1.1.0" + safe-buffer "5.2.1" vary "~1.1.2" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== connect-history-api-fallback@^1.2.0: version "1.6.0" @@ -1475,7 +3015,7 @@ connect@^3.6.6: parseurl "~1.3.3" utils-merge "1.0.1" -content-disposition@0.5.4: +content-disposition@~0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -1492,71 +3032,91 @@ content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie-signature@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" + integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== -cookie@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@~0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -copy-webpack-plugin@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" - integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== +copy-webpack-plugin@^13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz#fba18c22bcab3633524e1b652580ff4489eddc0d" + integrity sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw== dependencies: - fast-glob "^3.2.11" glob-parent "^6.0.1" - globby "^13.1.1" normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" + tinyglobby "^0.2.12" core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" + layout-base "^1.0.0" -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cose-base@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-2.2.0.tgz#1c395c35b6e10bb83f9769ca8b817d614add5c01" + integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" + layout-base "^2.0.0" -css-loader@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e" - integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw== +cosmiconfig@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.1.tgz#df110631a8547b5d1a98915271986f06e3011379" + integrity sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + +css-declaration-sorter@^7.2.0: + version "7.3.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz#acd204976d7ca5240b5579bfe6e73d4d088fd568" + integrity sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA== + +css-loader@^7.1.2: + version "7.1.4" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.4.tgz#8f6bf9f8fc8cbef7d2ef6e80acc6545eaefa90b1" + integrity sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw== dependencies: icss-utils "^5.1.0" - postcss "^8.4.7" - postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.0" - postcss-modules-scope "^3.0.0" + postcss "^8.4.40" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.5" + semver "^7.6.3" + +css-minimizer-webpack-plugin@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.4.tgz#92d2643e3658e3f484a70382a5dba18e51997f2e" + integrity sha512-2iACis+P8qdLj1tHcShtztkGhCNIRUajJj7iX0IM9a5FA0wXGwjV8Nf6+HsBjBfb4LO8TTAVoetBbM54V6f3+Q== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + cssnano "^7.0.4" + jest-worker "^30.0.5" + postcss "^8.4.40" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" css-select@^4.1.3: version "4.3.0" @@ -1569,6 +3129,25 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.2.2.tgz#01b6e8d163637bb2dd6c982ca4ed65863682786e" + integrity sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.2.1.tgz#86cac7011561272b30e6b1e042ba6ce047aa7518" + integrity sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA== + dependencies: + mdn-data "2.27.1" + source-map-js "^1.2.1" + css-tree@~2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" @@ -1582,11 +3161,65 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css-what@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea" + integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssnano-preset-default@^7.0.11: + version "7.0.11" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-7.0.11.tgz#ea81661d0e8fe59b752560cca4a9f2fac763e92c" + integrity sha512-waWlAMuCakP7//UCY+JPrQS1z0OSLeOXk2sKWJximKWGupVxre50bzPlvpbUwZIDylhf/ptf0Pk+Yf7C+hoa3g== + dependencies: + browserslist "^4.28.1" + css-declaration-sorter "^7.2.0" + cssnano-utils "^5.0.1" + postcss-calc "^10.1.1" + postcss-colormin "^7.0.6" + postcss-convert-values "^7.0.9" + postcss-discard-comments "^7.0.6" + postcss-discard-duplicates "^7.0.2" + postcss-discard-empty "^7.0.1" + postcss-discard-overridden "^7.0.1" + postcss-merge-longhand "^7.0.5" + postcss-merge-rules "^7.0.8" + postcss-minify-font-values "^7.0.1" + postcss-minify-gradients "^7.0.1" + postcss-minify-params "^7.0.6" + postcss-minify-selectors "^7.0.6" + postcss-normalize-charset "^7.0.1" + postcss-normalize-display-values "^7.0.1" + postcss-normalize-positions "^7.0.1" + postcss-normalize-repeat-style "^7.0.1" + postcss-normalize-string "^7.0.1" + postcss-normalize-timing-functions "^7.0.1" + postcss-normalize-unicode "^7.0.6" + postcss-normalize-url "^7.0.1" + postcss-normalize-whitespace "^7.0.1" + postcss-ordered-values "^7.0.2" + postcss-reduce-initial "^7.0.6" + postcss-reduce-transforms "^7.0.1" + postcss-svgo "^7.1.1" + postcss-unique-selectors "^7.0.5" + +cssnano-utils@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-5.0.1.tgz#f529e9aa0d7930512ca45b9e2ddb8d6b9092eb30" + integrity sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg== + +cssnano@^7.0.4: + version "7.1.3" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-7.1.3.tgz#2a542bb8d62b6bee9e23e455ba2e507fd102f611" + integrity sha512-mLFHQAzyapMVFLiJIn7Ef4C2UCEvtlTlbyILR6B5ZsUAV3D/Pa761R5uC1YPhyBkRd3eqaDm2ncaNrD7R4mTRg== + dependencies: + cssnano-preset-default "^7.0.11" + lilconfig "^3.1.3" + csso@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" @@ -1594,15 +3227,36 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -csstype@^2.6.8: - version "2.6.21" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" - integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== +csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== -d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" - integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape-fcose@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz#e4d6f6490df4fab58ae9cea9e5c3ab8d7472f471" + integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== + dependencies: + cose-base "^2.2.0" + +cytoscape@^3.33.1: + version "3.33.1" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" + integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== + +"d3-array@1 - 2": + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: version "3.2.4" @@ -1611,27 +3265,11 @@ d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: dependencies: internmap "1 - 2" -d3-axis@1: - version "1.0.12" - resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9" - integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ== - d3-axis@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== -d3-brush@1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b" - integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA== - dependencies: - d3-dispatch "1" - d3-drag "1" - d3-interpolate "1" - d3-selection "1" - d3-transition "1" - d3-brush@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" @@ -1643,14 +3281,6 @@ d3-brush@3: d3-selection "3" d3-transition "3" -d3-chord@1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f" - integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA== - dependencies: - d3-array "1" - d3-path "1" - d3-chord@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" @@ -1658,28 +3288,11 @@ d3-chord@3: dependencies: d3-path "1 - 3" -d3-collection@1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" - integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== - -d3-color@1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" - integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== - "d3-color@1 - 3", d3-color@3: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== -d3-contour@1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3" - integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg== - dependencies: - d3-array "^1.1.1" - d3-contour@4: version "4.0.2" resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc" @@ -1694,24 +3307,11 @@ d3-delaunay@6: dependencies: delaunator "5" -d3-dispatch@1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" - integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== - "d3-dispatch@1 - 3", d3-dispatch@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== -d3-drag@1: - version "1.2.5" - resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70" - integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w== - dependencies: - d3-dispatch "1" - d3-selection "1" - "d3-drag@2 - 3", d3-drag@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" @@ -1720,15 +3320,6 @@ d3-drag@1: d3-dispatch "1 - 3" d3-selection "3" -d3-dsv@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c" - integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g== - dependencies: - commander "2" - iconv-lite "0.4" - rw "1" - "d3-dsv@1 - 3", d3-dsv@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" @@ -1738,23 +3329,11 @@ d3-dsv@1: iconv-lite "0.6" rw "1" -d3-ease@1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2" - integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ== - "d3-ease@1 - 3", d3-ease@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== -d3-fetch@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7" - integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA== - dependencies: - d3-dsv "1" - d3-fetch@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" @@ -1762,16 +3341,6 @@ d3-fetch@3: dependencies: d3-dsv "1 - 3" -d3-force@1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b" - integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg== - dependencies: - d3-collection "1" - d3-dispatch "1" - d3-quadtree "1" - d3-timer "1" - d3-force@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" @@ -1781,23 +3350,11 @@ d3-force@3: d3-quadtree "1 - 3" d3-timer "1 - 3" -d3-format@1: - version "1.4.5" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" - integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== - "d3-format@1 - 3", d3-format@3: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== -d3-geo@1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f" - integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg== - dependencies: - d3-array "1" - d3-geo@3: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.0.tgz#74fd54e1f4cebd5185ac2039217a98d39b0a4c0e" @@ -1805,23 +3362,11 @@ d3-geo@3: dependencies: d3-array "2.5.0 - 3" -d3-hierarchy@1: - version "1.1.9" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" - integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== - d3-hierarchy@3: version "3.1.2" resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== -d3-interpolate@1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" - integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== - dependencies: - d3-color "1" - "d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" @@ -1839,43 +3384,28 @@ d3-path@1: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== -d3-polygon@1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e" - integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ== - d3-polygon@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== -d3-quadtree@1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135" - integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA== - "d3-quadtree@1 - 3", d3-quadtree@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== -d3-random@1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291" - integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ== - d3-random@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== -d3-scale-chromatic@1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98" - integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg== +d3-sankey@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/d3-sankey/-/d3-sankey-0.12.3.tgz#b3c268627bd72e5d80336e8de6acbfec9d15d01d" + integrity sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== dependencies: - d3-color "1" - d3-interpolate "1" + d3-array "1 - 2" + d3-shape "^1.2.0" d3-scale-chromatic@3: version "3.0.0" @@ -1885,18 +3415,6 @@ d3-scale-chromatic@3: d3-color "1 - 3" d3-interpolate "1 - 3" -d3-scale@2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" - integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== - dependencies: - d3-array "^1.2.0" - d3-collection "1" - d3-format "1" - d3-interpolate "1" - d3-time "1" - d3-time-format "2" - d3-scale@4: version "4.0.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" @@ -1908,23 +3426,11 @@ d3-scale@4: d3-time "2.1.1 - 3" d3-time-format "2 - 4" -d3-selection@1, d3-selection@^1.1.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c" - integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg== - -"d3-selection@2 - 3", d3-selection@3: +"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== -d3-shape@1: - version "1.3.7" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" - integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== - dependencies: - d3-path "1" - d3-shape@3: version "3.2.0" resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" @@ -1932,12 +3438,12 @@ d3-shape@3: dependencies: d3-path "^3.1.0" -d3-time-format@2: - version "2.3.0" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850" - integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ== +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== dependencies: - d3-time "1" + d3-path "1" "d3-time-format@2 - 4", d3-time-format@4: version "4.1.0" @@ -1946,11 +3452,6 @@ d3-time-format@2: dependencies: d3-time "1 - 3" -d3-time@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" - integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== - "d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" @@ -1958,29 +3459,12 @@ d3-time@1: dependencies: d3-array "2 - 3" -d3-timer@1: - version "1.0.10" - resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" - integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== - "d3-timer@1 - 3", d3-timer@3: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== -d3-transition@1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398" - integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA== - dependencies: - d3-color "1" - d3-dispatch "1" - d3-ease "1" - d3-interpolate "1" - d3-selection "^1.1.0" - d3-timer "1" - -"d3-transition@2 - 3", d3-transition@3: +"d3-transition@2 - 3", d3-transition@3, d3-transition@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== @@ -1991,22 +3475,6 @@ d3-transition@1: d3-interpolate "1 - 3" d3-timer "1 - 3" -d3-voronoi@1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" - integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg== - -d3-zoom@1: - version "1.8.3" - resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a" - integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ== - dependencies: - d3-dispatch "1" - d3-drag "1" - d3-interpolate "1" - d3-selection "1" - d3-transition "1" - d3-zoom@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" @@ -2018,47 +3486,10 @@ d3-zoom@3: d3-selection "2 - 3" d3-transition "2 - 3" -d3@^5.14: - version "5.16.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877" - integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw== - dependencies: - d3-array "1" - d3-axis "1" - d3-brush "1" - d3-chord "1" - d3-collection "1" - d3-color "1" - d3-contour "1" - d3-dispatch "1" - d3-drag "1" - d3-dsv "1" - d3-ease "1" - d3-fetch "1" - d3-force "1" - d3-format "1" - d3-geo "1" - d3-hierarchy "1" - d3-interpolate "1" - d3-path "1" - d3-polygon "1" - d3-quadtree "1" - d3-random "1" - d3-scale "2" - d3-scale-chromatic "1" - d3-selection "1" - d3-shape "1" - d3-time "1" - d3-time-format "2" - d3-timer "1" - d3-transition "1" - d3-voronoi "1" - d3-zoom "1" - -d3@^7.0.0: - version "7.8.5" - resolved "https://registry.yarnpkg.com/d3/-/d3-7.8.5.tgz#fde4b760d4486cdb6f0cc8e2cbff318af844635c" - integrity sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA== +d3@^7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== dependencies: d3-array "3" d3-axis "3" @@ -2091,28 +3522,18 @@ d3@^7.0.0: d3-transition "3" d3-zoom "3" -dagre-d3@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/dagre-d3/-/dagre-d3-0.6.4.tgz#0728d5ce7f177ca2337df141ceb60fbe6eeb7b29" - integrity sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ== +dagre-d3-es@7.0.14: + version "7.0.14" + resolved "https://registry.yarnpkg.com/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz#1272276e26457cf3b97dac569f8f0531ec33c377" + integrity sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg== dependencies: - d3 "^5.14" - dagre "^0.8.5" - graphlib "^2.1.8" - lodash "^4.17.15" + d3 "^7.9.0" + lodash-es "^4.17.21" -dagre@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" - integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== - dependencies: - graphlib "^2.1.8" - lodash "^4.17.15" - -dayjs@^1.11.6: - version "1.11.6" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" - integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== +dayjs@^1.11.19: + version "1.11.20" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.20.tgz#88d919fd639dc991415da5f4cb6f1b6650811938" + integrity sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -2121,41 +3542,47 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.1.0, debug@^4.3.4: +debug@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -deepmerge@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== +default-browser-id@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.1.tgz#f7a7ccb8f5104bf8e0f71ba3b1ccfa5eafdb21e8" + integrity sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== + +default-browser@^5.2.1: + version "5.5.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.5.0.tgz#2792e886f2422894545947cc80e1a444496c5976" + integrity sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw== dependencies: - execa "^5.0.0" + bundle-name "^4.1.0" + default-browser-id "^5.0.0" -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== define-property@^0.2.5: version "0.2.5" @@ -2186,7 +3613,7 @@ delaunator@5: dependencies: robust-predicates "^3.0.0" -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -2196,27 +3623,32 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -destroy@1.2.0: +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +destroy@1.2.0, destroy@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-libc@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== dependencies: - path-type "^4.0.0" - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + dequal "^2.0.0" dns-packet@^5.2.2: version "5.4.0" @@ -2241,7 +3673,16 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.2.0: +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -2253,10 +3694,19 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.5.tgz#c83ed5a3ae5ce23e52efe654ea052ffb358dd7e3" - integrity sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ== +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@^3.3.1: + version "3.4.10" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.10.tgz#96704295b4d8aeefcc8c7a90caa579b0ad69e46a" + integrity sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w== + optionalDependencies: + "@types/trusted-types" "^2.0.7" domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" @@ -2267,6 +3717,15 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +domutils@^3.0.1, domutils@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -2275,15 +3734,24 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.5.263: + version "1.5.286" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" + integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== emojis-list@^3.0.0: version "3.0.0" @@ -2295,28 +3763,56 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -enhanced-resolve@^5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" - integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +encoding-sniffer@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819" + integrity sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw== + dependencies: + iconv-lite "^0.6.3" + whatwg-encoding "^3.1.1" + +enhanced-resolve@^5.20.0: + version "5.20.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz#eeeb3966bea62c348c40a0cc9e7912e2557d0be0" + integrity sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA== dependencies: graceful-fs "^4.2.4" - tapable "^2.2.0" + tapable "^2.3.0" entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -envinfo@^7.8.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== +entities@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== + +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +envinfo@^7.18.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== error-ex@^1.3.1: version "1.3.2" @@ -2325,155 +3821,111 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== -esbuild-android-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz#5e8151d5f0a748c71a7fbea8cee844ccf008e6fc" - integrity sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q== +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -esbuild-android-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz#5ee72a6baa444bc96ffcb472a3ba4aba2cc80666" - integrity sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA== +es-module-lexer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz#f657cd7a9448dcdda9c070a3cb75e5dc1e85f5b1" + integrity sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw== -esbuild-darwin-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz#70047007e093fa1b3ba7ef86f9b3fa63db51fe25" - integrity sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q== - -esbuild-darwin-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz#41c951f23d9a70539bcca552bae6e5196696ae04" - integrity sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw== - -esbuild-freebsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz#a761b5afd12bbedb7d56c612e9cfa4d2711f33f0" - integrity sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw== - -esbuild-freebsd-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz#6b0839d4d58deabc6cbd96276eb8cbf94f7f335e" - integrity sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g== - -esbuild-linux-32@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz#bd50bfe22514d434d97d5150977496e2631345b4" - integrity sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA== - -esbuild-linux-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz#074bb2b194bf658245f8490f29c01ffcdfa8c931" - integrity sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA== - -esbuild-linux-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz#3bf789c4396dc032875a122988efd6f3733f28f5" - integrity sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ== - -esbuild-linux-arm@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz#b91b5a8d470053f6c2c9c8a5e67ec10a71fe4a67" - integrity sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A== - -esbuild-linux-mips64le@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz#2fb54099ada3c950a7536dfcba46172c61e580e2" - integrity sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A== - -esbuild-linux-ppc64le@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz#9e3b8c09825fb27886249dfb3142a750df29a1b7" - integrity sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg== - -esbuild-linux-riscv64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz#923d0f5b6e12ee0d1fe116b08e4ae4478fe40693" - integrity sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA== - -esbuild-linux-s390x@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz#3b1620220482b96266a0c6d9d471d451a1eab86f" - integrity sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww== - -esbuild-loader@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-2.20.0.tgz#28fcff0142fa7bd227512d69f31e9a6e202bb88f" - integrity sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: - esbuild "^0.15.6" - joycon "^3.0.1" - json5 "^2.2.0" - loader-utils "^2.0.0" - tapable "^2.2.0" - webpack-sources "^2.2.0" + es-errors "^1.3.0" -esbuild-netbsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz#276730f80da646859b1af5a740e7802d8cd73e42" - integrity sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w== +es-toolkit@^1.45.1: + version "1.46.1" + resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.46.1.tgz#38ca27191a98a867fc544b81cf1477a68947fb06" + integrity sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ== -esbuild-openbsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz#bd0eea1dd2ca0722ed489d88c26714034429f8ae" - integrity sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw== +esbuild-loader@~4.4.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-4.4.2.tgz#9a799c590840d3eafd66dbf86f4f7bfa45dd2495" + integrity sha512-8LdoT9sC7fzfvhxhsIAiWhzLJr9yT3ggmckXxsgvM07wgrRxhuT98XhLn3E7VczU5W5AFsPKv9DdWcZIubbWkQ== + dependencies: + esbuild "^0.27.1" + get-tsconfig "^4.10.1" + loader-utils "^2.0.4" + webpack-sources "^1.4.3" -esbuild-sunos-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz#5e56bf9eef3b2d92360d6d29dcde7722acbecc9e" - integrity sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg== - -esbuild-windows-32@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz#a4f1a301c1a2fa7701fcd4b91ef9d2620cf293d0" - integrity sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw== - -esbuild-windows-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz#bc2b467541744d653be4fe64eaa9b0dbbf8e07f6" - integrity sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA== - -esbuild-windows-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz#9a7266404334a86be800957eaee9aef94c3df328" - integrity sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA== - -esbuild@^0.15.12, esbuild@^0.15.6, esbuild@^0.15.9: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.12.tgz#6c8e22d6d3b7430d165c33848298d3fc9a1f251c" - integrity sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng== +esbuild@^0.25.0, esbuild@^0.25.10: + version "0.25.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== optionalDependencies: - "@esbuild/android-arm" "0.15.12" - "@esbuild/linux-loong64" "0.15.12" - esbuild-android-64 "0.15.12" - esbuild-android-arm64 "0.15.12" - esbuild-darwin-64 "0.15.12" - esbuild-darwin-arm64 "0.15.12" - esbuild-freebsd-64 "0.15.12" - esbuild-freebsd-arm64 "0.15.12" - esbuild-linux-32 "0.15.12" - esbuild-linux-64 "0.15.12" - esbuild-linux-arm "0.15.12" - esbuild-linux-arm64 "0.15.12" - esbuild-linux-mips64le "0.15.12" - esbuild-linux-ppc64le "0.15.12" - esbuild-linux-riscv64 "0.15.12" - esbuild-linux-s390x "0.15.12" - esbuild-netbsd-64 "0.15.12" - esbuild-openbsd-64 "0.15.12" - esbuild-sunos-64 "0.15.12" - esbuild-windows-32 "0.15.12" - esbuild-windows-64 "0.15.12" - esbuild-windows-arm64 "0.15.12" + "@esbuild/aix-ppc64" "0.25.12" + "@esbuild/android-arm" "0.25.12" + "@esbuild/android-arm64" "0.25.12" + "@esbuild/android-x64" "0.25.12" + "@esbuild/darwin-arm64" "0.25.12" + "@esbuild/darwin-x64" "0.25.12" + "@esbuild/freebsd-arm64" "0.25.12" + "@esbuild/freebsd-x64" "0.25.12" + "@esbuild/linux-arm" "0.25.12" + "@esbuild/linux-arm64" "0.25.12" + "@esbuild/linux-ia32" "0.25.12" + "@esbuild/linux-loong64" "0.25.12" + "@esbuild/linux-mips64el" "0.25.12" + "@esbuild/linux-ppc64" "0.25.12" + "@esbuild/linux-riscv64" "0.25.12" + "@esbuild/linux-s390x" "0.25.12" + "@esbuild/linux-x64" "0.25.12" + "@esbuild/netbsd-arm64" "0.25.12" + "@esbuild/netbsd-x64" "0.25.12" + "@esbuild/openbsd-arm64" "0.25.12" + "@esbuild/openbsd-x64" "0.25.12" + "@esbuild/openharmony-arm64" "0.25.12" + "@esbuild/sunos-x64" "0.25.12" + "@esbuild/win32-arm64" "0.25.12" + "@esbuild/win32-ia32" "0.25.12" + "@esbuild/win32-x64" "0.25.12" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +esbuild@^0.27.1: + version "0.27.4" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.4.tgz#b9591dd7e0ab803a11c9c3b602850403bef22f00" + integrity sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.4" + "@esbuild/android-arm" "0.27.4" + "@esbuild/android-arm64" "0.27.4" + "@esbuild/android-x64" "0.27.4" + "@esbuild/darwin-arm64" "0.27.4" + "@esbuild/darwin-x64" "0.27.4" + "@esbuild/freebsd-arm64" "0.27.4" + "@esbuild/freebsd-x64" "0.27.4" + "@esbuild/linux-arm" "0.27.4" + "@esbuild/linux-arm64" "0.27.4" + "@esbuild/linux-ia32" "0.27.4" + "@esbuild/linux-loong64" "0.27.4" + "@esbuild/linux-mips64el" "0.27.4" + "@esbuild/linux-ppc64" "0.27.4" + "@esbuild/linux-riscv64" "0.27.4" + "@esbuild/linux-s390x" "0.27.4" + "@esbuild/linux-x64" "0.27.4" + "@esbuild/netbsd-arm64" "0.27.4" + "@esbuild/netbsd-x64" "0.27.4" + "@esbuild/openbsd-arm64" "0.27.4" + "@esbuild/openbsd-x64" "0.27.4" + "@esbuild/openharmony-arm64" "0.27.4" + "@esbuild/sunos-x64" "0.27.4" + "@esbuild/win32-arm64" "0.27.4" + "@esbuild/win32-ia32" "0.27.4" + "@esbuild/win32-x64" "0.27.4" + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@~1.0.3: version "1.0.3" @@ -2535,36 +3987,6 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -execa@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" - integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.1" - human-signals "^3.0.1" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^3.0.7" - strip-final-newline "^3.0.0" - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -2578,39 +4000,39 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -express@^4.17.3, express@^4.18.2: - version "4.19.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" - integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== +express@^4.21.2, express@^4.22.1: + version "4.22.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" + integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" - content-disposition "0.5.4" + body-parser "~1.20.3" + content-disposition "~0.5.4" content-type "~1.0.4" - cookie "0.6.0" - cookie-signature "1.0.6" + cookie "~0.7.1" + cookie-signature "~1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" + finalhandler "~1.3.1" + fresh "~0.5.2" + http-errors "~2.0.0" + merge-descriptors "1.0.3" methods "~1.1.2" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "~0.1.12" proxy-addr "~2.0.7" - qs "6.11.0" + qs "~6.14.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "~0.19.0" + serve-static "~1.16.2" setprototypeof "1.2.0" - statuses "2.0.1" + statuses "~2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -2630,6 +4052,11 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2649,28 +4076,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.11: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" +fast-uri@^3.0.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.2.tgz#8af3d4fc9d3e71b11572cc2673b514a7d1a8c8ec" + integrity sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ== faye-websocket@^0.11.3: version "0.11.4" @@ -2679,10 +4088,15 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" -fflate@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" - integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== fill-range@^4.0.0: version "4.0.0" @@ -2714,23 +4128,28 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@~1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" + integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - statuses "2.0.1" + statuses "~2.0.2" unpipe "~1.0.0" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + follow-redirects@^1.0.0: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== for-in@^1.0.2: version "1.0.2" @@ -2742,10 +4161,10 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fraction.js@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== +fraction.js@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a" + integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ== fragment-cache@^0.2.1: version "0.2.1" @@ -2754,66 +4173,76 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: +fresh@0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== +fs-extra@^11.3.2: + version "11.3.4" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.4.tgz#ab6934eca8bcf6f7f6b82742e33591f86301d6fc" + integrity sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs-monkey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -get-intrinsic@^1.0.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-east-asian-width@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" + integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" -get-stream@^6.0.0, get-stream@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-tsconfig@^4.10.1: + version "4.13.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876" + integrity sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== + dependencies: + resolve-pkg-maps "^1.0.0" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - glob-parent@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -2821,45 +4250,37 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regex.js@^1.0.0, glob-to-regex.js@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz#2b323728271d133830850e32311f40766c5f6413" + integrity sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ== + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -globby@^13.1.1, globby@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" - integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^4.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graphlib@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" +graceful-fs@^4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== gray-matter@^4.0.3: version "4.0.3" @@ -2871,6 +4292,11 @@ gray-matter@^4.0.3: section-matter "^1.0.0" strip-bom-string "^1.0.0" +hachure-fill@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/hachure-fill/-/hachure-fill-0.5.2.tgz#d19bc4cc8750a5962b47fb1300557a85fcf934cc" + integrity sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg== + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -2886,10 +4312,10 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-value@^0.3.1: version "0.3.1" @@ -2922,23 +4348,105 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - hash-sum@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hast-util-from-html@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz#485c74785358beb80c4ba6346299311ac4c49c82" + integrity sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.1.0" + hast-util-from-parse5 "^8.0.0" + parse5 "^7.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + +hast-util-from-parse5@^8.0.0: + version "8.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz#830a35022fff28c3fea3697a98c2f4cc6b835a2e" + integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + hastscript "^9.0.0" + property-information "^7.0.0" + vfile "^6.0.0" + vfile-location "^5.0.0" + web-namespaces "^2.0.0" + +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-sanitize@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f" + integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg== + dependencies: + "@types/hast" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + unist-util-position "^5.0.0" + +hast-util-to-html@^9.0.0, hast-util-to-html@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff" + integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hookable@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -2949,11 +4457,6 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" - integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== - html-minifier-terser@^6.0.2: version "6.1.0" resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" @@ -2967,10 +4470,15 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-webpack-plugin@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" - integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +html-webpack-plugin@^5.6.4: + version "5.6.6" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz#5321b9579f4a1949318550ced99c2a4a4e60cbaf" + integrity sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" @@ -2978,6 +4486,16 @@ html-webpack-plugin@^5.5.0: pretty-error "^4.0.0" tapable "^2.0.0" +htmlparser2@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4" + integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.2.2" + entities "^7.0.1" + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -3014,6 +4532,17 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@~2.0.0, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" @@ -3029,10 +4558,10 @@ http-proxy-middleware@^0.19.1: lodash "^4.17.11" micromatch "^3.1.10" -http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== +http-proxy-middleware@^2.0.9: + version "2.0.9" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" + integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== dependencies: "@types/http-proxy" "^1.17.8" http-proxy "^1.18.1" @@ -3049,90 +4578,72 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -human-signals@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" - integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== - -iconv-lite@0.4, iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@0.6: +iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@~0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +immutable@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.5.tgz#93ee4db5c2a9ab42a4a783069f3c5d8847d40165" + integrity sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A== -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -immutable@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" - integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== +import-fresh@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + "internmap@1 - 2": version "2.0.3" resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== +ipaddr.js@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.3.0.tgz#71dce70e1398122208996d1c22f2ba46a24b1abc" + integrity sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg== is-accessor-descriptor@^0.1.6: version "0.1.6" @@ -3165,13 +4676,6 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -3204,10 +4708,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -3233,11 +4737,23 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-interactive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== +is-network-error@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.3.1.tgz#a2a86b80ffd6b05b774755c73c8aaab16597e58d" + integrity sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3255,6 +4771,11 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -3262,43 +4783,28 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - -is-unicode-supported@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" - integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== +is-unicode-supported@^2.0.0, is-unicode-supported@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +is-wsl@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.1.tgz#327897b26832a3eb117da6c27492d04ca132594f" + integrity sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw== dependencies: - is-docker "^2.0.0" + is-inside-container "^1.0.0" isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -3311,11 +4817,28 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -javascript-stringify@^2.0.1: +javascript-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== +jest-regex-util@30.0.1: + version "30.0.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" + integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== + +jest-util@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.3.0.tgz#95a4fbacf2dac20e768e2f1744b70519f2ba7980" + integrity sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg== + dependencies: + "@jest/types" "30.3.0" + "@types/node" "*" + chalk "^4.1.2" + ci-info "^4.2.0" + graceful-fs "^4.2.11" + picomatch "^4.0.3" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -3325,10 +4848,21 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -joycon@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" - integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== +jest-worker@^30.0.5: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.3.0.tgz#ae4dc1f1d93d0cba1415624fcedaec40ea764f14" + integrity sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ== + dependencies: + "@types/node" "*" + "@ungap/structured-clone" "^1.3.0" + jest-util "30.3.0" + merge-stream "^2.0.0" + supports-color "^8.1.1" + +jiti@^2.5.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== js-tokens@^4.0.0: version "4.0.0" @@ -3336,38 +4870,35 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + version "3.14.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - json-schema-traverse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json5@^2.1.2, json5@^2.2.0: +json5@^2.1.2: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -3377,10 +4908,17 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -khroma@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/khroma/-/khroma-1.4.1.tgz#ad6a5b6a972befc5112ce5129887a1a83af2c003" - integrity sha512-+GmxKvmiRuCcUYDgR7g5Ngo0JEDeOsGdNONdU2zsiBQaK4z19Y2NvXqfEDE0ZiIrg45GTZyAnPLVsLZZACYm3Q== +katex@^0.16.25: + version "0.16.40" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.40.tgz#87c94e4149f8fa7c22ff95bae1dc687355a38d63" + integrity sha512-1DJcK/L05k1Y9Gf7wMcyuqFOL6BiY3vY0CFcAM/LPRN04NALxcl6u7lOWNsp3f/bCHWxigzQl6FbR95XJ4R84Q== + dependencies: + commander "^8.3.0" + +khroma@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1" + integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -3406,34 +4944,121 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klona@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" - integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== +launch-editor@^2.6.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.14.1.tgz#f7e0da3f58aaea03fea01074d840b5f739ed7ddc" + integrity sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA== + dependencies: + picocolors "^1.1.1" + shell-quote "^1.8.4" -lilconfig@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" - integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + +layout-base@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285" + integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== + +lightningcss-android-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968" + integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg== + +lightningcss-darwin-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5" + integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ== + +lightningcss-darwin-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e" + integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w== + +lightningcss-freebsd-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575" + integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig== + +lightningcss-linux-arm-gnueabihf@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d" + integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw== + +lightningcss-linux-arm64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335" + integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ== + +lightningcss-linux-arm64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133" + integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg== + +lightningcss-linux-x64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6" + integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA== + +lightningcss-linux-x64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b" + integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg== + +lightningcss-win32-arm64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38" + integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw== + +lightningcss-win32-x64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a" + integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q== + +lightningcss@^1.30.2: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9" + integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-android-arm64 "1.32.0" + lightningcss-darwin-arm64 "1.32.0" + lightningcss-darwin-x64 "1.32.0" + lightningcss-freebsd-x64 "1.32.0" + lightningcss-linux-arm-gnueabihf "1.32.0" + lightningcss-linux-arm64-gnu "1.32.0" + lightningcss-linux-arm64-musl "1.32.0" + lightningcss-linux-x64-gnu "1.32.0" + lightningcss-linux-x64-musl "1.32.0" + lightningcss-win32-arm64-msvc "1.32.0" + lightningcss-win32-x64-msvc "1.32.0" + +lilconfig@^3.1.1, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -linkify-it@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" - integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== +linkify-it@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.1.tgz#10c4cecbb5c6828eabf81d3c801adc4a542dfb55" + integrity sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg== dependencies: - uc.micro "^1.0.1" + uc.micro "^2.0.0" -loader-runner@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" - integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== +loader-runner@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" + integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== -loader-utils@^2.0.0: +loader-utils@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== @@ -3442,18 +5067,33 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lodash-es@^4.17.21: + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.23.tgz#58c4360fd1b5d33afc6c0bbd3d1149349b1138e0" + integrity sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== -log-symbols@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" - integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.11, lodash@^4.17.20, lodash@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== + +log-symbols@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-7.0.1.tgz#f52e68037d96f589fc572ff2193dc424d48c195b" + integrity sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg== dependencies: - chalk "^5.0.0" - is-unicode-supported "^1.1.0" + is-unicode-supported "^2.0.0" + yoctocolors "^2.1.1" lower-case@^2.0.2: version "2.0.2" @@ -3462,19 +5102,12 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== dependencies: - yallist "^4.0.0" - -magic-string@^0.25.7: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== - dependencies: - sourcemap-codec "^1.4.8" + "@jridgewell/sourcemap-codec" "^1.5.5" map-cache@^0.2.2: version "0.2.2" @@ -3488,94 +5121,172 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it-anchor@^8.6.5: - version "8.6.5" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz#30c4bc5bbff327f15ce3c429010ec7ba75e7b5f8" - integrity sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ== +markdown-it-anchor@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-9.2.0.tgz#89375d9a2a79336403ab7c4fd36b1965cc45e5c8" + integrity sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg== -markdown-it-container@^3.0.0: +markdown-it-emoji@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-3.0.0.tgz#1d19b06040a020f9a827577bb7dbf67aa5de9a5b" - integrity sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw== + resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz#8475a04d671d7c93f931b76fb90c582768b7f0b5" + integrity sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg== -markdown-it-emoji@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz#cd42421c2fda1537d9cc12b9923f5c8aeb9029c8" - integrity sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ== - -markdown-it@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" - integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== +markdown-it@^14.1.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.2.0.tgz#06d48d9035e77d5b1c85adb315482fc8240289ef" + integrity sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ== dependencies: argparse "^2.0.1" - entities "~3.0.1" - linkify-it "^4.0.1" - mdurl "^1.0.1" - uc.micro "^1.0.5" + entities "^4.4.0" + linkify-it "^5.0.1" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +marked@^16.3.0: + version "16.4.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-16.4.2.tgz#4959a64be6c486f0db7467ead7ce288de54290a3" + integrity sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mdast-util-to-hast@^13.0.0: + version "13.2.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz#d7ff84ca499a57e2c060ae67548ad950e689a053" + integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" mdn-data@2.0.28: version "2.0.28" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== +mdn-data@2.27.1: + version "2.27.1" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.27.1.tgz#e37b9c50880b75366c4d40ac63d9bbcacdb61f0e" + integrity sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ== + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -medium-zoom@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/medium-zoom/-/medium-zoom-1.0.6.tgz#9247f21ca9313d8bbe9420aca153a410df08d027" - integrity sha512-UdiUWfvz9fZMg1pzf4dcuqA0W079o0mpqbTnOz5ip4VGYX96QjmbM+OgOU/0uOzAytxC0Ny4z+VcYQnhdifimg== +medium-zoom@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/medium-zoom/-/medium-zoom-1.1.0.tgz#6efb6bbda861a02064ee71a2617a8dc4381ecc71" + integrity sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ== -memfs@^3.4.3: - version "3.4.9" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.9.tgz#403bb953776d72fef4e39e1197a25ffa156d143a" - integrity sha512-3rm8kbrzpUGRyPKSGuk387NZOwQ90O4rI9tsWQkzNW7BLSnKGp23RsEsKK8N8QVCrtJoAMqy3spxHC4os4G6PQ== +memfs@^4.43.1: + version "4.57.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.57.1.tgz#5ccee42e2aab1cf086c45baf9c4ef1ff4fffb123" + integrity sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ== dependencies: - fs-monkey "^1.0.3" + "@jsonjoy.com/fs-core" "4.57.1" + "@jsonjoy.com/fs-fsa" "4.57.1" + "@jsonjoy.com/fs-node" "4.57.1" + "@jsonjoy.com/fs-node-builtins" "4.57.1" + "@jsonjoy.com/fs-node-to-fsa" "4.57.1" + "@jsonjoy.com/fs-node-utils" "4.57.1" + "@jsonjoy.com/fs-print" "4.57.1" + "@jsonjoy.com/fs-snapshot" "4.57.1" + "@jsonjoy.com/json-pack" "^1.11.0" + "@jsonjoy.com/util" "^1.9.0" + glob-to-regex.js "^1.0.1" + thingies "^2.5.0" + tree-dump "^1.0.3" + tslib "^2.0.0" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -mermaid@^8.14.0: - version "8.14.0" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.14.0.tgz#ef589b0537f56d6340069070edb51719a4faba00" - integrity sha512-ITSHjwVaby1Li738sxhF48sLTxcNyUAoWfoqyztL1f7J6JOLpHOuQPNLBb6lxGPUA0u7xP9IRULgvod0dKu35A== +mermaid@11.15.0: + version "11.15.0" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-11.15.0.tgz#b485c13ea5e1e74f3328c4bb00427bda87fa1c1e" + integrity sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw== dependencies: - "@braintree/sanitize-url" "^3.1.0" - d3 "^7.0.0" - dagre "^0.8.5" - dagre-d3 "^0.6.4" - dompurify "2.3.5" - graphlib "^2.1.8" - khroma "^1.4.1" - moment-mini "^2.24.0" - stylis "^4.0.10" + "@braintree/sanitize-url" "^7.1.1" + "@iconify/utils" "^3.0.2" + "@mermaid-js/parser" "^1.1.1" + "@types/d3" "^7.4.3" + "@upsetjs/venn.js" "^2.0.0" + cytoscape "^3.33.1" + cytoscape-cose-bilkent "^4.1.0" + cytoscape-fcose "^2.2.0" + d3 "^7.9.0" + d3-sankey "^0.12.3" + dagre-d3-es "7.0.14" + dayjs "^1.11.19" + dompurify "^3.3.1" + es-toolkit "^1.45.1" + katex "^0.16.25" + khroma "^2.1.0" + marked "^16.3.0" + roughjs "^4.6.6" + stylis "^4.3.6" + ts-dedent "^2.2.0" + uuid "^11.1.0 || ^12 || ^13 || ^14.0.0" methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -3595,7 +5306,7 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -3608,47 +5319,48 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime-types@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - -mini-css-extract-plugin@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" - integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== +mini-css-extract-plugin@^2.9.4: + version "2.10.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.1.tgz#a7f0bb890f4e1ce6dfc124bd1e6d6fcd3b359844" + integrity sha512-k7G3Y5QOegl380tXmZ68foBRRjE9Ljavx835ObdvmZjQ639izvZD8CS7BkWw1qKPPzHsGL/JDhl0uyU1zc2rJw== dependencies: schema-utils "^4.0.0" + tapable "^2.2.1" minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimist@^1.2.0: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -3662,10 +5374,15 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -moment-mini@^2.24.0: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.29.4.tgz#cbbcdc58ce1b267506f28ea6668dbe060a32758f" - integrity sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg== +mlly@^1.7.4, mlly@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.2.tgz#e7f7919a82d13b174405613117249a3f449d78bb" + integrity sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA== + dependencies: + acorn "^8.16.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.3" ms@2.0.0: version "2.0.0" @@ -3677,7 +5394,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3690,10 +5407,15 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +nanoid@^5.1.6: + version "5.1.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.7.tgz#a9f09a4ce73ba0b88830af36ee49666bad7827b6" + integrity sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ== nanomatch@^1.2.9: version "1.2.13" @@ -3717,6 +5439,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -3730,40 +5457,21 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npm-run-path@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" - integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== - dependencies: - path-key "^4.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -3780,10 +5488,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-visit@^1.0.0: version "1.0.1" @@ -3804,7 +5512,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -3818,64 +5526,70 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: - wrappy "1" + mimic-function "^5.0.0" -onetime@^5.1.0, onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" +oniguruma-parser@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz#82ba2208d7a2b69ee344b7efe0ae930c627dcc4a" + integrity sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w== -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== +oniguruma-to-es@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz#f2571bb8c8ea52c0bec5595c48cb2d5ebb2b809c" + integrity sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ== dependencies: - mimic-fn "^4.0.0" + oniguruma-parser "^0.12.1" + regex "^6.1.0" + regex-recursion "^6.0.2" -open@^8.0.9: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== +open@^10.0.3: + version "10.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" + integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + wsl-utils "^0.1.0" -ora@^6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.2.tgz#7b3c1356b42fd90fb1dad043d5dbe649388a0bf5" - integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw== +ora@^9.0.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-9.3.0.tgz#187c87cc1062350f549f481de32bf91424c2b0e3" + integrity sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw== dependencies: - bl "^5.0.0" - chalk "^5.0.0" - cli-cursor "^4.0.0" - cli-spinners "^2.6.1" + chalk "^5.6.2" + cli-cursor "^5.0.0" + cli-spinners "^3.2.0" is-interactive "^2.0.0" - is-unicode-supported "^1.1.0" - log-symbols "^5.1.0" - strip-ansi "^7.0.1" - wcwidth "^1.0.1" + is-unicode-supported "^2.1.0" + log-symbols "^7.0.1" + stdin-discarder "^0.3.1" + string-width "^8.1.0" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== +p-retry@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" + integrity sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ== dependencies: - "@types/retry" "0.12.0" + "@types/retry" "0.12.2" + is-network-error "^1.0.0" retry "^0.13.1" +package-manager-detector@^1.3.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz#70d0cf0aa02c877eeaf66c4d984ede0be9130734" + integrity sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -3891,7 +5605,7 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.0.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -3901,6 +5615,28 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5-htmlparser2-tree-adapter@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b" + integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g== + dependencies: + domhandler "^5.0.3" + parse5 "^7.0.0" + +parse5-parser-stream@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" + integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== + dependencies: + parse5 "^7.0.0" + +parse5@^7.0.0, parse5@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -3919,95 +5655,215 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-data-parser@0.1.0, path-data-parser@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/path-data-parser/-/path-data-parser-0.1.0.tgz#8f5ba5cc70fc7becb3dcefaea08e2659aba60b8c" + integrity sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-to-regexp@~0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== +pathe@^2.0.1, pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +perfect-debounce@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz#e7078e38f231cb191855c3136a4423aef725d261" + integrity sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +pkijs@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-3.4.0.tgz#d9164def30ff6d97be2d88966d5e36192499ca9c" + integrity sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw== + dependencies: + "@noble/hashes" "1.4.0" + asn1js "^3.0.6" + bytestreamjs "^2.0.1" + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + +points-on-curve@0.2.0, points-on-curve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-0.2.0.tgz#7dbb98c43791859434284761330fa893cb81b4d1" + integrity sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A== + +points-on-path@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/points-on-path/-/points-on-path-0.2.1.tgz#553202b5424c53bed37135b318858eacff85dd52" + integrity sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g== + dependencies: + path-data-parser "0.1.0" + points-on-curve "0.2.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== -postcss-csso@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-csso/-/postcss-csso-6.0.1.tgz#6a3e812e236fde6d710a525f2b63e6d9da5a5008" - integrity sha512-ZV4yEziMrx6CEiqabGLrDva0pMD7Fbw7yP+LzJvaynM4OJgTssGN6dHiMsJMJdpmNaLJltXVLsrb/5sxbFa8sA== +postcss-calc@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-10.1.1.tgz#52b385f2e628239686eb6e3a16207a43f36064ca" + integrity sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw== dependencies: - csso "^5.0.5" + postcss-selector-parser "^7.0.0" + postcss-value-parser "^4.2.0" -postcss-load-config@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" - integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== +postcss-colormin@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-7.0.6.tgz#8f1bcfaa6f4959a872824f3b5bd4e1278bf35e45" + integrity sha512-oXM2mdx6IBTRm39797QguYzVEWzbdlFiMNfq88fCCN1Wepw3CYmJ/1/Ifa/KjWo+j5ZURDl2NTldLJIw51IeNQ== dependencies: - lilconfig "^2.0.5" - yaml "^2.1.1" + browserslist "^4.28.1" + caniuse-api "^3.0.0" + colord "^2.9.3" + postcss-value-parser "^4.2.0" -postcss-loader@^7.0.1: +postcss-convert-values@^7.0.9: + version "7.0.9" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-7.0.9.tgz#6ada5c2c480f1ddbd4c886339025a916ecc8ff01" + integrity sha512-l6uATQATZaCa0bckHV+r6dLXfWtUBKXxO3jK+AtxxJJtgMPD+VhhPCCx51I4/5w8U5uHV67g3w7PXj+V3wlMlg== + dependencies: + browserslist "^4.28.1" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-7.0.6.tgz#4e9c696a83391d90b3ffa4485ac144e555db443c" + integrity sha512-Sq+Fzj1Eg5/CPf1ERb0wS1Im5cvE2gDXCE+si4HCn1sf+jpQZxDI4DXEp8t77B/ImzDceWE2ebJQFXdqZ6GRJw== + dependencies: + postcss-selector-parser "^7.1.1" + +postcss-discard-duplicates@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz#9cf3e659d4f94b046eef6f93679490c0250a8e4e" + integrity sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w== + +postcss-discard-empty@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.0.1.tgz#4c883cc0a1b2bfe2074377b7a74c1cd805684395" - integrity sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ== + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz#b6c57e8b5c69023169abea30dceb93f98a2ffd9f" + integrity sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg== + +postcss-discard-overridden@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz#bd9c9bc5e4548d3b6e67e7f8d64f2c9d745ae2a0" + integrity sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg== + +postcss-load-config@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== dependencies: - cosmiconfig "^7.0.0" - klona "^2.0.5" - semver "^7.3.7" + lilconfig "^3.1.1" -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +postcss-loader@^8.2.0: + version "8.2.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-8.2.1.tgz#c3d9b35498af906fe6c25eb62583c06f619f92fc" + integrity sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow== + dependencies: + cosmiconfig "^9.0.0" + jiti "^2.5.1" + semver "^7.6.2" -postcss-modules-local-by-default@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" - integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== +postcss-merge-longhand@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz#e1b126e92f583815482e8b1e82c47d2435a20421" + integrity sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^7.0.5" + +postcss-merge-rules@^7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-7.0.8.tgz#d63ce875b9f7880ca4aa89d9ae3eaa3657215f82" + integrity sha512-BOR1iAM8jnr7zoQSlpeBmCsWV5Uudi/+5j7k05D0O/WP3+OFMPD86c1j/20xiuRtyt45bhxw/7hnhZNhW2mNFA== + dependencies: + browserslist "^4.28.1" + caniuse-api "^3.0.0" + cssnano-utils "^5.0.1" + postcss-selector-parser "^7.1.1" + +postcss-minify-font-values@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz#6fb4770131b31fd5a2014bd84e32f386a3406664" + integrity sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz#933cb642dd00df397237c17194f37dcbe4cad739" + integrity sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A== + dependencies: + colord "^2.9.3" + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-7.0.6.tgz#ca0df1bd4eaa70ee7a4ee17f393d275988f44657" + integrity sha512-YOn02gC68JijlaXVuKvFSCvQOhTpblkcfDre2hb/Aaa58r2BIaK4AtE/cyZf2wV7YKAG+UlP9DT+By0ry1E4VQ== + dependencies: + browserslist "^4.28.1" + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-7.0.6.tgz#1e0240e1fa3372d81d3f0586591f1e8d2ae21e16" + integrity sha512-lIbC0jy3AAwDxEgciZlBullDiMBeBCT+fz5G8RcA9MWqh/hfUkpOI3vNDUNEZHgokaoiv0juB9Y8fGcON7rU/A== + dependencies: + cssesc "^3.0.0" + postcss-selector-parser "^7.1.1" + +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz#d150f43837831dae25e4085596e84f6f5d6ec368" + integrity sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw== dependencies: icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" + postcss-selector-parser "^7.0.0" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c" + integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA== dependencies: - postcss-selector-parser "^6.0.4" + postcss-selector-parser "^7.0.0" postcss-modules-values@^4.0.0: version "4.0.0" @@ -4016,27 +5872,127 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== +postcss-normalize-charset@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz#bccc3f7c5f4440883608eea8b444c8f41ce55ff6" + integrity sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ== + +postcss-normalize-display-values@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz#feb40277d89a7f677b67a84cac999f0306e38235" + integrity sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz#c771c0d33034455205f060b999d8557c2308d22c" + integrity sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz#05fe4d838eedbd996436c5cab78feef9bb1ae57b" + integrity sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz#0f111e7b5dfb6de6ab19f09d9e1c16fabeee232f" + integrity sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz#7b645a36f113fec49d95d56386c9980316c71216" + integrity sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.6.tgz#6935d6baf7f7374a34c216a7fe13229acd1073f2" + integrity sha512-z6bwTV84YW6ZvvNoaNLuzRW4/uWxDKYI1iIDrzk6D2YTL7hICApy+Q1LP6vBEsljX8FM7YSuV9qI79XESd4ddQ== + dependencies: + browserslist "^4.28.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz#d6471a22b6747ce93d7038c16eb9f1ba8b307e25" + integrity sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz#ab8e9ff1f3213f3f3851c0a7d0e4ce4716777cea" + integrity sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz#0e803fbb9601e254270481772252de9a8c905f48" + integrity sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw== + dependencies: + cssnano-utils "^5.0.1" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-7.0.6.tgz#fa3af45e60cd04d9a3d29315eb97c82b7b447ead" + integrity sha512-G6ZyK68AmrPdMB6wyeA37ejnnRG2S8xinJrZJnOv+IaRKf6koPAVbQsiC7MfkmXaGmF1UO+QCijb27wfpxuRNg== + dependencies: + browserslist "^4.28.1" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz#f87111264b0dfa07e1f708d7e6401578707be5d6" + integrity sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^7.0.0, postcss-selector-parser@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz#e75d2e0d843f620e5df69076166f4e16f891cb9f" + integrity sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-svgo@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-7.1.1.tgz#14b90fd2a1b1f27bcb2d0ef0444f954237e7883c" + integrity sha512-zU9H9oEDrUFKa0JB7w+IYL7Qs9ey1mZyjhbf0KLxwJDdDRtoPvCmaEfknzqfHj44QS9VD6c5sJnBAVYTLRg/Sg== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^4.0.1" + +postcss-unique-selectors@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-7.0.5.tgz#a7dd5652c95f459176e5f135c021473e4ee58874" + integrity sha512-3QoYmEt4qg/rUWDn6Tc8+ZVPmbp4G1hXDtCNWDx0st8SjtCbRcxRXDDM1QrEiXGG3A45zscSJFb4QH90LViyxg== + dependencies: + postcss-selector-parser "^7.1.1" + postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.1.10, postcss@^8.4.16, postcss@^8.4.18, postcss@^8.4.7: - version "8.4.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" - integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== +postcss@^8.4.40, postcss@^8.5.6, postcss@^8.5.8: + version "8.5.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.14.tgz#a66c2d7808fadf69ebb5b84a03f8bafd76c4919c" + integrity sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg== dependencies: - nanoid "^3.3.6" - picocolors "^1.0.0" - source-map-js "^1.0.2" + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" pretty-error@^4.0.0: version "4.0.0" @@ -4046,16 +6002,21 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -prismjs@^1.29.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" - integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== +prismjs@^1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9" + integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -4064,22 +6025,34 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +pvtsutils@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.6.tgz#ec46e34db7422b9e4fdc5490578c1883657d6001" + integrity sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg== dependencies: - side-channel "^1.0.4" + tslib "^2.8.1" -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +pvutils@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.5.tgz#84b0dea4a5d670249aa9800511804ee0b7c2809c" + integrity sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA== + +qs@~6.14.0: + version "6.14.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" + integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== + dependencies: + side-channel "^1.1.0" randombytes@^2.1.0: version "2.1.0" @@ -4093,15 +6066,15 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" readable-stream@^2.0.1: version "2.3.7" @@ -4116,7 +6089,7 @@ readable-stream@^2.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.4.0: +readable-stream@^3.0.6: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -4125,6 +6098,16 @@ readable-stream@^3.0.6, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-5.0.0.tgz#fbf1f71a727891d685bb1786f9ba74084f6e2f91" + integrity sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -4132,6 +6115,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -4140,6 +6128,51 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regex-recursion@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-6.0.2.tgz#a0b1977a74c87f073377b938dbedfab2ea582b33" + integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== + dependencies: + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803" + integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== + dependencies: + regex-utilities "^2.3.0" + +rehype-parse@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.1.tgz#9993bda129acc64c417a9d3654a7be38b2a94c20" + integrity sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag== + dependencies: + "@types/hast" "^3.0.0" + hast-util-from-html "^2.0.0" + unified "^11.0.0" + +rehype-sanitize@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz#16e95f4a67a69cbf0f79e113c8e0df48203db73c" + integrity sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg== + dependencies: + "@types/hast" "^3.0.0" + hast-util-sanitize "^5.0.0" + +rehype-stringify@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-10.0.1.tgz#2ec1ebc56c6aba07905d3b4470bdf0f684f30b75" + integrity sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA== + dependencies: + "@types/hast" "^3.0.0" + hast-util-to-html "^9.0.0" + unified "^11.0.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -4181,27 +6214,23 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -restore-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" - integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" + onetime "^7.0.0" + signal-exit "^4.1.0" ret@~0.1.10: version "0.1.15" @@ -4213,59 +6242,82 @@ retry@^0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - robust-predicates@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== -rollup@^2.79.1: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== - optionalDependencies: - fsevents "~2.3.2" - -rollup@~2.78.0: - version "2.78.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.78.1.tgz#52fe3934d9c83cb4f7c4cb5fb75d88591be8648f" - integrity sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg== - optionalDependencies: - fsevents "~2.3.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== +rollup@^4.43.0, rollup@^4.52.4: + version "4.60.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.60.0.tgz#d7d68c8cda873e96e08b2443505609b7e7be9eb8" + integrity sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ== dependencies: - queue-microtask "^1.2.2" + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.60.0" + "@rollup/rollup-android-arm64" "4.60.0" + "@rollup/rollup-darwin-arm64" "4.60.0" + "@rollup/rollup-darwin-x64" "4.60.0" + "@rollup/rollup-freebsd-arm64" "4.60.0" + "@rollup/rollup-freebsd-x64" "4.60.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.60.0" + "@rollup/rollup-linux-arm-musleabihf" "4.60.0" + "@rollup/rollup-linux-arm64-gnu" "4.60.0" + "@rollup/rollup-linux-arm64-musl" "4.60.0" + "@rollup/rollup-linux-loong64-gnu" "4.60.0" + "@rollup/rollup-linux-loong64-musl" "4.60.0" + "@rollup/rollup-linux-ppc64-gnu" "4.60.0" + "@rollup/rollup-linux-ppc64-musl" "4.60.0" + "@rollup/rollup-linux-riscv64-gnu" "4.60.0" + "@rollup/rollup-linux-riscv64-musl" "4.60.0" + "@rollup/rollup-linux-s390x-gnu" "4.60.0" + "@rollup/rollup-linux-x64-gnu" "4.60.0" + "@rollup/rollup-linux-x64-musl" "4.60.0" + "@rollup/rollup-openbsd-x64" "4.60.0" + "@rollup/rollup-openharmony-arm64" "4.60.0" + "@rollup/rollup-win32-arm64-msvc" "4.60.0" + "@rollup/rollup-win32-ia32-msvc" "4.60.0" + "@rollup/rollup-win32-x64-gnu" "4.60.0" + "@rollup/rollup-win32-x64-msvc" "4.60.0" + fsevents "~2.3.2" + +roughjs@^4.6.6: + version "4.6.6" + resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.6.tgz#1059f49a5e0c80dee541a005b20cc322b222158b" + integrity sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ== + dependencies: + hachure-fill "^0.5.2" + path-data-parser "^0.1.0" + points-on-curve "^0.2.0" + points-on-path "^0.2.1" + +run-applescript@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.1.0.tgz#2e9e54c4664ec3106c5b5630e249d3d6595c4911" + integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== rw@1: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +rxjs@^7.4.0: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + dependencies: + tslib "^2.1.0" safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -4278,23 +6330,154 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass@^1.55.0: - version "1.55.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.55.0.tgz#0c4d3c293cfe8f8a2e8d3b666e1cf1bff8065d1c" - integrity sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A== +sass-embedded-all-unknown@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.98.0.tgz#0c91965a1ba012f0aadb6b3d55a6d5c0b18bac04" + integrity sha512-6n4RyK7/1mhdfYvpP3CClS3fGoYqDvRmLClCESS6I7+SAzqjxvGG6u5Fo+cb1nrPNbbilgbM4QKdgcgWHO9NCA== dependencies: - chokidar ">=3.0.0 <4.0.0" - immutable "^4.0.0" - source-map-js ">=0.6.2 <2.0.0" + sass "1.98.0" -schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +sass-embedded-android-arm64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.98.0.tgz#58e21f445c301b59728003c503a80b5fbb1182a7" + integrity sha512-M9Ra98A6vYJHpwhoC/5EuH1eOshQ9ZyNwC8XifUDSbRl/cGeQceT1NReR9wFj3L7s1pIbmes1vMmaY2np0uAKQ== + +sass-embedded-android-arm@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm/-/sass-embedded-android-arm-1.98.0.tgz#40e4b7ce6f474416226a76776ce1e4dc9fde466c" + integrity sha512-LjGiMhHgu7VL1n7EJxTCre1x14bUsWd9d3dnkS2rku003IWOI/fxc7OXgaKagoVzok1kv09rzO3vFXJR5ZeONQ== + +sass-embedded-android-riscv64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.98.0.tgz#9de379d27ed167444cdc1bfc80d102a8a9d0e111" + integrity sha512-WPe+0NbaJIZE1fq/RfCZANMeIgmy83x4f+SvFOG7LhUthHpZWcOcrPTsCKKmN3xMT3iw+4DXvqTYOCYGRL3hcQ== + +sass-embedded-android-x64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-android-x64/-/sass-embedded-android-x64-1.98.0.tgz#a4f688057d737b711f96f840231b3dce4f43c850" + integrity sha512-zrD25dT7OHPEgLWuPEByybnIfx4rnCtfge4clBgjZdZ3lF6E7qNLRBtSBmoFflh6Vg0RlEjJo5VlpnTMBM5MQQ== + +sass-embedded-darwin-arm64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.98.0.tgz#a71da5fe877884e0b5aee3e59fb425abda94bb02" + integrity sha512-cgr1z9rBnCdMf8K+JabIaYd9Rag2OJi5mjq08XJfbJGMZV/TA6hFJCLGkr5/+ZOn4/geTM5/3aSfQ8z5EIJAOg== + +sass-embedded-darwin-x64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.98.0.tgz#0dedd7f956bf25252c13af0ca983af23e9b87dff" + integrity sha512-OLBOCs/NPeiMqTdOrMFbVHBQFj19GS3bSVSxIhcCq16ZyhouUkYJEZjxQgzv9SWA2q6Ki8GCqp4k6jMeUY9dcA== + +sass-embedded-linux-arm64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.98.0.tgz#a17f72336a25664ae536bd5df19b42b84a08625d" + integrity sha512-axOE3t2MTBwCtkUCbrdM++Gj0gC0fdHJPrgzQ+q1WUmY9NoNMGqflBtk5mBZaWUeha2qYO3FawxCB8lctFwCtw== + +sass-embedded-linux-arm@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.98.0.tgz#af86953007a36e0b3dd21dcb8244ecc420447c6d" + integrity sha512-03baQZCxVyEp8v1NWBRlzGYrmVT/LK7ZrHlF1piscGiGxwfdxoLXVuxsylx3qn/dD/4i/rh7Bzk7reK1br9jvQ== + +sass-embedded-linux-musl-arm64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.98.0.tgz#e8b34278e0313b20de0285d3aefe0c6f19742bb4" + integrity sha512-LeqNxQA8y4opjhe68CcFvMzCSrBuJqYVFbwElEj9bagHXQHTp9xVPJRn6VcrC+0VLEDq13HVXMv7RslIuU0zmA== + +sass-embedded-linux-musl-arm@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.98.0.tgz#8c007f05d54bc9a86457733517dae64fa9f1f99d" + integrity sha512-OBkjTDPYR4hSaueOGIM6FDpl9nt/VZwbSRpbNu9/eEJcxE8G/vynRugW8KRZmCFjPy8j/jkGBvvS+k9iOqKV3g== + +sass-embedded-linux-musl-riscv64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.98.0.tgz#846fe9bf7b31baf7d86f3b349768a56d55034748" + integrity sha512-7w6hSuOHKt8FZsmjRb3iGSxEzM87fO9+M8nt5JIQYMhHTj5C+JY/vcske0v715HCVj5e1xyTnbGXf8FcASeAIw== + +sass-embedded-linux-musl-x64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.98.0.tgz#c29018ed2909c4a2e37980053e52f71b35d0f76a" + integrity sha512-QikNyDEJOVqPmxyCFkci8ZdCwEssdItfjQFJB+D+Uy5HFqcS5Lv3d3GxWNX/h1dSb23RPyQdQc267ok5SbEyJw== + +sass-embedded-linux-riscv64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.98.0.tgz#91b262e32e14fd4c0cd72b1e2c320cdcdd8d9d94" + integrity sha512-E7fNytc/v4xFBQKzgzBddV/jretA4ULAPO6XmtBiQu4zZBdBozuSxsQLe2+XXeb0X4S2GIl72V7IPABdqke/vA== + +sass-embedded-linux-x64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.98.0.tgz#f89609c115914d09f7ae6d9c4b9cae42c903ffec" + integrity sha512-VsvP0t/uw00mMNPv3vwyYKUrFbqzxQHnRMO+bHdAMjvLw4NFf6mscpym9Bzf+NXwi1ZNKnB6DtXjmcpcvqFqYg== + +sass-embedded-unknown-all@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.98.0.tgz#1256f1c0ccd5ac8d1004a8764d91735e4fafac57" + integrity sha512-C4MMzcAo3oEDQnW7L8SBgB9F2Fq5qHPnaYTZRMOH3Mp/7kM4OooBInXpCiiFjLnjY95hzP4KyctVx0uYR6MYlQ== dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" + sass "1.98.0" + +sass-embedded-win32-arm64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.98.0.tgz#d7c28b1504b0cfc253eaa2e2e675d48b2dd54311" + integrity sha512-nP/10xbAiPbhQkMr3zQfXE4TuOxPzWRQe1Hgbi90jv2R4TbzbqQTuZVOaJf7KOAN4L2Bo6XCTRjK5XkVnwZuwQ== + +sass-embedded-win32-x64@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.98.0.tgz#861ca78a70b6d6b0da8e0bc001f8b2f595e62c37" + integrity sha512-/lbrVsfbcbdZQ5SJCWcV0NVPd6YRs+FtAnfedp4WbCkO/ZO7Zt/58MvI4X2BVpRY/Nt5ZBo1/7v2gYcQ+J4svQ== + +sass-embedded@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.98.0.tgz#c8a314cd522361f814d838d1518be4406e9db716" + integrity sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg== + dependencies: + "@bufbuild/protobuf" "^2.5.0" + colorjs.io "^0.5.0" + immutable "^5.1.5" + rxjs "^7.4.0" + supports-color "^8.1.1" + sync-child-process "^1.0.2" + varint "^6.0.0" + optionalDependencies: + sass-embedded-all-unknown "1.98.0" + sass-embedded-android-arm "1.98.0" + sass-embedded-android-arm64 "1.98.0" + sass-embedded-android-riscv64 "1.98.0" + sass-embedded-android-x64 "1.98.0" + sass-embedded-darwin-arm64 "1.98.0" + sass-embedded-darwin-x64 "1.98.0" + sass-embedded-linux-arm "1.98.0" + sass-embedded-linux-arm64 "1.98.0" + sass-embedded-linux-musl-arm "1.98.0" + sass-embedded-linux-musl-arm64 "1.98.0" + sass-embedded-linux-musl-riscv64 "1.98.0" + sass-embedded-linux-musl-x64 "1.98.0" + sass-embedded-linux-riscv64 "1.98.0" + sass-embedded-linux-x64 "1.98.0" + sass-embedded-unknown-all "1.98.0" + sass-embedded-win32-arm64 "1.98.0" + sass-embedded-win32-x64 "1.98.0" + +sass-loader@16.0.7: + version "16.0.7" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-16.0.7.tgz#d1f8723b795805831d41b5825e3d9cd72cb939e7" + integrity sha512-w6q+fRHourZ+e+xA1kcsF27iGM6jdB8teexYCfdUw0sYgcDNeZESnDNT9sUmmPm3ooziwUJXGwZJSTF3kOdBfA== + dependencies: + neo-async "^2.6.2" + +sass@1.98.0: + version "1.98.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.98.0.tgz#924ce85a3745ccaccd976262fdc1bc0c13aa8e57" + integrity sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A== + dependencies: + chokidar "^4.0.0" + immutable "^5.1.5" + source-map-js ">=0.6.2 <2.0.0" + optionalDependencies: + "@parcel/watcher" "^2.4.1" + +sax@^1.4.1, sax@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.0.tgz#da59637629307b97e7c4cb28e080a7bc38560d5b" + integrity sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA== schema-utils@^4.0.0: version "4.0.0" @@ -4306,6 +6489,16 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + section-matter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" @@ -4319,19 +6512,18 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" - integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== +selfsigned@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-5.5.0.tgz#4c9ab7c7c9f35f18fb6a9882c253eb0e6bd6557b" + integrity sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew== dependencies: - node-forge "^1" + "@peculiar/x509" "^1.14.2" + pkijs "^3.3.3" -semver@^7.3.5, semver@^7.3.7: - version "7.5.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" - integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== - dependencies: - lru-cache "^6.0.0" +semver@^7.6.2, semver@^7.6.3: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== send@0.18.0: version "0.18.0" @@ -4352,10 +6544,29 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -serialize-javascript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== +send@~0.19.0, send@~0.19.1: + version "0.19.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" + integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "~0.5.2" + http-errors "~2.0.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.4.1" + range-parser "~1.2.1" + statuses "~2.0.2" + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -4372,7 +6583,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.15.0, serve-static@^1.13.2: +serve-static@^1.13.2: version "1.15.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== @@ -4382,6 +6593,16 @@ serve-static@1.15.0, serve-static@^1.13.2: parseurl "~1.3.3" send "0.18.0" +serve-static@~1.16.2: + version "1.16.3" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" + integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "~0.19.1" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -4397,7 +6618,7 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: +setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -4409,45 +6630,79 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== +shell-quote@^1.8.4: + version "1.8.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.4.tgz#2edd9a4dcefc96649e2e2cb12f637b1f1d92a190" + integrity sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ== + +shiki@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-4.0.2.tgz#d81495df11e1cb8a05907310a6d051e054435586" + integrity sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ== dependencies: - shebang-regex "^3.0.0" + "@shikijs/core" "4.0.2" + "@shikijs/engine-javascript" "4.0.2" + "@shikijs/engine-oniguruma" "4.0.2" + "@shikijs/langs" "4.0.2" + "@shikijs/themes" "4.0.2" + "@shikijs/types" "4.0.2" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shiki@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.11.1.tgz#df0f719e7ab592c484d8b73ec10e215a503ab8cc" - integrity sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: - jsonc-parser "^3.0.0" - vscode-oniguruma "^1.6.1" - vscode-textmate "^6.0.0" + es-errors "^1.3.0" + object-inspect "^1.13.3" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sitemap@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-9.0.1.tgz#33e9b09e2177eb896e05b16da4219f53919842f6" + integrity sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ== + dependencies: + "@types/node" "^24.9.2" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.4.1" snapdragon-node@^2.0.1: version "2.1.1" @@ -4488,16 +6743,21 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" -source-list-map@^2.0.1: +source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -4527,15 +6787,15 @@ source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: +source-map@^0.6.0, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== spdy-transport@^3.0.0: version "3.0.0" @@ -4590,6 +6850,24 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@~2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +stdin-discarder@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.3.1.tgz#92a1e741e709248865d0562bb7babe84d350ae6a" + integrity sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA== + +string-width@^8.1.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.0.tgz#bdb6a9bd6d7800db635adae96cdb0443fec56c42" + integrity sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw== + dependencies: + get-east-asian-width "^1.5.0" + strip-ansi "^7.1.2" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -4604,6 +6882,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -4611,37 +6897,35 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== +strip-ansi@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== dependencies: - ansi-regex "^6.0.1" + ansi-regex "^6.2.2" strip-bom-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +style-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5" + integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA== -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +stylehacks@^7.0.5: + version "7.0.8" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-7.0.8.tgz#cb5d00bb1779a30c4d408a7d576c016c88b36491" + integrity sha512-I3f053GBLIiS5Fg6OMFhq/c+yW+5Hc2+1fgq7gElDMMSqwlRb3tBf2ef6ucLStYRpId4q//bQO1FjcyNyy4yDQ== + dependencies: + browserslist "^4.28.1" + postcss-selector-parser "^7.1.1" -style-loader@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" - integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== - -stylis@^4.0.10: - version "4.2.0" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" - integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== +stylis@^4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== supports-color@^5.3.0: version "5.5.0" @@ -4657,35 +6941,66 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svgo@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-4.0.1.tgz#c82dacd04ee9f1d55cd4e0b7f9a214c86670e3ee" + integrity sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w== + dependencies: + commander "^11.1.0" + css-select "^5.1.0" + css-tree "^3.0.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.1.1" + sax "^1.5.0" -tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: +sync-child-process@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/sync-child-process/-/sync-child-process-1.0.2.tgz#45e7c72e756d1243e80b547ea2e17957ab9e367f" + integrity sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA== + dependencies: + sync-message-port "^1.0.0" + +sync-message-port@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/sync-message-port/-/sync-message-port-1.2.0.tgz#4b0d622085f21496061037125dec61755d96e330" + integrity sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg== + +synckit@^0.11.12: + version "0.11.12" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" + integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== + dependencies: + "@pkgr/core" "^0.2.9" + +tapable@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.1.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" - integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== - dependencies: - "@jridgewell/trace-mapping" "^0.3.14" - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - terser "^5.14.1" +tapable@^2.2.1, tapable@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== -terser@^5.10.0, terser@^5.14.1: +terser-webpack-plugin@^5.3.17: + version "5.4.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz#95fc4cf4437e587be11ecf37d08636089174d76b" + integrity sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + terser "^5.31.1" + +terser@^5.10.0: version "5.15.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw== @@ -4695,11 +7010,39 @@ terser@^5.10.0, terser@^5.14.1: commander "^2.20.0" source-map-support "~0.5.20" +terser@^5.31.1: + version "5.46.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +thingies@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-2.6.0.tgz#e09b98b9e6f6caf8a759eca8481fea1de974d2b1" + integrity sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tinyexec@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.4.tgz#6c60864fe1d01331b2f17c6890f535d7e5385408" + integrity sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw== + +tinyglobby@^0.2.12, tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -4732,21 +7075,53 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -ts-debounce@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ts-debounce/-/ts-debounce-4.0.0.tgz#33440ef64fab53793c3d546a8ca6ae539ec15841" - integrity sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg== +tree-dump@^1.0.3, tree-dump@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.1.0.tgz#ab29129169dc46004414f5a9d4a3c6e89f13e8a4" + integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA== + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + +ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tslib@^2.0.3: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tsyringe@^4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.10.0.tgz#d0c95815d584464214060285eaaadd94aa03299c" + integrity sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw== + dependencies: + tslib "^1.9.3" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -4755,10 +7130,38 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +ufo@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.3.tgz#799666e4e88c122a9659805e30b9dc071c3aed4f" + integrity sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q== + +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + +undici@^7.19.0: + version "7.24.5" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.24.5.tgz#7debcf5623df2d1cb469b6face01645d9c852ae2" + integrity sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q== + +unified@^11.0.0, unified@^11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" union-value@^1.0.0: version "1.0.1" @@ -4770,12 +7173,50 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" +unist-util-is@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9" + integrity sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02" + integrity sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468" + integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -4793,13 +7234,13 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.1" uri-js@^4.2.2: version "4.4.1" @@ -4833,126 +7274,108 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +"uuid@^11.1.0 || ^12 || ^13 || ^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-14.0.0.tgz#0af883220163d264ffe0c084f6b8a89b9666966d" + integrity sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +varint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -vite@~3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/vite/-/vite-3.1.8.tgz#fa29144167d19b773baffd65b3972ea4c12359c9" - integrity sha512-m7jJe3nufUbuOfotkntGFupinL/fmuTNuQmiVE7cH2IZMuf4UbfbGYMUT3jVWgGYuRVLY9j8NnrRqgw5rr5QTg== +vfile-location@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3" + integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== dependencies: - esbuild "^0.15.9" - postcss "^8.4.16" - resolve "^1.22.1" - rollup "~2.78.0" + "@types/unist" "^3.0.0" + vfile "^6.0.0" + +vfile-message@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" + integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + +vite@~7.1.9: + version "7.1.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.12.tgz#8b29a3f61eba23bcb93fc9ec9af4a3a1e83eecdb" + integrity sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug== + dependencies: + esbuild "^0.25.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" optionalDependencies: - fsevents "~2.3.2" + fsevents "~2.3.3" -vscode-oniguruma@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607" - integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA== - -vscode-textmate@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-6.0.0.tgz#a3777197235036814ac9a92451492f2748589210" - integrity sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ== - -vue-demi@*: - version "0.13.11" - resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" - integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== - -vue-loader@^17.0.0: - version "17.0.1" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.1.tgz#c0ee8875e0610a0c2d13ba9b4d50a9c8442e7a3a" - integrity sha512-/OOyugJnImKCkAKrAvdsWMuwoCqGxWT5USLsjohzWbMgOwpA5wQmzQiLMzZd7DjhIfunzAGIApTOgIylz/kwcg== +vue-loader@^17.4.2: + version "17.4.2" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.4.2.tgz#f87f0d8adfcbbe8623de9eba1979d41ba223c6da" + integrity sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w== dependencies: chalk "^4.1.0" hash-sum "^2.0.0" - loader-utils "^2.0.0" + watchpack "^2.4.0" -vue-router@^4.1.6: - version "4.1.6" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1" - integrity sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ== +vue-router@^4.6.0: + version "4.6.4" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.6.4.tgz#a0a9cb9ef811a106d249e4bb9313d286718020d8" + integrity sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg== dependencies: - "@vue/devtools-api" "^6.4.5" + "@vue/devtools-api" "^6.6.4" -vue@^3.2.41: - version "3.2.41" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.41.tgz#ed452b8a0f7f2b962f055c8955139c28b1c06806" - integrity sha512-uuuvnrDXEeZ9VUPljgHkqB5IaVO8SxhPpqF2eWOukVrBnRBx2THPSGQBnVRt0GrIG1gvCmFXMGbd7FqcT1ixNQ== +vue@^3.5.22, vue@^3.5.29: + version "3.5.30" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.30.tgz#66df25e9795af3e5522b36f24f3d290fde83f8a0" + integrity sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg== dependencies: - "@vue/compiler-dom" "3.2.41" - "@vue/compiler-sfc" "3.2.41" - "@vue/runtime-dom" "3.2.41" - "@vue/server-renderer" "3.2.41" - "@vue/shared" "3.2.41" + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-sfc" "3.5.30" + "@vue/runtime-dom" "3.5.30" + "@vue/server-renderer" "3.5.30" + "@vue/shared" "3.5.30" -vuepress-plugin-mermaidjs@2.0.0-beta.2: - version "2.0.0-beta.2" - resolved "https://registry.yarnpkg.com/vuepress-plugin-mermaidjs/-/vuepress-plugin-mermaidjs-2.0.0-beta.2.tgz#cd6e030efff6981cd318534fa52c64533079a666" - integrity sha512-0pDJjLFsnMuvy3wc2iEhz0OQy+tQva04ynVdhMKdH6KtetuezxtNbwazEJcRQGzDzyo2r/5rGRLYvA4MhGnj5w== +vuepress@2.0.0-rc.26: + version "2.0.0-rc.26" + resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-2.0.0-rc.26.tgz#c1eb7c2cf58f2c1c6d932fc0006c2d52c116c281" + integrity sha512-ztTS3m6Q2MAb6D26vM2UyU5nOuxIhIk37SSD3jTcKI00x4ha0FcwY3Cm0MAt6w58REBmkwNLPxN5iiulatHtbw== dependencies: - mermaid "^8.14.0" + "@vuepress/cli" "2.0.0-rc.26" + "@vuepress/client" "2.0.0-rc.26" + "@vuepress/core" "2.0.0-rc.26" + "@vuepress/markdown" "2.0.0-rc.26" + "@vuepress/shared" "2.0.0-rc.26" + "@vuepress/utils" "2.0.0-rc.26" + vue "^3.5.22" -vuepress-plugin-redirect@^2.0.0-beta.120: - version "2.0.0-beta.120" - resolved "https://registry.yarnpkg.com/vuepress-plugin-redirect/-/vuepress-plugin-redirect-2.0.0-beta.120.tgz#b1a40c227e3170a903f86ad43dfa1b7a71364677" - integrity sha512-LFOlTZMSqnxwMxF0yb0jvZsTRTCxFH1Dj6jTLcIWERYGe4+EfdLUWtfMAixVLttdu9h3Nie3flJ65wMLdsPbDw== - dependencies: - "@vuepress/cli" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - cac "^6.7.14" - vuepress-shared "2.0.0-beta.120" - -vuepress-shared@2.0.0-beta.120: - version "2.0.0-beta.120" - resolved "https://registry.yarnpkg.com/vuepress-shared/-/vuepress-shared-2.0.0-beta.120.tgz#3f9b3e0533a93c096c5ba1c2eb99f92fdf784392" - integrity sha512-DSxkmHJEnA9oqgFnqtREqO2Q5S/mLRTl2/sQT6qdS8bWAmYEJZEAKBJpt0C2VCELyRMkehfhnTUJtf1MvSF/AA== - dependencies: - "@vuepress/client" "2.0.0-beta.53" - "@vuepress/plugin-git" "2.0.0-beta.53" - "@vuepress/shared" "2.0.0-beta.53" - "@vuepress/utils" "2.0.0-beta.53" - dayjs "^1.11.6" - execa "^6.1.0" - fflate "^0.7.4" - ora "^6.1.2" - vue "^3.2.41" - vue-router "^4.1.6" - -vuepress-vite@2.0.0-beta.53: - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/vuepress-vite/-/vuepress-vite-2.0.0-beta.53.tgz#6724d5edd99df2d494a8145206192e4cc88e9b9a" - integrity sha512-kITVMM+LcV5mDQXQXAKgK0adAGMm7oyPls6HPTLM9gUvpSs2A19zfwf8zFoxIF9X+ANay4Tg87egtnJOcp8Wcg== - dependencies: - "@vuepress/bundler-vite" "2.0.0-beta.53" - "@vuepress/cli" "2.0.0-beta.53" - "@vuepress/core" "2.0.0-beta.53" - "@vuepress/theme-default" "2.0.0-beta.53" - -vuepress@^2.0.0-beta.53: - version "2.0.0-beta.53" - resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-2.0.0-beta.53.tgz#3530f36e6ef99827c8182c13db34aca4d4680231" - integrity sha512-swnH25oCHAE0ZIXBAp4gaalIsrxLLn+mguekOybwLcTNQUgbcqf8EXwVxOgN663JzPuHcxRAJg3nN/swKsFifQ== - dependencies: - vuepress-vite "2.0.0-beta.53" - -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.0, watchpack@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" + integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -4964,117 +7387,117 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== -webpack-chain@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-6.5.1.tgz#4f27284cbbb637e3c8fbdef43eef588d4d861206" - integrity sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA== - dependencies: - deepmerge "^1.5.2" - javascript-stringify "^2.0.1" - -webpack-dev-middleware@^5.3.1: - version "5.3.4" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" - integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== +webpack-dev-middleware@^7.4.2: + version "7.4.5" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz#d4e8720aa29cb03bc158084a94edb4594e3b7ac0" + integrity sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA== dependencies: colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" + memfs "^4.43.1" + mime-types "^3.0.1" + on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5" - integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== +webpack-dev-server@^5.2.2: + version "5.2.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.4.tgz#6e6306ce59848ed322c235e48b326632b1eed6d6" + integrity sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA== dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.1" + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.25" + "@types/express-serve-static-core" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" + bonjour-service "^1.2.1" + chokidar "^3.6.0" colorette "^2.0.10" - compression "^1.7.4" + compression "^1.8.1" connect-history-api-fallback "^2.0.0" - default-gateway "^6.0.3" - express "^4.17.3" + express "^4.22.1" graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" + http-proxy-middleware "^2.0.9" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + schema-utils "^4.2.0" + selfsigned "^5.5.0" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.4.2" + webpack-dev-middleware "^7.4.2" + ws "^8.18.0" -webpack-merge@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== +webpack-merge@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== dependencies: clone-deep "^4.0.1" - wildcard "^2.0.0" + flat "^5.0.2" + wildcard "^2.0.1" -webpack-sources@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" - integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" + source-list-map "^2.0.0" + source-map "~0.6.1" -webpack-sources@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" - integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +webpack-sources@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.4.tgz#a338b95eb484ecc75fbb196cbe8a2890618b4891" + integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q== -webpack@^5.74.0: - version "5.76.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.1.tgz#7773de017e988bccb0f13c7d75ec245f377d295c" - integrity sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ== +webpack-v5-chain@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-v5-chain/-/webpack-v5-chain-1.1.0.tgz#b2c1a407f2c1adf3eb964a18551efafa3a28bab4" + integrity sha512-GX6NmPpCPoKgjHxAzAhPOzDSMfdX3JzGRcppeYPSmwLmPjiqUDxGZ3rt8h4qsNsZ299rMUMIrHTk5QrIGjwW2g== dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.7.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" + deepmerge "^4.3.1" + javascript-stringify "^2.1.0" + +webpack@^5.102.1: + version "5.105.4" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.105.4.tgz#1b77fcd55a985ac7ca9de80a746caffa38220169" + integrity sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.16.0" + acorn-import-phases "^1.0.3" + browserslist "^4.28.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" - es-module-lexer "^0.9.0" + enhanced-resolve "^5.20.0" + es-module-lexer "^2.0.0" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" + loader-runner "^4.3.1" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.4.0" - webpack-sources "^3.2.3" + schema-utils "^4.3.3" + tapable "^2.3.0" + terser-webpack-plugin "^5.3.17" + watchpack "^2.5.1" + webpack-sources "^3.3.4" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" @@ -5090,39 +7513,41 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== dependencies: - isexe "^2.0.0" + iconv-lite "0.6.3" -wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@^8.4.2: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== - -yallist@^4.0.0: +whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +wildcard@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -yaml@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207" - integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg== +ws@^8.18.0: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" + integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== + +wsl-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/wsl-utils/-/wsl-utils-0.1.0.tgz#8783d4df671d4d50365be2ee4c71917a0557baab" + integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== + dependencies: + is-wsl "^3.1.0" + +yoctocolors@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.2.tgz#d795f54d173494e7d8db93150cec0ed7f678c83a" + integrity sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug== + +zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000..c69d3dd3 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,247 @@ +# MCC Version Adaptation Tools + +Scripts for analyzing Minecraft version differences and generating MCC palette files. + +Requires: Python 3.10+ + +## Local debug helpers + +The `tools/` directory also contains the shell helpers used for day-to-day MCC debugging: + +```bash +source tools/mcc-env.sh +mc-start 1.21.11 +mcc-debug -v 1.21.11 --file-input +mcc-cmd "debug state" +mcc-publish --rid linux-x64 +``` + +### Shared server, isolated MCC sessions + +- `mc-*` commands operate on the shared local Minecraft server. +- `mcc-*` commands operate on one MCC client session. +- The default `session` is the current worktree name. +- The default username is derived from `session`, so two worktrees can join the same shared server without kicking each other. +- `MCC_SERVERS` remains the shared server-root override. + +Keep shared servers running by default. Do not stop or reset them unless the user explicitly asks for that, or you need to switch server versions. + +### Full inventory regression sweep + +Use `tools/run-inventory-full-sweep.sh` when changing inventory, container, item-slot serialization, packet palettes, game-mode handling, or block-use behavior. It runs MCC against real local servers with temporary configs and checks player inventory, creative give/delete, search, click, drop, chest container, mirrored player slots, and crash markers. + +```bash +# Focused retest +tools/run-inventory-full-sweep.sh --versions "1.21.10 1.21.11" + +# Full default major-version sweep +tools/run-inventory-full-sweep.sh +``` + +Useful environment overrides: + +```bash +RUN_ROOT=/tmp/my-inventory-run \ +MCC_SERVERS=/path/to/servers \ +STOP_ON_FAIL=1 \ +tools/run-inventory-full-sweep.sh --versions "1.19 1.20.4" +``` + +The script writes `summary.tsv` under `RUN_ROOT`. Per-version MCC logs are under `/tmp/mcc-debug/inventory-full-<version>/mcc-debug.log`, and command output blocks are saved next to the summary. + +### tmpfs build mode + +```bash +source tools/mcc-env.sh +export MCC_BUILD_MODE=tmpfs +mcc-build +mcc-build-clean +``` + +When `MCC_BUILD_MODE=tmpfs`, build output goes to `/dev/shm/mcc-build/<worktree>/` on Linux, or `${TMPDIR:-/tmp}/mcc-build/<worktree>/` when `/dev/shm` is unavailable. + +## Data Sources + +Two types of data can be used as input: + +| Source | How to Get | Authoritative? | +|--------|-----------|---------------| +| Decompiled Java source | `MinecraftDecompiler.jar` → `MinecraftOfficial/<ver>-decompiled/` | Mostly (see caveat below) | +| Server data reports | `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports` | **Yes** | + +**Important since MC 1.21.9**: Some items and blocks are registered outside `Items.java`/`Blocks.java` field declarations (via block registration callbacks). In these cases, the decompiled source undercounts entries. **Always use server data reports** for item and block palettes when available. + +### Decompiling a new MC version + +```bash +# Server side (default) — also downloads server.jar into MinecraftOfficial/downloads/<ver>/ +tools/decompile.sh --version 1.21.9 + +# Client side +tools/decompile.sh --version 1.21.9 --side CLIENT +``` + +If you keep server assets outside the repo, set `MCC_SERVERS=/path/to/servers` before using `tools/mcc-env.sh` or `tools/start-server.sh`. + +The script auto-downloads `MinecraftDecompiler.jar` from GitHub releases if it doesn't exist. + +### Generating server data reports + +```bash +cd /tmp +java -DbundlerMainClass=net.minecraft.data.Main \ + -jar $MCC_SERVERS/<version>/server.jar \ + --reports --output /tmp/mc_reports +``` + +This generates: +- `/tmp/mc_reports/reports/registries.json` — all registries with protocol IDs +- `/tmp/mc_reports/reports/blocks.json` — all blocks with block state IDs +- `/tmp/mc_reports/reports/packets.json` — packet protocol definitions + +## diff_registries.py — Compare registries between versions + +Compares Items, EntityTypes, Blocks, DataComponents, and EntityDataSerializers between two MC versions. Reports whether each palette needs updating, lists added/removed entries, and shows ID shift statistics. + +```bash +# Basic comparison (decompiled source only) +python3 tools/diff_registries.py 1.21.8 1.21.9 + +# With cross-validation against server registries.json (recommended) +python3 tools/diff_registries.py 1.21.8 1.21.9 --registry /tmp/mc_reports/reports/registries.json +``` + +The `--registry` flag enables cross-validation: compares the count and set of entries found in decompiled Java source against the server's authoritative registry. Any mismatches indicate that palette generation must use server data instead of Java source. + +Output indicates for each registry: +- **IDENTICAL** → reuse existing palette +- **PALETTE UPDATE NEEDED** → create new palette file + update version routing +- **Count MISMATCH** (with --registry) → server has entries not in Java source + +## gen_item_palette.py — Generate ItemPalette C# file + +Two modes: + +```bash +# Preferred: from server registries.json (accurate since 1.21.9) +python3 tools/gen_item_palette.py --from-registry /tmp/mc_reports/reports/registries.json 1219 + +# Legacy: from decompiled Items.java +python3 tools/gen_item_palette.py 1.21.1 121 +``` + +Output: `MinecraftClient/Inventory/ItemPalettes/ItemPalette<suffix>.cs` + +Validates each item name against `ItemType.cs` and warns about missing enum values. Add missing values to `ItemType.cs` in alphabetical order before compiling. + +## gen_block_palette.py — Generate BlockPalette C# file + +```bash +python3 tools/gen_block_palette.py /tmp/mc_reports/reports/blocks.json 1219 +# → MinecraftClient/Mapping/BlockPalettes/Palette1219.cs +``` + +Generates a complete block palette with block state ID ranges from the server's `blocks.json`. Validates against `Material.cs` and warns about missing enum values. + +## gen_entity_palette.py — Generate EntityPalette C# file + +```bash +python3 tools/gen_entity_palette.py /tmp/mc_reports/reports/registries.json 1219 +# → MinecraftClient/Mapping/EntityPalettes/EntityPalette1219.cs +``` + +Generates entity type palette from server's `registries.json`. Validates against `EntityType.cs` and warns about missing enum values. + +## gen_entity_metadata_palette.py — Generate EntityMetadataPalette C# file + +```bash +python3 tools/gen_entity_metadata_palette.py 1.21.9 1219 +# → MinecraftClient/Mapping/EntityMetadataPalettes/EntityMetadataPalette1219.cs +``` + +Reads `EntityDataSerializers.java` static block registration order. Maps Java field names to MCC's `EntityMetaDataType` enum. If a new serializer type appears that isn't in the mapping table, it will warn you to update: +1. The script's `FIELD_TO_ENUM` dict +2. MCC's `EntityMetaDataType.cs` enum +3. `DataTypes.cs` ReadNextMetadata() read logic + +## gen_command_argument_registry.py — Generate DeclareCommands registry arrays + +```bash +python3 tools/gen_command_argument_registry.py 1.20.6 1.21.5 1.21.6 +``` + +Reads `ArgumentTypeInfos.java`, skips the `SharedConstants.IS_RUNNING_IN_IDE` block, and prints C# array initializers for the runtime `COMMAND_ARGUMENT_TYPE` registry order. Use this when Mojang inserts new command argument types and the modern `DeclareCommands` parser needs updated ID routing. + +## gen_block_shapes.py — Download & compact block collision shapes + +Downloads block collision shapes from PrismarineJS `minecraft-data` and compacts them into a single JSON for MCC's physics engine. + +```bash +# Auto-download for a specific MC version +python3 tools/gen_block_shapes.py 26.1 +# → MinecraftClient/Physics/BlockShapeData.json + +# From a local file (if network is slow) +python3 tools/gen_block_shapes.py --from-file /path/to/blockCollisionShapes.json +``` + +Output: `MinecraftClient/Physics/BlockShapeData.json` (embedded as a resource via `.csproj`). + +Data source: `https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/pc/<version>/blockCollisionShapes.json` + +Uses `curl` with resume (`-C -`) for reliable download over slow connections. Falls back to manual download if retries are exhausted. + +## gen_block_color_map.py -- Generate minimap block color JSON + +Extracts block-to-MapColor RGB mappings from decompiled Minecraft source for the TUI minimap. + +```bash +python3 tools/gen_block_color_map.py MinecraftOfficial/26.1-rc-2-decompiled +# -> MinecraftClient/Tui/MinimapBlockColors.json +``` + +Parses three files from the decompiled source: +- `MapColor.java` -- extracts the 64 base MapColor constants and their RGB values +- `DyeColor.java` -- maps dye colors to MapColor constants +- `Blocks.java` -- determines each block's assigned MapColor via `.mapColor()` calls + +Output: `MinecraftClient/Tui/MinimapBlockColors.json` (embedded as a resource via `.csproj`). Contains color entries, plus lists of transparent, water, and ice materials. + +Validates each block name against MCC's `Material.cs` enum. Blocks without a matching enum value are skipped. + +## gen_entity_category_map.py -- Generate minimap entity category JSON + +Extracts entity-to-MobCategory mappings from decompiled Minecraft source for the TUI minimap. + +```bash +python3 tools/gen_entity_category_map.py MinecraftOfficial/26.1-rc-2-decompiled +# -> MinecraftClient/Tui/MinimapEntityCategories.json +``` + +Parses `EntityType.java` to read each entity's `MobCategory` assignment from the `EntityType.Builder.of(Factory, MobCategory.XXX)` call. Maps Minecraft categories to MCC minimap categories: +- `MONSTER` -> hostile +- `CREATURE`/`AMBIENT`/`AXOLOTLS`/`WATER_*` -> passive +- `MISC` -> non_living + +The script maintains manual override lists for: +- **Neutral mobs** (e.g. Enderman, Spider, Wolf, Bee) -- Minecraft has no "neutral" category; these are MONSTER or CREATURE in code but only attack when provoked +- **Passive overrides** (e.g. Villager, WanderingTrader) -- classified as MISC in Minecraft for spawning reasons but should appear as passive on the minimap + +Output: `MinecraftClient/Tui/MinimapEntityCategories.json` (embedded as a resource via `.csproj`). Validates each entity name against MCC's `EntityType.cs` enum. + +## Recommended workflow + +1. Generate server reports (Step 0) +2. Run `diff_registries.py --registry` to identify changes and validate source completeness +3. For each registry needing update: + - Items: `gen_item_palette.py --from-registry` + - Blocks: `gen_block_palette.py` + - Entities: `gen_entity_palette.py` + - Metadata: `gen_entity_metadata_palette.py` +4. Update block collision shapes: `gen_block_shapes.py` +5. Update minimap data (if blocks or entities changed): + - Block colors: `gen_block_color_map.py` + - Entity categories: `gen_entity_category_map.py` +6. Add any missing enum values to `ItemType.cs`, `Material.cs`, `EntityType.cs`, `EntityMetaDataType.cs` +7. Update version routing (see SKILL.md) +8. Build and test diff --git a/tools/decompile.sh b/tools/decompile.sh new file mode 100644 index 00000000..791431ba --- /dev/null +++ b/tools/decompile.sh @@ -0,0 +1,199 @@ +#!/bin/bash +# Download (if needed) and run MinecraftDecompiler to produce decompiled source +# and server.jar for a given Minecraft version. +# +# Usage: +# ./tools/decompile.sh --version <ver> [--side SERVER|CLIENT] +# +# Examples: +# ./tools/decompile.sh --version 1.21.11 +# ./tools/decompile.sh --version 1.21.11 --side CLIENT + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +MC_OFFICIAL="$REPO_ROOT/MinecraftOfficial" +DECOMPILER_JAR="$MC_OFFICIAL/MinecraftDecompiler.jar" +DECOMPILER_REPO="MaxPixelStudios/MinecraftDecompiler" + +VERSION="" +SIDE="SERVER" + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) VERSION="$2"; shift 2 ;; + --side) SIDE="$(echo "$2" | tr '[:lower:]' '[:upper:]')"; shift 2 ;; + -h|--help) + echo "Usage: $0 --version <ver> [--side SERVER|CLIENT]" + echo "" + echo "Options:" + echo " --version <ver> Minecraft version (e.g. 1.21.11)" + echo " --side <env> SERVER (default) or CLIENT" + exit 0 + ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +if [[ -z "$VERSION" ]]; then + echo "Error: --version is required" + echo "Usage: $0 --version <ver> [--side SERVER|CLIENT]" + exit 1 +fi + +if [[ "$SIDE" != "SERVER" && "$SIDE" != "CLIENT" ]]; then + echo "Error: --side must be SERVER or CLIENT (got: $SIDE)" + exit 1 +fi + +# --- Ensure MinecraftDecompiler.jar exists --- +if [[ ! -f "$DECOMPILER_JAR" ]]; then + echo "MinecraftDecompiler.jar not found, downloading latest release..." + DOWNLOAD_URL=$(curl -sL "https://api.github.com/repos/$DECOMPILER_REPO/releases/latest" \ + | python3 -c " +import json, sys +data = json.load(sys.stdin) +for a in data['assets']: + if a['name'] == 'MinecraftDecompiler.jar': + print(a['browser_download_url']) + break +") + if [[ -z "$DOWNLOAD_URL" ]]; then + echo "Error: could not find MinecraftDecompiler.jar in latest release" + exit 1 + fi + echo "Downloading from $DOWNLOAD_URL ..." + curl -L -o "$DECOMPILER_JAR" "$DOWNLOAD_URL" + echo "Downloaded MinecraftDecompiler.jar" +fi + +# --- Build output paths --- +SIDE_LOWER="$(echo "$SIDE" | tr '[:upper:]' '[:lower:]')" + +if [[ "$SIDE" == "SERVER" ]]; then + REMAPPED_JAR="$MC_OFFICIAL/remapped_jar/${VERSION}-remapped.jar" + DECOMPILED_DIR="$MC_OFFICIAL/${VERSION}-decompiled" +else + REMAPPED_JAR="$MC_OFFICIAL/remapped_jar/${VERSION}-${SIDE_LOWER}-remapped.jar" + DECOMPILED_DIR="$MC_OFFICIAL/${VERSION}-${SIDE_LOWER}-decompiled" +fi + +if [[ -d "$DECOMPILED_DIR" ]]; then + echo "Decompiled source already exists: $DECOMPILED_DIR" + echo "Delete it first if you want to re-decompile." + exit 0 +fi + +mkdir -p "$MC_OFFICIAL/remapped_jar" + +# --- Resolve version metadata from Mojang manifest --- +MANIFEST_URL="https://launchermeta.mojang.com/mc/game/version_manifest_v2.json" +VERSION_URL=$(curl -sL "$MANIFEST_URL" | python3 -c " +import json, sys +data = json.load(sys.stdin) +for v in data['versions']: + if v['id'] == '$VERSION': + print(v['url']) + break +") +if [[ -z "$VERSION_URL" ]]; then + echo "Error: version $VERSION not found in Mojang launcher manifest." + exit 1 +fi + +VERSION_META=$(curl -sL "$VERSION_URL") +MAPPING_KEY="${SIDE_LOWER}_mappings" +HAS_MAPPINGS=$(echo "$VERSION_META" | python3 -c " +import json, sys +data = json.load(sys.stdin) +print('true' if '$MAPPING_KEY' in data.get('downloads', {}) else 'false') +") + +echo "=== Decompiling Minecraft $VERSION ($SIDE) ===" +echo " Remapped JAR: $REMAPPED_JAR" +echo " Decompiled: $DECOMPILED_DIR" +echo " Obfuscated: $HAS_MAPPINGS" +echo "" + +cd "$MC_OFFICIAL" + +if [[ "$HAS_MAPPINGS" == "true" ]]; then + # Obfuscated version: use --version/--side to auto-download jar + mappings + deobfuscate + java -jar "$DECOMPILER_JAR" \ + --version "$VERSION" \ + --side "$SIDE" \ + --decompile \ + --output "$REMAPPED_JAR" \ + --decompiled-output "$DECOMPILED_DIR" +else + # Unobfuscated version (26.1+): download jar, extract inner jar from bundle, decompile directly. + # MinecraftDecompiler requires --mapping-path with --input, but unobfuscated versions + # have no mappings. We use Vineflower directly instead. + echo "No Proguard mappings for $VERSION; decompiling without deobfuscation." + + JAR_URL=$(echo "$VERSION_META" | python3 -c " +import json, sys +data = json.load(sys.stdin) +print(data['downloads']['${SIDE_LOWER}']['url']) +") + ORIGINAL_JAR="$MC_OFFICIAL/remapped_jar/${VERSION}-${SIDE_LOWER}-original.jar" + if [[ ! -f "$ORIGINAL_JAR" ]]; then + echo "Downloading ${SIDE_LOWER}.jar ..." + curl -L -o "$ORIGINAL_JAR" "$JAR_URL" + fi + + # Since 1.18, server.jar is a bundled jar containing the actual game jar inside + # META-INF/versions/<ver>/server-<ver>.jar. Extract it if present. + DECOMPILE_TARGET="$ORIGINAL_JAR" + EXTRACT_DIR=$(mktemp -d) + trap "rm -rf '$EXTRACT_DIR'" EXIT + if unzip -q -o "$ORIGINAL_JAR" "META-INF/versions.list" -d "$EXTRACT_DIR" 2>/dev/null; then + INNER_PATH=$(awk '{print $NF}' "$EXTRACT_DIR/META-INF/versions.list" | head -1) + if [[ -n "$INNER_PATH" ]]; then + unzip -q -o "$ORIGINAL_JAR" "META-INF/versions/$INNER_PATH" -d "$EXTRACT_DIR" + DECOMPILE_TARGET="$EXTRACT_DIR/META-INF/versions/$INNER_PATH" + echo "Extracted inner jar: $INNER_PATH" + fi + fi + + # Use Vineflower directly (bundled with MinecraftDecompiler, or standalone) + VINEFLOWER_JAR="$MC_OFFICIAL/downloads/decompiler/vineflower.jar" + if [[ ! -f "$VINEFLOWER_JAR" ]]; then + # Fall back to vineflower bundled inside MinecraftDecompiler's cache + VINEFLOWER_JAR=$(find "$MC_OFFICIAL" -name "vineflower*.jar" -not -name "MinecraftDecompiler.jar" 2>/dev/null | head -1) + fi + if [[ -z "$VINEFLOWER_JAR" || ! -f "$VINEFLOWER_JAR" ]]; then + echo "Error: vineflower.jar not found. Place it at $MC_OFFICIAL/downloads/decompiler/vineflower.jar" + exit 1 + fi + + echo "Decompiling with Vineflower: $VINEFLOWER_JAR" + java -jar "$VINEFLOWER_JAR" "$DECOMPILE_TARGET" "$DECOMPILED_DIR" +fi + +echo "" +echo "=== Done ===" +echo "Decompiled source: $DECOMPILED_DIR" + +# --- For SERVER side, also ensure downloads/<ver>/server.jar exists --- +if [[ "$SIDE" == "SERVER" ]]; then + DOWNLOADS_DIR="$MC_OFFICIAL/downloads/$VERSION" + if [[ ! -f "$DOWNLOADS_DIR/server.jar" ]]; then + mkdir -p "$DOWNLOADS_DIR" + echo "" + echo "Downloading server.jar for $VERSION into $DOWNLOADS_DIR ..." + SERVER_JAR_URL=$(echo "$VERSION_META" | python3 -c " +import json, sys +data = json.load(sys.stdin) +print(data['downloads']['server']['url']) +") + if [[ -n "$SERVER_JAR_URL" ]]; then + curl -L -o "$DOWNLOADS_DIR/server.jar" "$SERVER_JAR_URL" + echo "Downloaded server.jar" + else + echo "Warning: could not download server.jar for $VERSION." + fi + else + echo "server.jar already exists: $DOWNLOADS_DIR/server.jar" + fi +fi diff --git a/tools/diff_registries.py b/tools/diff_registries.py new file mode 100644 index 00000000..aba04dff --- /dev/null +++ b/tools/diff_registries.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +""" +Compare Minecraft registry data between two decompiled server versions. + +Compares Items, EntityTypes, Blocks, DataComponents, and EntityDataSerializers +to determine which MCC palettes need updating for a new MC version. + +Usage: + python3 tools/diff_registries.py <old_version> <new_version> [--registry <registries.json>] + +Examples: + python3 tools/diff_registries.py 1.20.6 1.21.1 + python3 tools/diff_registries.py 1.21.8 1.21.9 --registry /tmp/mc_reports/reports/registries.json + +The optional --registry flag cross-validates decompiled source counts against +the server's authoritative registries.json (generated via --reports). +Since MC 1.21.9, some items/blocks are registered outside Items.java/Blocks.java, +making this cross-validation essential for detecting hidden entries. +""" + +import json +import re +import sys +import os +from pathlib import Path + +DECOMPILED_ROOT = Path(__file__).resolve().parent.parent / "MinecraftOfficial" + + +def find_java_file(version_dir: Path, *possible_paths: str) -> Path | None: + for p in possible_paths: + full = version_dir / p + if full.exists(): + return full + return None + + +def extract_field_names(filepath: Path, pattern: str) -> list[str]: + """Extract field names from public static final declarations.""" + results = [] + with open(filepath) as f: + for line in f: + m = re.match(pattern, line) + if m: + results.append(m.group(1)) + return results + + +def extract_register_multiline(filepath: Path) -> list[str]: + """Extract register("name", ...) calls, handling multiline Java formatting.""" + with open(filepath) as f: + content = f.read() + flat = re.sub(r'\s+', ' ', content) + return re.findall(r'(?:= |return )register\(\s*"([^"]+)"', flat) + + +def extract_static_register_order(filepath: Path) -> list[str]: + """Extract registerSerializer(FIELD_NAME) calls from the static {} block.""" + results = [] + in_static = False + with open(filepath) as f: + for line in f: + if 'static {' in line: + in_static = True + continue + if in_static and 'registerSerializer(' in line: + m = re.search(r'registerSerializer\((\w+)\)', line) + if m: + results.append(m.group(1)) + if in_static and '}' in line and 'registerSerializer' not in line: + break + return results + + +def compare_lists(old: list[str], new: list[str], label: str) -> bool: + """Compare two ordered lists and report differences.""" + set_old, set_new = set(old), set(new) + added = sorted(set_new - set_old) + removed = sorted(set_old - set_new) + + print(f"\n{'='*60}") + print(f" {label}") + print(f"{'='*60}") + print(f" Old: {len(old)} entries, New: {len(new)} entries") + + if not added and not removed: + if old == new: + print(f" Result: IDENTICAL — no palette update needed") + else: + print(f" Result: Same set but DIFFERENT ORDER — palette update needed!") + for i, (a, b) in enumerate(zip(old, new)): + if a != b: + print(f" First diff at index {i}: old={a}, new={b}") + break + return False + + if added: + print(f" Added ({len(added)}): {added}") + for item in added: + idx = new.index(item) + prev_name = new[idx - 1] if idx > 0 else "(start)" + next_name = new[idx + 1] if idx < len(new) - 1 else "(end)" + print(f" \"{item}\" at index {idx}, between \"{prev_name}\" and \"{next_name}\"") + if removed: + print(f" Removed ({len(removed)}): {removed}") + + common_old = [x for x in old if x in set_new] + common_new = [x for x in new if x in set_old] + if common_old != common_new: + print(f" Common items REORDERED — palette update needed!") + else: + print(f" Common items have same relative order") + + # ID shift analysis + id_old = {name: i for i, name in enumerate(old)} + id_new = {name: i for i, name in enumerate(new)} + shifted = [(n, id_old[n], id_new[n]) for n in sorted(set_old & set_new) if id_old[n] != id_new[n]] + if shifted: + from collections import Counter + deltas = Counter(new_id - old_id for _, old_id, new_id in shifted) + print(f" {len(shifted)} entries with changed IDs, delta distribution: {sorted(deltas.items())}") + + print(f" Result: PALETTE UPDATE NEEDED") + return True + + +def cross_validate(registry_data: dict, java_entries: list[str], registry_key: str, + label: str, convert_fn=None): + """Cross-validate Java source entries against server registries.json.""" + reg = registry_data.get(registry_key, {}).get("entries", {}) + server_names = set() + for key in reg: + name = key.removeprefix("minecraft:") + server_names.add(name) + + if convert_fn: + java_names = set(convert_fn(n) for n in java_entries) + else: + java_names = set(n.lower() for n in java_entries) + + server_count = len(server_names) + java_count = len(java_names) + + print(f"\n --- Cross-validation: {label} ---") + print(f" Java source: {java_count} entries, Server registry: {server_count} entries") + + if server_count == java_count: + print(f" ✓ Counts match — Java source is complete") + else: + diff = server_count - java_count + print(f" ⚠ Count MISMATCH: server has {diff:+d} entries vs Java source") + extra_in_server = server_names - java_names + extra_in_java = java_names - server_names + if extra_in_server: + print(f" In server but NOT in Java source ({len(extra_in_server)}):") + for n in sorted(extra_in_server): + pid = reg[f"minecraft:{n}"]["protocol_id"] + print(f" [{pid}] {n}") + print(f" ⚠ MUST use --from-registry / server data to generate palette!") + if extra_in_java: + print(f" In Java source but NOT in server ({len(extra_in_java)}):") + for n in sorted(extra_in_java): + print(f" {n}") + + +def diff_items(old_dir: Path, new_dir: Path, registry_data: dict | None = None): + old_f = find_java_file(old_dir, "net/minecraft/world/item/Items.java") + new_f = find_java_file(new_dir, "net/minecraft/world/item/Items.java") + if not old_f or not new_f: + print(" [SKIP] Items.java not found") + return + pattern = r'\s+public static final Item (\w+)\s*=' + old = extract_field_names(old_f, pattern) + new = extract_field_names(new_f, pattern) + compare_lists(old, new, "Items.java (Item registry)") + + if registry_data: + cross_validate(registry_data, new, "minecraft:item", "Items", + convert_fn=lambda n: n.lower()) + + +def diff_entity_types(old_dir: Path, new_dir: Path, registry_data: dict | None = None): + old_f = find_java_file(old_dir, "net/minecraft/world/entity/EntityType.java") + new_f = find_java_file(new_dir, "net/minecraft/world/entity/EntityType.java") + if not old_f or not new_f: + print(" [SKIP] EntityType.java not found") + return + old = extract_register_multiline(old_f) + new = extract_register_multiline(new_f) + compare_lists(old, new, "EntityType.java (Entity registry)") + + if registry_data: + cross_validate(registry_data, new, "minecraft:entity_type", "EntityType", + convert_fn=lambda n: n) + + +def diff_blocks(old_dir: Path, new_dir: Path, registry_data: dict | None = None): + old_f = find_java_file(old_dir, "net/minecraft/world/level/block/Blocks.java") + new_f = find_java_file(new_dir, "net/minecraft/world/level/block/Blocks.java") + if not old_f or not new_f: + print(" [SKIP] Blocks.java not found") + return + old = extract_register_multiline(old_f) + new = extract_register_multiline(new_f) + compare_lists(old, new, "Blocks.java (Block registry)") + + if registry_data: + cross_validate(registry_data, new, "minecraft:block", "Blocks", + convert_fn=lambda n: n) + + +def diff_data_components(old_dir: Path, new_dir: Path): + old_f = find_java_file(old_dir, "net/minecraft/core/component/DataComponents.java") + new_f = find_java_file(new_dir, "net/minecraft/core/component/DataComponents.java") + if not old_f or not new_f: + print(" [SKIP] DataComponents.java not found") + return + old = extract_register_multiline(old_f) + new = extract_register_multiline(new_f) + needs_update = compare_lists(old, new, "DataComponents.java (StructuredComponents registry)") + if needs_update or True: + print("\n Registration order (new version):") + for i, name in enumerate(new): + marker = " <-- NEW" if name not in set(old) else "" + print(f" {i}: {name}{marker}") + + +def diff_entity_data_serializers(old_dir: Path, new_dir: Path): + old_f = find_java_file(old_dir, "net/minecraft/network/syncher/EntityDataSerializers.java") + new_f = find_java_file(new_dir, "net/minecraft/network/syncher/EntityDataSerializers.java") + if not old_f or not new_f: + print(" [SKIP] EntityDataSerializers.java not found") + return + old = extract_static_register_order(old_f) + new = extract_static_register_order(new_f) + needs_update = compare_lists(old, new, "EntityDataSerializers.java (EntityMetadata palette)") + print("\n Registration order (new version):") + for i, name in enumerate(new): + marker = " <-- NEW" if name not in set(old) else "" + print(f" {i}: {name}{marker}") + + +def main(): + # Parse arguments + args = sys.argv[1:] + registry_path = None + + if "--registry" in args: + idx = args.index("--registry") + if idx + 1 >= len(args): + print("Error: --registry requires a path argument") + sys.exit(1) + registry_path = Path(args[idx + 1]) + args = args[:idx] + args[idx + 2:] + + if len(args) != 2: + print(__doc__) + sys.exit(1) + + old_ver, new_ver = args[0], args[1] + old_dir = DECOMPILED_ROOT / f"{old_ver}-decompiled" + new_dir = DECOMPILED_ROOT / f"{new_ver}-decompiled" + + for d, v in [(old_dir, old_ver), (new_dir, new_ver)]: + if not d.exists(): + print(f"Error: {d} not found. Decompile {v} first:") + print(f" cd MinecraftOfficial && java -jar MinecraftDecompiler.jar " + f"--version {v} --side SERVER --decompile " + f"--output {v}-remapped.jar --decompiled-output {v}-decompiled") + sys.exit(1) + + registry_data = None + if registry_path: + if not registry_path.exists(): + print(f"Error: {registry_path} not found") + sys.exit(1) + with open(registry_path) as f: + registry_data = json.load(f) + print(f"Loaded server registries.json for cross-validation") + + print(f"Comparing MC {old_ver} → {new_ver}") + print(f"Old: {old_dir}") + print(f"New: {new_dir}") + + diff_items(old_dir, new_dir, registry_data) + diff_entity_types(old_dir, new_dir, registry_data) + diff_blocks(old_dir, new_dir, registry_data) + diff_data_components(old_dir, new_dir) + diff_entity_data_serializers(old_dir, new_dir) + + print(f"\n{'='*60}") + print(" Summary") + print(f"{'='*60}") + print(" Review each section above. For any marked 'PALETTE UPDATE NEEDED',") + print(" create a new palette file in MCC and update the version routing.") + print(" For 'IDENTICAL' sections, the existing palette can be reused.") + if registry_data: + print("\n Cross-validation was performed against server registries.json.") + print(" If any count mismatches were found, use server data generator output") + print(" (--from-registry) instead of decompiled Java source for palette generation.") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_block_color_map.py b/tools/gen_block_color_map.py new file mode 100644 index 00000000..8bea845f --- /dev/null +++ b/tools/gen_block_color_map.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +""" +Generate MinimapBlockColors.json from decompiled Minecraft source. + +Parses MapColor.java for the 62 base map colors (ID -> RGB), then parses +Blocks.java to extract each block's mapColor assignment, and outputs a +JSON mapping from MCC Material enum names (PascalCase) to RGB triples. + +Usage: + python3 tools/gen_block_color_map.py <decompiled_root> + +Example: + python3 tools/gen_block_color_map.py MinecraftOfficial/26.1-rc-2-decompiled +""" + +import json +import re +import sys +from pathlib import Path + +OUTPUT_PATH = (Path(__file__).resolve().parent.parent + / "MinecraftClient" / "Tui" / "MinimapBlockColors.json") +MATERIAL_CS = (Path(__file__).resolve().parent.parent + / "MinecraftClient" / "Mapping" / "Material.cs") + + +def mc_name_to_csharp(mc_name: str) -> str: + name = mc_name.removeprefix("minecraft:") + return "".join(word.capitalize() for word in name.split("_")) + + +def parse_map_colors(map_color_java: Path) -> dict[str, tuple[int, int, int]]: + """Parse MapColor.java: extract name -> (R, G, B) for each constant.""" + text = map_color_java.read_text() + colors: dict[str, tuple[int, int, int]] = {} + + pattern = re.compile( + r'public static final MapColor\s+(\w+)\s*=\s*new\s+MapColor\(\s*(\d+)\s*,\s*(\d+)\s*\)') + for m in pattern.finditer(text): + name = m.group(1) + color_int = int(m.group(3)) + r = (color_int >> 16) & 0xFF + g = (color_int >> 8) & 0xFF + b = color_int & 0xFF + colors[name] = (r, g, b) + + return colors + + +def parse_dye_to_map_color(dye_color_java: Path) -> dict[str, str]: + """Parse DyeColor.java: extract DyeColor name -> MapColor name.""" + text = dye_color_java.read_text() + mapping: dict[str, str] = {} + + pattern = re.compile( + r'(\w+)\(\d+,\s*"[^"]+",\s*\d+,\s*MapColor\.(\w+)') + for m in pattern.finditer(text): + mapping[m.group(1)] = m.group(2) + + return mapping + + +def extract_block_declarations(text: str) -> list[tuple[str, str, str]]: + """Extract (field_name, block_id, full_register_body) for each block declaration. + + Returns list of (FIELD_NAME, "block_name", "register(...) content"). + """ + results = [] + + # Find all "public static final Block FIELD = register(...)" declarations. + # These span multiple lines and end with ");". + # Strategy: find start pattern, then track parens to find matching end. + field_pattern = re.compile( + r'public\s+static\s+final\s+Block\s+(\w+)\s*=\s*register\s*\(') + + pos = 0 + while pos < len(text): + m = field_pattern.search(text, pos) + if not m: + break + + field_name = m.group(1) + paren_start = m.end() - 1 # position of opening '(' + + # Find matching closing ')' then ';' + depth = 1 + i = paren_start + 1 + while i < len(text) and depth > 0: + if text[i] == '(': + depth += 1 + elif text[i] == ')': + depth -= 1 + i += 1 + + register_body = text[paren_start:i] + + # Extract block name string from register call + name_match = re.search(r'(?:BlockIds\.(\w+)|"(\w+)")', register_body) + if name_match: + raw_id = name_match.group(1) or name_match.group(2) + block_id = raw_id.lower() if raw_id.isupper() else raw_id + else: + block_id = field_name.lower() + + results.append((field_name, block_id, register_body)) + pos = i + + return results + + +def parse_blocks(blocks_java: Path, map_colors: dict[str, tuple[int, int, int]], + dye_to_map: dict[str, str]) -> dict[str, tuple[int, int, int]]: + """Parse Blocks.java: extract block_name -> (R, G, B).""" + text = blocks_java.read_text() + + declarations = extract_block_declarations(text) + print(f" Found {len(declarations)} block register() declarations") + + # First pass: assign MapColor name to each block + field_to_block_id: dict[str, str] = {} + block_color_name: dict[str, str] = {} + + map_color_direct = re.compile(r'\.mapColor\(MapColor\.(\w+)\)') + map_color_dye = re.compile(r'\.mapColor\(DyeColor\.(\w+)\)') + map_color_ref = re.compile(r'\.mapColor\((\w+)\.defaultMapColor\(\)') + map_color_waterlogged = re.compile(r'\.mapColor\(waterloggedMapColor\(MapColor\.(\w+)\)') + + for field_name, block_id, body in declarations: + field_to_block_id[field_name] = block_id + + mc = map_color_direct.search(body) + if mc: + block_color_name[block_id] = mc.group(1) + continue + + mc = map_color_dye.search(body) + if mc: + dye_name = mc.group(1) + if dye_name in dye_to_map: + block_color_name[block_id] = dye_to_map[dye_name] + continue + + mc = map_color_waterlogged.search(body) + if mc: + block_color_name[block_id] = mc.group(1) + continue + + mc = map_color_ref.search(body) + if mc: + ref_field = mc.group(1) + ref_block = field_to_block_id.get(ref_field) + if ref_block and ref_block in block_color_name: + block_color_name[block_id] = block_color_name[ref_block] + + # Second pass: resolve remaining BLOCK.defaultMapColor() references + for field_name, block_id, body in declarations: + if block_id in block_color_name: + continue + mc = map_color_ref.search(body) + if mc: + ref_field = mc.group(1) + ref_block = field_to_block_id.get(ref_field) + if ref_block and ref_block in block_color_name: + block_color_name[block_id] = block_color_name[ref_block] + + result: dict[str, tuple[int, int, int]] = {} + for block_id, color_name in block_color_name.items(): + if color_name in map_colors: + cs_name = mc_name_to_csharp(block_id) + result[cs_name] = map_colors[color_name] + + return result + + +def load_known_materials() -> set[str]: + known = set() + if MATERIAL_CS.exists(): + with open(MATERIAL_CS) as f: + for line in f: + m = re.match(r'\s+(\w+),?\s*$', line) + if m: + known.add(m.group(1)) + return known + + +TRANSPARENT_BLOCKS = [ + "Air", "CaveAir", "VoidAir", + "Glass", "GlassPane", + "WhiteStainedGlass", "OrangeStainedGlass", "MagentaStainedGlass", + "LightBlueStainedGlass", "YellowStainedGlass", "LimeStainedGlass", + "PinkStainedGlass", "GrayStainedGlass", "LightGrayStainedGlass", + "CyanStainedGlass", "PurpleStainedGlass", "BlueStainedGlass", + "BrownStainedGlass", "GreenStainedGlass", "RedStainedGlass", + "BlackStainedGlass", + "WhiteStainedGlassPane", "OrangeStainedGlassPane", "MagentaStainedGlassPane", + "LightBlueStainedGlassPane", "YellowStainedGlassPane", "LimeStainedGlassPane", + "PinkStainedGlassPane", "GrayStainedGlassPane", "LightGrayStainedGlassPane", + "CyanStainedGlassPane", "PurpleStainedGlassPane", "BlueStainedGlassPane", + "BrownStainedGlassPane", "GreenStainedGlassPane", "RedStainedGlassPane", + "BlackStainedGlassPane", + "TintedGlass", "Barrier", "Light", "StructureVoid", +] + +WATER_BLOCKS = ["Water"] +ICE_BLOCKS = ["Ice", "PackedIce", "BlueIce", "FrostedIce"] + + +def build_map_palette(map_color_java: Path) -> dict[str, list[int]]: + """Build MapColor ID -> [R, G, B] palette for the Map bot (map packet rendering). + + Returns a dict keyed by string IDs ("0", "1", ...) to keep JSON simple. + """ + text = map_color_java.read_text() + palette: dict[str, list[int]] = {} + + pattern = re.compile( + r'new\s+MapColor\(\s*(\d+)\s*,\s*(\d+)\s*\)') + for m in pattern.finditer(text): + cid = int(m.group(1)) + raw = int(m.group(2)) + r = (raw >> 16) & 0xFF + g = (raw >> 8) & 0xFF + b = raw & 0xFF + palette[str(cid)] = [r, g, b] + + print(f" Built map_palette with {len(palette)} base color entries") + return dict(sorted(palette.items(), key=lambda x: int(x[0]))) + + +def main(): + if len(sys.argv) != 2: + print(__doc__) + sys.exit(1) + + root = Path(sys.argv[1]) + if not root.is_dir(): + print(f"Error: {root} is not a directory") + sys.exit(1) + + map_color_java = root / "net/minecraft/world/level/material/MapColor.java" + dye_color_java = root / "net/minecraft/world/item/DyeColor.java" + blocks_java = root / "net/minecraft/world/level/block/Blocks.java" + + for f in [map_color_java, dye_color_java, blocks_java]: + if not f.exists(): + print(f"Error: {f} not found") + sys.exit(1) + + print("Parsing MapColor.java...") + map_colors = parse_map_colors(map_color_java) + print(f" Found {len(map_colors)} map colors") + + print("Parsing DyeColor.java...") + dye_to_map = parse_dye_to_map_color(dye_color_java) + print(f" Found {len(dye_to_map)} dye->map color mappings") + + print("Parsing Blocks.java...") + block_colors = parse_blocks(blocks_java, map_colors, dye_to_map) + print(f" Extracted colors for {len(block_colors)} blocks") + + known_materials = load_known_materials() + if known_materials: + matched = {k: v for k, v in block_colors.items() if k in known_materials} + unmatched = [k for k in block_colors if k not in known_materials] + if unmatched: + print(f"\n {len(unmatched)} blocks not in Material.cs (will be skipped):") + for name in sorted(unmatched)[:20]: + print(f" {name}") + if len(unmatched) > 20: + print(f" ... and {len(unmatched) - 20} more") + block_colors = matched + print(f" {len(block_colors)} blocks matched to Material.cs entries") + + map_palette = build_map_palette(map_color_java) + + output = { + "version": root.name.replace("-decompiled", "").replace("-client", ""), + "colors": {k: list(v) for k, v in sorted(block_colors.items())}, + "transparent": sorted(TRANSPARENT_BLOCKS), + "water": WATER_BLOCKS, + "ice": ICE_BLOCKS, + "map_palette": map_palette, + } + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + with open(OUTPUT_PATH, 'w') as f: + json.dump(output, f, indent=2) + print(f"\nGenerated {OUTPUT_PATH}") + print(f" {len(block_colors)} color entries") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_block_palette.py b/tools/gen_block_palette.py new file mode 100644 index 00000000..03c48eca --- /dev/null +++ b/tools/gen_block_palette.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +Generate an MCC BlockPalette C# file from server-generated blocks.json. + +The blocks.json file is generated by running: + java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports + +Usage: + python3 tools/gen_block_palette.py <blocks.json> <suffix> + +Example: + python3 tools/gen_block_palette.py /tmp/mc_reports/reports/blocks.json 1219 + # Generates Palette1219.cs +""" + +import json +import re +import sys +from pathlib import Path + +OUTPUT_DIR = (Path(__file__).resolve().parent.parent / + "MinecraftClient" / "Mapping" / "BlockPalettes") +MATERIAL_CS = OUTPUT_DIR.parent / "Material.cs" + + +def mc_name_to_csharp(mc_name: str) -> str: + """Convert minecraft:snake_case to PascalCase C# enum name.""" + name = mc_name.removeprefix("minecraft:") + return "".join(word.capitalize() for word in name.split("_")) + + +def load_known_materials() -> set[str]: + known = set() + if MATERIAL_CS.exists(): + with open(MATERIAL_CS) as f: + for line in f: + m = re.match(r'\s+(\w+),?\s*$', line) + if m: + known.add(m.group(1)) + return known + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + sys.exit(1) + + blocks_json = Path(sys.argv[1]) + class_suffix = sys.argv[2] + + if not blocks_json.exists(): + print(f"Error: {blocks_json} not found") + sys.exit(1) + + with open(blocks_json) as f: + data = json.load(f) + + # Build (min_state, max_state, cs_name) for each block, sorted by min_state + block_ranges = [] + for block_key, block_info in data.items(): + cs_name = mc_name_to_csharp(block_key) + states = block_info.get("states", []) + state_ids = [s["id"] for s in states] + if state_ids: + block_ranges.append((min(state_ids), max(state_ids), cs_name)) + + block_ranges.sort(key=lambda x: x[0]) + print(f"Loaded {len(block_ranges)} blocks from {blocks_json}") + + max_state = max(r[1] for r in block_ranges) + print(f"State ID range: 0 - {max_state}") + + known_materials = load_known_materials() + missing = [cs for _, _, cs in block_ranges if known_materials and cs not in known_materials] + if missing: + print(f"\nWARNING: {len(missing)} blocks not found in Material.cs enum:") + for cs_name in missing: + print(f" {cs_name}") + print("\nYou need to add these to Material.cs before the palette will compile.") + print("Insert them in alphabetical order within the enum.") + + class_name = f"Palette{class_suffix}" + output_path = OUTPUT_DIR / f"{class_name}.cs" + + lines = [ + "using System.Collections.Generic;", + "", + "namespace MinecraftClient.Mapping.BlockPalettes", + "{", + f" public class {class_name} : BlockPalette", + " {", + " private static readonly Dictionary<int, Material> materials = new();", + "", + f" static {class_name}()", + " {", + ] + + for min_s, max_s, cs_name in block_ranges: + lines.append(f" for (int i = {min_s}; i <= {max_s}; i++)") + lines.append(f" materials[i] = Material.{cs_name};") + + lines += [ + " }", + "", + " protected override Dictionary<int, Material> GetDict()", + " {", + " return materials;", + " }", + " }", + "}", + "", + ] + + output_path.write_text("\n".join(lines)) + print(f"Generated {output_path} with {len(block_ranges)} blocks ({max_state + 1} total states)") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_block_shapes.py b/tools/gen_block_shapes.py new file mode 100644 index 00000000..d0d3455d --- /dev/null +++ b/tools/gen_block_shapes.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +""" +Download block collision shapes from PrismarineJS minecraft-data and compact +them into a single JSON file for embedding in MCC's physics engine. + +Usage: + python3 tools/gen_block_shapes.py <mc_version> + python3 tools/gen_block_shapes.py --from-file /path/to/blockCollisionShapes.json + # e.g. python3 tools/gen_block_shapes.py 1.21.11 + +Output: + MinecraftClient/Physics/BlockShapeData.json + +The output JSON has two top-level keys: + - "shapes": { shapeId -> [[x0,y0,z0,x1,y1,z1], ...] } + - "blocks": { blockName -> shapeId | [shapeId, ...] } +""" + +import json +import sys +import os +import subprocess +import tempfile + +REPO = "PrismarineJS/minecraft-data" +BRANCH = "master" + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +REPO_ROOT = os.path.dirname(SCRIPT_DIR) +OUTPUT_PATH = os.path.join(REPO_ROOT, "MinecraftClient", "Physics", "BlockShapeData.json") + + +def resolve_version_path(version: str) -> str: + """Resolve actual data path using PrismarineJS dataPaths.json.""" + url = f"https://raw.githubusercontent.com/{REPO}/{BRANCH}/data/dataPaths.json" + tmp = tempfile.mktemp(suffix=".json") + try: + subprocess.run( + ["curl", "-sL", "--connect-timeout", "10", "--max-time", "30", + "-o", tmp, url], + check=True, timeout=35 + ) + with open(tmp) as f: + data = json.load(f) + pc = data.get("pc", {}) + if version in pc: + entry = pc[version] + bcs_path = entry.get("blockCollisionShapes", "") + if bcs_path: + return bcs_path # e.g. "pc/1.21.11" + return f"pc/{version}" + except Exception as e: + print(f" Warning: could not resolve version path ({e}), using default") + return f"pc/{version}" + finally: + if os.path.exists(tmp): + os.remove(tmp) + + +def download_collision_shapes(version: str) -> dict: + """Download blockCollisionShapes.json with curl and resume support.""" + ver_path = resolve_version_path(version) + url = f"https://raw.githubusercontent.com/{REPO}/{BRANCH}/data/{ver_path}/blockCollisionShapes.json" + print(f"Downloading: {url}") + + tmp = tempfile.mktemp(suffix=".json") + max_retries = 5 + + for attempt in range(1, max_retries + 1): + print(f" Attempt {attempt}/{max_retries}...") + result = subprocess.run( + ["curl", "-sL", "-C", "-", + "--connect-timeout", "15", "--max-time", "180", + "--retry", "3", "--retry-delay", "2", + "-o", tmp, url], + timeout=200 + ) + + if not os.path.exists(tmp): + print(f" No file downloaded") + continue + + size = os.path.getsize(tmp) + print(f" Downloaded {size:,} bytes") + + try: + with open(tmp) as f: + data = json.load(f) + os.remove(tmp) + return data + except json.JSONDecodeError as e: + print(f" Incomplete/corrupt JSON ({e}), retrying...") + # Don't delete tmp, curl -C - will resume + + if os.path.exists(tmp): + os.remove(tmp) + print(f"ERROR: Failed to download complete file after {max_retries} attempts.") + print(f"You can manually download from: {url}") + print(f"Then run: {sys.argv[0]} --from-file /path/to/blockCollisionShapes.json") + sys.exit(1) + + +def compact(raw: dict) -> dict: + """Convert PrismarineJS format to compacted format for embedding.""" + shapes_raw = raw.get("shapes", {}) + blocks_raw = raw.get("blocks", {}) + + if not shapes_raw: + raise ValueError("Could not find 'shapes' key in input JSON") + if not blocks_raw: + raise ValueError("Could not find 'blocks' key in input JSON") + + shapes = {} + for sid, boxes in shapes_raw.items(): + compacted = [] + for box in boxes: + compacted.append([round(c, 6) for c in box]) + shapes[sid] = compacted + + blocks = {} + for name, data in blocks_raw.items(): + blocks[name] = data + + return {"shapes": shapes, "blocks": blocks} + + +def main(): + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} <mc_version>") + print(f" {sys.argv[0]} --from-file <path/to/blockCollisionShapes.json>") + print() + print(f"Example: {sys.argv[0]} 1.21.11") + sys.exit(1) + + if sys.argv[1] == "--from-file": + if len(sys.argv) < 3: + print("Error: --from-file requires a file path") + sys.exit(1) + input_path = sys.argv[2] + print(f"Reading from: {input_path}") + with open(input_path) as f: + raw = json.load(f) + else: + version = sys.argv[1] + print(f"Fetching block collision shapes for MC {version}...") + raw = download_collision_shapes(version) + + result = compact(raw) + + shape_count = len(result["shapes"]) + block_count = len(result["blocks"]) + print(f" Shapes: {shape_count}") + print(f" Blocks: {block_count}") + + with open(OUTPUT_PATH, "w") as f: + json.dump(result, f, separators=(",", ":")) + + file_size = os.path.getsize(OUTPUT_PATH) + print(f" Written to: {OUTPUT_PATH}") + print(f" File size: {file_size:,} bytes") + print() + print("Done. The file is embedded as a resource via MinecraftClient.csproj.") + print("Rebuild MCC to include updated collision data.") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_command_argument_registry.py b/tools/gen_command_argument_registry.py new file mode 100644 index 00000000..6e6b70a2 --- /dev/null +++ b/tools/gen_command_argument_registry.py @@ -0,0 +1,85 @@ + +#!/usr/bin/env python3 +""" +Generate ordered DeclareCommands argument-type arrays from decompiled ArgumentTypeInfos.java. + +The runtime server registry excludes registrations guarded by SharedConstants.IS_RUNNING_IN_IDE, +so this script skips that block before emitting the final order. +""" + +from __future__ import annotations + +import argparse +import re +from pathlib import Path + + +REGISTER_RE = re.compile(r'register\(\$\$0, "([^"]+)"') + + +def extract_runtime_argument_types(path: Path) -> list[str]: + names: list[str] = [] + skipping_ide_block = False + brace_depth = 0 + + for line in path.read_text(encoding="utf-8").splitlines(): + if "if (SharedConstants.IS_RUNNING_IN_IDE)" in line: + skipping_ide_block = True + brace_depth += line.count("{") - line.count("}") + continue + + if skipping_ide_block: + brace_depth += line.count("{") - line.count("}") + if brace_depth <= 0: + skipping_ide_block = False + brace_depth = 0 + continue + + match = REGISTER_RE.search(line) + if match: + names.append(match.group(1)) + + return names + + +def emit_csharp_array(version: str, names: list[str]) -> str: + lines = [ + f"// {version} ({len(names)})", + f"private static readonly string[] s_modernArgumentTypes{version.replace('.', '')} =", + "[", + ] + + for index, name in enumerate(names): + suffix = "," if index < len(names) - 1 else "" + lines.append(f' "{name}"{suffix}') + + lines.append("];") + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "versions", + nargs="+", + help="Minecraft version folders under MinecraftOfficial/, for example: 1.20.6 1.21.5 1.21.6", + ) + parser.add_argument( + "--repo-root", + default=Path(__file__).resolve().parents[1], + type=Path, + help="Repository root. Defaults to the current repo.", + ) + args = parser.parse_args() + + for version in args.versions: + source = args.repo_root / "MinecraftOfficial" / f"{version}-decompiled" / "net" / "minecraft" / "commands" / "synchronization" / "ArgumentTypeInfos.java" + names = extract_runtime_argument_types(source) + print(emit_csharp_array(version, names)) + print() + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/gen_entity_category_map.py b/tools/gen_entity_category_map.py new file mode 100644 index 00000000..e258d186 --- /dev/null +++ b/tools/gen_entity_category_map.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Generate MinimapEntityCategories.json from decompiled Minecraft source. + +Parses EntityType.java to extract each entity's MobCategory assignment, +then maps them to MCC minimap categories (hostile/passive/neutral/non_living). + +Minecraft's MobCategory values: + MONSTER -> hostile (with neutral overrides for conditionally hostile mobs) + CREATURE -> passive (with neutral overrides for conditionally hostile mobs) + AMBIENT -> passive + AXOLOTLS -> passive + WATER_CREATURE -> passive + WATER_AMBIENT -> passive + UNDERGROUND_WATER_CREATURE -> passive + MISC -> non_living + +Some mobs classified as MONSTER or CREATURE are actually "neutral" -- they +only attack when provoked. These are listed in NEUTRAL_OVERRIDES below and +should be updated when new conditionally-hostile mobs are added. + +Usage: + python3 tools/gen_entity_category_map.py <decompiled_root> + +Example: + python3 tools/gen_entity_category_map.py MinecraftOfficial/26.1-rc-2-decompiled +""" + +import json +import re +import sys +from pathlib import Path + +OUTPUT_PATH = (Path(__file__).resolve().parent.parent + / "MinecraftClient" / "Tui" / "MinimapEntityCategories.json") +ENTITY_TYPE_CS = (Path(__file__).resolve().parent.parent + / "MinecraftClient" / "Mapping" / "EntityType.cs") + + +def mc_name_to_csharp(mc_name: str) -> str: + name = mc_name.removeprefix("minecraft:") + return "".join(word.capitalize() for word in name.split("_")) + + +# Mobs that Minecraft classifies as MONSTER or CREATURE but behave as +# "neutral" -- they only attack when provoked. This list is maintained +# manually because there is no machine-readable flag in the game data. +NEUTRAL_OVERRIDES = { + "bee", "dolphin", "goat", "iron_golem", "llama", "panda", + "polar_bear", "snow_golem", "trader_llama", "wolf", + "zombified_piglin", "enderman", "spider", "cave_spider", + "copper_golem", +} + +# Entities whose MobCategory in the game code doesn't match how they +# should appear on the minimap. For example, Villager and WanderingTrader +# are MISC in MC code (for spawning reasons) but should be passive on the map. +# ZombieHorse is MONSTER but is a rideable passive mob in practice. +PASSIVE_OVERRIDES = { + "villager", "wandering_trader", "zombie_horse", +} + +# Player has its own category in MCC -- extracted from MISC to "player". +PLAYER_OVERRIDES = {"player"} + +MC_TO_MCC = { + "MONSTER": "hostile", + "CREATURE": "passive", + "AMBIENT": "passive", + "AXOLOTLS": "passive", + "WATER_CREATURE": "passive", + "WATER_AMBIENT": "passive", + "UNDERGROUND_WATER_CREATURE": "passive", + "MISC": "non_living", +} + + +def extract_entity_categories(entity_type_java: Path) -> list[tuple[str, str, str]]: + """Extract (entity_id, field_name, MobCategory) from EntityType.java. + + Returns list of (entity_id, FIELD_NAME, MobCategory_name). + """ + text = entity_type_java.read_text() + results = [] + + field_pat = re.compile( + r'public\s+static\s+final\s+EntityType<[^>]+>\s+(\w+)\s*=\s*register\s*\(') + + pos = 0 + while pos < len(text): + m = field_pat.search(text, pos) + if not m: + break + + field_name = m.group(1) + paren_start = m.end() - 1 + depth = 1 + i = paren_start + 1 + while i < len(text) and depth > 0: + if text[i] == '(': + depth += 1 + elif text[i] == ')': + depth -= 1 + i += 1 + + body = text[paren_start:i] + + name_match = re.search(r'"(\w+)"', body) + entity_id = name_match.group(1) if name_match else field_name.lower() + + cat_match = re.search(r'MobCategory\.(\w+)', body) + mob_cat = cat_match.group(1) if cat_match else "MISC" + + results.append((entity_id, field_name, mob_cat)) + pos = i + + return results + + +def load_known_entity_types() -> set[str]: + known = set() + if ENTITY_TYPE_CS.exists(): + with open(ENTITY_TYPE_CS) as f: + for line in f: + m = re.match(r'\s+(\w+),?\s*$', line) + if m: + known.add(m.group(1)) + return known + + +def main(): + if len(sys.argv) != 2: + print(__doc__) + sys.exit(1) + + root = Path(sys.argv[1]) + entity_type_java = root / "net/minecraft/world/entity/EntityType.java" + + if not entity_type_java.exists(): + print(f"Error: {entity_type_java} not found") + sys.exit(1) + + print("Parsing EntityType.java...") + entities = extract_entity_categories(entity_type_java) + print(f" Found {len(entities)} entity type declarations") + + known_types = load_known_entity_types() + + hostile = [] + passive = [] + neutral = [] + non_living = [] + + for entity_id, field_name, mob_cat in entities: + cs_name = mc_name_to_csharp(entity_id) + + if known_types and cs_name not in known_types: + continue + + if entity_id in PLAYER_OVERRIDES: + continue + elif entity_id in NEUTRAL_OVERRIDES: + neutral.append(cs_name) + elif entity_id in PASSIVE_OVERRIDES: + passive.append(cs_name) + elif mob_cat in MC_TO_MCC: + cat = MC_TO_MCC[mob_cat] + if cat == "hostile": + hostile.append(cs_name) + elif cat == "passive": + passive.append(cs_name) + elif cat == "non_living": + non_living.append(cs_name) + else: + non_living.append(cs_name) + else: + non_living.append(cs_name) + + output = { + "version": root.name.replace("-decompiled", "").replace("-client", ""), + "hostile": sorted(hostile), + "passive": sorted(passive), + "neutral": sorted(neutral), + "non_living": sorted(non_living), + } + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + with open(OUTPUT_PATH, 'w') as f: + json.dump(output, f, indent=2) + + print(f"\nGenerated {OUTPUT_PATH}") + print(f" hostile: {len(hostile)}") + print(f" passive: {len(passive)}") + print(f" neutral: {len(neutral)}") + print(f" non_living: {len(non_living)}") + print(f" total: {len(hostile) + len(passive) + len(neutral) + len(non_living)}") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_entity_metadata_palette.py b/tools/gen_entity_metadata_palette.py new file mode 100644 index 00000000..2aac53dd --- /dev/null +++ b/tools/gen_entity_metadata_palette.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +""" +Generate an MCC EntityMetadataPalette C# file from decompiled EntityDataSerializers.java. + +Reads the static {} block registration order to determine serializer IDs, +then maps Java field names to MCC's EntityMetaDataType enum values. + +Usage: + python3 tools/gen_entity_metadata_palette.py <mc_version> <class_suffix> + +Example: + python3 tools/gen_entity_metadata_palette.py 1.20.6 1206 + # Generates EntityMetadataPalette1206.cs +""" + +import re +import sys +from pathlib import Path + +DECOMPILED_ROOT = Path(__file__).resolve().parent.parent / "MinecraftOfficial" +OUTPUT_DIR = (Path(__file__).resolve().parent.parent / + "MinecraftClient" / "Mapping" / "EntityMetadataPalettes") + +# Java field name → MCC EntityMetaDataType enum name +FIELD_TO_ENUM = { + "BYTE": "Byte", + "INT": "VarInt", + "LONG": "VarLong", + "FLOAT": "Float", + "STRING": "String", + "COMPONENT": "Chat", + "OPTIONAL_COMPONENT": "OptionalChat", + "ITEM_STACK": "Slot", + "BOOLEAN": "Boolean", + "ROTATIONS": "Rotation", + "BLOCK_POS": "Position", + "OPTIONAL_BLOCK_POS": "OptionalPosition", + "DIRECTION": "Direction", + "OPTIONAL_UUID": "OptionalUuid", + "OPTIONAL_LIVING_ENTITY_REFERENCE": "OptionalLivingEntityReference", + "BLOCK_STATE": "BlockId", + "OPTIONAL_BLOCK_STATE": "OptionalBlockId", + "COMPOUND_TAG": "Nbt", + "PARTICLE": "Particle", + "PARTICLES": "Particles", + "VILLAGER_DATA": "VillagerData", + "OPTIONAL_UNSIGNED_INT": "OptionalVarInt", + "POSE": "Pose", + "CAT_SOUND_VARIANT": "CatSoundVariant", + "CAT_VARIANT": "CatVariant", + "CHICKEN_SOUND_VARIANT": "ChickenSoundVariant", + "CHICKEN_VARIANT": "ChickenVariant", + "COW_SOUND_VARIANT": "CowSoundVariant", + "COW_VARIANT": "CowVariant", + "FROG_VARIANT": "FrogVariant", + "PIG_SOUND_VARIANT": "PigSoundVariant", + "PIG_VARIANT": "PigVariant", + "WOLF_SOUND_VARIANT": "WolfSoundVariant", + "WOLF_VARIANT": "WolfVariant", + "OPTIONAL_GLOBAL_POS": "OptionalGlobalPosition", + "PAINTING_VARIANT": "PaintingVariant", + "SNIFFER_STATE": "SnifferState", + "ARMADILLO_STATE": "ArmadilloState", + "COPPER_GOLEM_STATE": "CopperGolemState", + "WEATHERING_COPPER_STATE": "WeatheringCopperState", + "VECTOR3": "Vector3", + "QUATERNION": "Quaternion", + "RESOLVABLE_PROFILE": "ResolvableProfile", + "ZOMBIE_NAUTILUS_VARIANT": "ZombieNautilusVariant", + "HUMANOID_ARM": "HumanoidArm", +} + + +def extract_static_register_order(filepath: Path) -> list[str]: + results = [] + in_static = False + with open(filepath) as f: + for line in f: + if 'static {' in line: + in_static = True + continue + if in_static and 'registerSerializer(' in line: + m = re.search(r'registerSerializer\((\w+)\)', line) + if m: + results.append(m.group(1)) + if in_static and '}' in line and 'registerSerializer' not in line: + break + return results + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + sys.exit(1) + + mc_version = sys.argv[1] + class_suffix = sys.argv[2] + version_dir = DECOMPILED_ROOT / f"{mc_version}-decompiled" + eds_java = version_dir / "net" / "minecraft" / "network" / "syncher" / "EntityDataSerializers.java" + + if not eds_java.exists(): + print(f"Error: {eds_java} not found") + sys.exit(1) + + fields = extract_static_register_order(eds_java) + print(f"Found {len(fields)} entity data serializers in MC {mc_version}:") + + unmapped = [] + mappings = [] + for i, field in enumerate(fields): + if field in FIELD_TO_ENUM: + enum_name = FIELD_TO_ENUM[field] + mappings.append((i, enum_name)) + print(f" {i}: {field} -> EntityMetaDataType.{enum_name}") + else: + unmapped.append((i, field)) + print(f" {i}: {field} -> ??? UNMAPPED") + + if unmapped: + print(f"\nWARNING: {len(unmapped)} unmapped fields:") + for idx, field in unmapped: + print(f" [{idx}] {field}") + print("\nAdd entries to FIELD_TO_ENUM in this script and to EntityMetaDataType.cs enum.") + + class_name = f"EntityMetadataPalette{class_suffix}" + output_path = OUTPUT_DIR / f"{class_name}.cs" + + lines = [ + "using System.Collections.Generic;", + "", + f"namespace MinecraftClient.Mapping.EntityMetadataPalettes;", + "", + f"public class {class_name} : EntityMetadataPalette", + "{", + " private readonly Dictionary<int, EntityMetaDataType> entityMetadataMappings = new()", + " {", + ] + for idx, enum_name in mappings: + lines.append(f" {{ {idx}, EntityMetaDataType.{enum_name} }},") + lines += [ + " };", + "", + " public override Dictionary<int, EntityMetaDataType> GetEntityMetadataMappingsList()", + " {", + " return entityMetadataMappings;", + " }", + "}", + "", + ] + + output_path.write_text("\n".join(lines)) + print(f"\nGenerated {output_path} with {len(mappings)} mappings") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_entity_palette.py b/tools/gen_entity_palette.py new file mode 100644 index 00000000..a932d591 --- /dev/null +++ b/tools/gen_entity_palette.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +""" +Generate an MCC EntityPalette C# file from server-generated registries.json. + +The registries.json file is generated by running: + java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports + +Usage: + python3 tools/gen_entity_palette.py <registries.json> <suffix> + +Example: + python3 tools/gen_entity_palette.py /tmp/mc_reports/reports/registries.json 1219 + # Generates EntityPalette1219.cs +""" + +import json +import re +import sys +from pathlib import Path + +OUTPUT_DIR = (Path(__file__).resolve().parent.parent / + "MinecraftClient" / "Mapping" / "EntityPalettes") +ENTITY_TYPE_CS = OUTPUT_DIR.parent / "EntityType.cs" + + +def mc_name_to_csharp(mc_name: str) -> str: + """Convert minecraft:snake_case to PascalCase C# enum name.""" + name = mc_name.removeprefix("minecraft:") + return "".join(word.capitalize() for word in name.split("_")) + + +def load_known_entity_types() -> set[str]: + known = set() + if ENTITY_TYPE_CS.exists(): + with open(ENTITY_TYPE_CS) as f: + for line in f: + m = re.match(r'\s+(\w+),?\s*$', line) + if m: + known.add(m.group(1)) + return known + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + sys.exit(1) + + registry_path = Path(sys.argv[1]) + class_suffix = sys.argv[2] + + if not registry_path.exists(): + print(f"Error: {registry_path} not found") + sys.exit(1) + + with open(registry_path) as f: + data = json.load(f) + + entities_reg = data.get("minecraft:entity_type", {}).get("entries", {}) + mappings = [] + for entity_key, info in entities_reg.items(): + pid = info["protocol_id"] + cs_name = mc_name_to_csharp(entity_key) + mappings.append((pid, cs_name)) + + mappings.sort(key=lambda x: x[0]) + print(f"Loaded {len(mappings)} entity types from {registry_path}") + + known = load_known_entity_types() + missing = [cs for _, cs in mappings if known and cs not in known] + if missing: + print(f"\nWARNING: {len(missing)} entity types not found in EntityType.cs enum:") + for cs_name in missing: + print(f" {cs_name}") + print("\nYou need to add these to EntityType.cs before the palette will compile.") + print("Insert them in alphabetical order within the enum.") + + class_name = f"EntityPalette{class_suffix}" + output_path = OUTPUT_DIR / f"{class_name}.cs" + + lines = [ + "using System.Collections.Generic;", + "", + "namespace MinecraftClient.Mapping.EntityPalettes", + "{", + f" public class {class_name} : EntityPalette", + " {", + " private static readonly Dictionary<int, EntityType> mappings = new();", + "", + f" static {class_name}()", + " {", + ] + for pid, cs_name in mappings: + lines.append(f" mappings[{pid}] = EntityType.{cs_name};") + lines += [ + " }", + "", + " protected override Dictionary<int, EntityType> GetDict()", + " {", + " return mappings;", + " }", + " }", + "}", + "", + ] + + output_path.write_text("\n".join(lines)) + print(f"Generated {output_path} with {len(mappings)} entity types") + + +if __name__ == "__main__": + main() diff --git a/tools/gen_item_palette.py b/tools/gen_item_palette.py new file mode 100644 index 00000000..5a85963b --- /dev/null +++ b/tools/gen_item_palette.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +Generate an MCC ItemPalette C# file. + +Supports two input modes: + 1. Server registry (preferred since 1.21.9): + python3 tools/gen_item_palette.py --from-registry /tmp/mc_reports/reports/registries.json <suffix> + + 2. Decompiled Items.java (legacy): + python3 tools/gen_item_palette.py <mc_version> <suffix> + +The --from-registry mode uses the server's authoritative protocol_id assignments, +which is required since MC 1.21.9 where some items are registered outside Items.java. + +The <suffix> determines the class name (ItemPalette<suffix>) and should match MCC's +naming convention (e.g., 121 for 1.21, 1219 for 1.21.9). +""" + +import json +import re +import sys +from pathlib import Path + +DECOMPILED_ROOT = Path(__file__).resolve().parent.parent / "MinecraftOfficial" +OUTPUT_DIR = Path(__file__).resolve().parent.parent / "MinecraftClient" / "Inventory" / "ItemPalettes" +ITEM_TYPE_CS = OUTPUT_DIR.parent / "ItemType.cs" + +OVERRIDES = { + "CUT_STANDSTONE_SLAB": "CutSandstoneSlab", # Mojang typo in source +} + + +def mc_name_to_csharp(mc_name: str) -> str: + """Convert minecraft:snake_case to PascalCase C# enum name.""" + name = mc_name.removeprefix("minecraft:") + if name.upper() in OVERRIDES: + return OVERRIDES[name.upper()] + return "".join(word.capitalize() for word in name.split("_")) + + +def java_to_csharp_name(java_name: str) -> str: + """Convert SCREAMING_SNAKE_CASE Java field name to PascalCase C# enum name.""" + if java_name in OVERRIDES: + return OVERRIDES[java_name] + return "".join(word.capitalize() for word in java_name.lower().split("_")) + + +def load_known_enums() -> set[str]: + known = set() + if ITEM_TYPE_CS.exists(): + with open(ITEM_TYPE_CS) as f: + for line in f: + m = re.match(r'\s+(\w+),?\s*$', line) + if m and m.group(1) not in ("Null", "Unknown"): + known.add(m.group(1)) + return known + + +def items_from_registry(registry_path: Path) -> list[tuple[int, str]]: + """Load items from server registries.json, returns sorted (protocol_id, cs_name) pairs.""" + with open(registry_path) as f: + data = json.load(f) + items_reg = data.get("minecraft:item", {}).get("entries", {}) + result = [] + for item_key, info in items_reg.items(): + pid = info["protocol_id"] + cs_name = mc_name_to_csharp(item_key) + result.append((pid, cs_name)) + result.sort(key=lambda x: x[0]) + return result + + +def items_from_java(mc_version: str) -> list[tuple[int, str]]: + """Load items from decompiled Items.java field declaration order.""" + version_dir = DECOMPILED_ROOT / f"{mc_version}-decompiled" + items_java = version_dir / "net" / "minecraft" / "world" / "item" / "Items.java" + if not items_java.exists(): + print(f"Error: {items_java} not found") + sys.exit(1) + + pattern = re.compile(r'\s+public static final Item (\w+)\s*=') + result = [] + with open(items_java) as f: + for line in f: + m = pattern.match(line) + if m: + idx = len(result) + cs_name = java_to_csharp_name(m.group(1)) + result.append((idx, cs_name)) + return result + + +def main(): + if len(sys.argv) < 3: + print(__doc__) + sys.exit(1) + + from_registry = sys.argv[1] == "--from-registry" + + if from_registry: + if len(sys.argv) != 4: + print("Usage: gen_item_palette.py --from-registry <registries.json> <suffix>") + sys.exit(1) + registry_path = Path(sys.argv[2]) + class_suffix = sys.argv[3] + if not registry_path.exists(): + print(f"Error: {registry_path} not found") + sys.exit(1) + mappings = items_from_registry(registry_path) + print(f"Loaded {len(mappings)} items from {registry_path}") + else: + mc_version = sys.argv[1] + class_suffix = sys.argv[2] + mappings = items_from_java(mc_version) + print(f"Found {len(mappings)} items in MC {mc_version} Items.java") + + known_enums = load_known_enums() + missing = [(pid, cs) for pid, cs in mappings if known_enums and cs not in known_enums] + if missing: + print(f"\nWARNING: {len(missing)} items not found in ItemType.cs enum:") + for pid, cs_name in missing: + print(f" [{pid}] {cs_name}") + print("\nYou need to add these to ItemType.cs before the palette will compile.") + print("Insert them in alphabetical order within the enum.") + + class_name = f"ItemPalette{class_suffix}" + output_path = OUTPUT_DIR / f"{class_name}.cs" + + lines = [ + "using System.Collections.Generic;", + "", + "namespace MinecraftClient.Inventory.ItemPalettes", + "{", + f" public class {class_name} : ItemPalette", + " {", + " private static readonly Dictionary<int, ItemType> mappings = new();", + "", + f" static {class_name}()", + " {", + ] + for pid, cs_name in mappings: + lines.append(f" mappings[{pid}] = ItemType.{cs_name};") + lines += [ + " }", + "", + " protected override Dictionary<int, ItemType> GetDict()", + " {", + " return mappings;", + " }", + " }", + "}", + "", + ] + + output_path.write_text("\n".join(lines)) + print(f"Generated {output_path} with {len(mappings)} mappings") + + +if __name__ == "__main__": + main() diff --git a/tools/mc-rcon.sh b/tools/mc-rcon.sh new file mode 100755 index 00000000..5017358a --- /dev/null +++ b/tools/mc-rcon.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Send an RCON command to a Minecraft server +# Usage: mc-rcon.sh <command> [port] [password] +set -euo pipefail + +CMD="${1:?Usage: mc-rcon.sh <command> [port] [password]}" +PORT="${2:-25575}" +PW="${3:-test123}" + +python3 -c " +import socket, struct, sys + +s = socket.socket() +s.settimeout(5) +try: + s.connect(('localhost', $PORT)) +except Exception as e: + print(f'Connection failed: {e}', file=sys.stderr) + sys.exit(1) + +def send(req_id, pkt_type, body): + body = body.encode() + s.send(struct.pack('<iii', 10 + len(body), req_id, pkt_type) + body + b'\x00\x00') + +def recv(): + length = struct.unpack('<i', s.recv(4))[0] + data = b'' + while len(data) < length: + data += s.recv(length - len(data)) + return data + +send(1, 3, '$PW') +r = recv() +rid = struct.unpack('<i', r[:4])[0] +if rid == -1: + print('Auth failed', file=sys.stderr) + s.close() + sys.exit(1) + +send(2, 2, \"\"\"$CMD\"\"\") +r = recv() +body = r[8:-2].decode(errors='replace') +if body: + print(body) +s.close() +" diff --git a/tools/mcc-debug.sh b/tools/mcc-debug.sh new file mode 100644 index 00000000..d0ca0e78 --- /dev/null +++ b/tools/mcc-debug.sh @@ -0,0 +1,306 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" + +usage() { + cat <<'EOF' +Usage: tools/mcc-debug.sh [options] + +One-step build, server start, and MCC launch for debugging. + +Options: + -v, --version VER Server directory name (default: 1.21.11-Vanilla) + -m, --mode MODE Console mode: classic or tui (default: classic) + -p, --port PORT Server port (default: 25565) + --session NAME Session name for scoped runtime artifacts + --username NAME MCC username (default: resolved from session) + --no-build Skip dotnet build + --debug-on Enable debug messages from the start + --file-input Use FileInput mode (classic only; enables mcc-cmd) + -h, --help Show this help + +Examples: + tools/mcc-debug.sh # Classic mode, default server + tools/mcc-debug.sh -m tui # TUI mode + tools/mcc-debug.sh -v 1.21.11-Vanilla --debug-on + tools/mcc-debug.sh --session smoke-a --username SmokeA + tools/mcc-debug.sh --file-input # FileInput for script-driven testing +EOF +} + +VERSION="1.21.11-Vanilla" +MODE="classic" +PORT="25565" +PORT_SET_BY_USER=false +SESSION="" +USERNAME="" +DO_BUILD=true +DEBUG_ON=false +FILE_INPUT=false +BUILD_ROOT="$(_mcc_build_root)" +BUILD_ROOT_ENV_PREFIX="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -v|--version) + if [[ $# -lt 2 ]]; then + echo "$1 requires a value" >&2 + exit 1 + fi + VERSION="$2" + shift 2 + ;; + -m|--mode) + if [[ $# -lt 2 ]]; then + echo "$1 requires a value" >&2 + exit 1 + fi + MODE="$2" + shift 2 + ;; + -p|--port) + if [[ $# -lt 2 ]]; then + echo "$1 requires a value" >&2 + exit 1 + fi + PORT="$2" + PORT_SET_BY_USER=true + shift 2 + ;; + --session) + if [[ $# -lt 2 ]]; then + echo "--session requires a value" >&2 + exit 1 + fi + SESSION="$2" + shift 2 + ;; + --username) + if [[ $# -lt 2 ]]; then + echo "--username requires a value" >&2 + exit 1 + fi + USERNAME="$2" + shift 2 + ;; + --no-build) DO_BUILD=false; shift ;; + --debug-on) DEBUG_ON=true; shift ;; + --file-input) FILE_INPUT=true; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; + esac +done + +SESSION="$(_mcc_resolve_session "$SESSION")" +if [[ -z "$USERNAME" ]]; then + USERNAME="$(_mcc_resolve_username "$SESSION")" +fi + +if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then + mkdir -p "$BUILD_ROOT" + printf -v BUILD_ROOT_QUOTED '%q' "$BUILD_ROOT" + BUILD_ROOT_ENV_PREFIX="MCC_BUILD_ROOT=$BUILD_ROOT_QUOTED " +fi + +SESSION_ROOT="$(_mcc_session_root "$SESSION")" +CFG="$SESSION_ROOT/MinecraftClient.debug.ini" +MCC_LOG="$(_mcc_session_log_file "$SESSION")" +INPUT_FILE="$(_mcc_session_input_file "$SESSION")" +PID_FILE="$(_mcc_session_pid_file "$SESSION")" +META_FILE="$(_mcc_session_meta_file "$SESSION")" +MCC_TMUX_SESSION="$(_mcc_tmux_session_name "$SESSION")" +SESSION_NAME="mc-${VERSION//\./_}" +PREPARE_CFG_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" +ENSURE_SERVER_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" +PREFLIGHT_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" +GET_PORT_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/get_server_port.sh" + +mkdir -p "$SESSION_ROOT" + +echo "=== MCC Debug Session ===" +echo " Server: $VERSION (port $PORT)" +echo " Session: $SESSION" +echo " User: $USERNAME" +echo " Mode: $MODE" +echo " Build: $BUILD_ROOT" +echo " Root: $SESSION_ROOT" +echo " Config: $CFG" +echo " Log: $MCC_LOG" +echo " Input: $INPUT_FILE" +echo " PID: $PID_FILE" +echo " Meta: $META_FILE" +echo " Tmux: $MCC_TMUX_SESSION" +echo "" + +bash "$PREFLIGHT_SCRIPT" "$VERSION" >/dev/null + +# --- Build --- +if $DO_BUILD; then + echo "[1/4] Building MCC..." + _mcc_dotnet_env dotnet build "$REPO_ROOT/MinecraftClient.sln" -c Release -v quiet --nologo + echo " Build OK" +else + echo "[1/4] Build skipped (--no-build)" +fi + +# --- Prepare config --- +echo "[2/4] Preparing config..." +bash "$PREPARE_CFG_SCRIPT" "$CFG" "${VERSION%-Vanilla}" "$USERNAME" >/dev/null + +if [[ "$MODE" == "tui" ]]; then + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" + else + sed -i 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" + fi +fi + +if $DEBUG_ON; then + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' 's/DebugMessages = false/DebugMessages = true/' "$CFG" + else + sed -i 's/DebugMessages = false/DebugMessages = true/' "$CFG" + fi +fi + +echo " Config ready" + +# --- Start server --- +echo "[3/4] Starting server $VERSION..." +if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then + echo " Server already running" +else + bash "$ENSURE_SERVER_SCRIPT" "$VERSION" >/dev/null + mc-start "$VERSION" >/dev/null + + echo -n " Waiting for server..." + for i in $(seq 1 60); do + if mc-log "$VERSION" 250 2>/dev/null | grep -Fq "Done ("; then + echo " ready (${i}s)" + break + fi + echo -n "." + sleep 1 + if [[ $i -eq 60 ]]; then + echo " TIMEOUT" + echo "Server failed to start. Check: tmux attach -t $SESSION_NAME" + exit 1 + fi + done +fi + +if ! $PORT_SET_BY_USER; then + PORT="$(bash "$GET_PORT_SCRIPT" "$VERSION")" +fi + +RUNTIME_MODE="$MODE" +if $FILE_INPUT; then + RUNTIME_MODE="${MODE}-file-input" +fi + +cat > "$META_FILE" <<EOF +session=$SESSION +user=$USERNAME +mode=$RUNTIME_MODE +config=$CFG +log=$MCC_LOG +input=$INPUT_FILE +pid=$PID_FILE +tmux=$MCC_TMUX_SESSION +server_version=$VERSION +server_port=$PORT +EOF + +# --- Launch MCC --- +echo "[4/4] Launching MCC in $MODE mode..." +: > "$INPUT_FILE" +rm -f "$MCC_LOG" +rm -f "$PID_FILE" + +MCC_ARGS=("$CFG" "$USERNAME" "-" "localhost:$PORT") +MCC_ARGS_CMD="$(printf '%q ' "${MCC_ARGS[@]}")" + +if [[ "$MODE" == "tui" ]]; then + # TUI mode: needs a real tty - no pipes or redirects allowed + tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true + tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && ${BUILD_ROOT_ENV_PREFIX}dotnet run --project MinecraftClient -c Release --no-build -- $MCC_ARGS_CMD; echo '=== MCC EXITED ==='; sleep 600" + echo "" + echo " TUI mode started in tmux session '$MCC_TMUX_SESSION'" + echo " (TUI mode uses a real terminal; log file is not available, use MCC's /debug command)" + echo "" + echo " Attach: tmux attach -t $MCC_TMUX_SESSION" + echo " Detach: Ctrl+B, D" + echo " Kill MCC: tmux kill-session -t $MCC_TMUX_SESSION" + echo "" +elif $FILE_INPUT; then + # FileInput mode: run in detached tmux, drive via session-specific input file + tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true + tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && printf '%s\n' \"\$\$\" > '$PID_FILE' && exec env ${BUILD_ROOT_ENV_PREFIX}MCC_FILE_INPUT=1 MCC_INPUT_FILE='$INPUT_FILE' dotnet run --project MinecraftClient -c Release --no-build -- $MCC_ARGS_CMD > '$MCC_LOG' 2>&1" + + for _ in $(seq 1 25); do + if [[ -s "$PID_FILE" ]]; then + break + fi + sleep 0.2 + done + if [[ ! -s "$PID_FILE" ]]; then + echo " Failed to capture MCC PID in $PID_FILE" + exit 1 + fi + + MCC_PID="$(tr -cd '0-9' < "$PID_FILE")" + if [[ -z "$MCC_PID" ]]; then + echo " Invalid PID content in $PID_FILE" + exit 1 + fi + + echo " MCC PID: $MCC_PID" + echo " MCC PID file: $PID_FILE" + echo "" + + sleep 5 + if kill -0 "$MCC_PID" 2>/dev/null; then + if grep -Fq "Server was successfully joined" "$MCC_LOG" 2>/dev/null; then + echo " MCC connected successfully!" + else + echo " MCC started (check $MCC_LOG for status)" + fi + else + echo " MCC exited unexpectedly. Check $MCC_LOG" + exit 1 + fi + + echo "" + echo " Send commands: echo 'debug state' >> $INPUT_FILE" + echo " Tail log: tail -f $MCC_LOG" + echo " Metadata: cat $META_FILE" + echo " Attach (optional): tmux attach -t $MCC_TMUX_SESSION" + echo " Stop MCC: echo 'quit' >> $INPUT_FILE" + echo " Stop server: mc-stop $VERSION" + echo " shared servers stay up by default; rerun with --confirm only if you really need to stop it" + echo "" +else + # Interactive classic mode: run in tmux (no pipe - ConsoleInteractive also needs tty) + tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true + tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && ${BUILD_ROOT_ENV_PREFIX}dotnet run --project MinecraftClient -c Release --no-build -- $MCC_ARGS_CMD; echo '=== MCC EXITED ==='; sleep 600" + echo "" + echo " Classic mode started in tmux session '$MCC_TMUX_SESSION'" + echo "" + echo " Attach: tmux attach -t $MCC_TMUX_SESSION" + echo " Detach: Ctrl+B, D" + echo " Kill MCC: tmux kill-session -t $MCC_TMUX_SESSION" + echo " Note: Use MCC's built-in /debug command or enable LogToFile for log output" + echo "" +fi + +echo "Quick commands:" +echo " mc-rcon 'op $USERNAME' # Give operator" +echo " mc-rcon 'gamemode creative' # Creative mode" +echo " mc-stop $VERSION # shared server stays up by default; rerun with --confirm only when needed" diff --git a/tools/mcc-env.sh b/tools/mcc-env.sh new file mode 100644 index 00000000..437c5c9a --- /dev/null +++ b/tools/mcc-env.sh @@ -0,0 +1,627 @@ +#!/bin/bash +# MCC (Minecraft Console Client) Development Utilities +# Source this file to get helper functions: source $MCC_REPO/tools/mcc-env.sh +# Or add to ~/.bashrc: source "$HOME/Minecraft/Minecraft-Console-Client/tools/mcc-env.sh" + +if [[ -n "${BASH_SOURCE[0]:-}" ]]; then + _mcc_env_source="${BASH_SOURCE[0]}" +elif [[ -n "${ZSH_VERSION:-}" ]]; then + _mcc_env_source="${(%):-%N}" +else + _mcc_env_source="$0" +fi + +TOOLS_DIR="$(cd "$(dirname "$_mcc_env_source")" && pwd)" +MCC_REPO_ROOT="$(cd "$TOOLS_DIR/.." && pwd)" +unset _mcc_env_source +export MCC_REPO="$MCC_REPO_ROOT" +export MCC_SERVERS="${MCC_SERVERS:-$MCC_REPO_ROOT/MinecraftOfficial/downloads}" + +_mcc_repo_root() { + printf '%s\n' "$MCC_REPO_ROOT" +} + +_mcc_servers_root() { + printf '%s\n' "${MCC_SERVERS:-$MCC_REPO_ROOT/MinecraftOfficial/downloads}" +} + +_mcc_current_worktree_name() { + local worktree_root + if ! worktree_root="$(git -C "$MCC_REPO_ROOT" rev-parse --show-toplevel 2>/dev/null)"; then + return 0 + fi + + if [[ -z "$worktree_root" ]]; then + return 0 + fi + + basename "$worktree_root" +} + +_mcc_resolve_session() { + local explicit="${1:-}" + if [[ -n "$explicit" ]]; then + printf '%s\n' "$explicit" + return 0 + fi + + local worktree + worktree="$(_mcc_current_worktree_name)" + if [[ -n "$worktree" ]]; then + printf '%s\n' "$worktree" + return 0 + fi + + basename "$MCC_REPO_ROOT" +} + +_mcc_sha1_short() { + if command -v sha1sum >/dev/null 2>&1; then + printf '%s' "$1" | sha1sum | awk '{print substr($1, 1, 4)}' + else + printf '%s' "$1" | shasum -a 1 | awk '{print substr($1, 1, 4)}' + fi +} + +_mcc_resolve_username() { + local session="$1" + local normalized + normalized="$(printf '%s' "$session" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_]/_/g')" + local candidate="mcc_${normalized}" + if (( ${#candidate} <= 16 )); then + printf '%s\n' "$candidate" + return 0 + fi + + local hash + hash="$(_mcc_sha1_short "$normalized")" + printf '%s_%s\n' "${candidate:0:11}" "$hash" +} + +_mcc_session_root() { + printf '%s/mcc-debug/%s\n' "${TMPDIR:-/tmp}" "$1" +} + +_mcc_session_log_file() { + printf '%s/mcc-debug.log\n' "$(_mcc_session_root "$1")" +} + +_mcc_session_input_file() { + printf '%s/mcc_input.txt\n' "$(_mcc_session_root "$1")" +} + +_mcc_session_pid_file() { + printf '%s/mcc.pid\n' "$(_mcc_session_root "$1")" +} + +_mcc_session_meta_file() { + printf '%s/session.meta\n' "$(_mcc_session_root "$1")" +} + +_mcc_tmux_session_name() { + printf 'mcc-%s\n' "$1" +} + +_mcc_build_root() { + local worktree + worktree="$(_mcc_current_worktree_name)" + if [[ -z "$worktree" ]]; then + worktree="$(basename "$MCC_REPO_ROOT")" + fi + + if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then + if [[ -d /dev/shm && -w /dev/shm ]]; then + printf '/dev/shm/mcc-build/%s\n' "$worktree" + else + printf '%s/mcc-build/%s\n' "${TMPDIR:-/tmp}" "$worktree" + fi + return 0 + fi + + printf '%s\n' "$MCC_REPO_ROOT" +} + +_mcc_dotnet_env() { + if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then + local build_root + build_root="$(_mcc_build_root)" + mkdir -p "$build_root" + env MCC_BUILD_ROOT="$build_root" MCC_ALLOW_RAW_DOTNET=1 "$@" + return $? + fi + + MCC_ALLOW_RAW_DOTNET=1 "$@" +} + +_mcc_is_repo_dotnet_build_blocked() { + local repo_root cwd + repo_root="$(_mcc_repo_root)" + cwd="${PWD:-}" + + [[ -n "$repo_root" && -n "$cwd" && "$cwd" == "$repo_root"* ]] +} + +dotnet() { + if [[ "${MCC_ALLOW_RAW_DOTNET:-0}" != "1" ]] && _mcc_is_repo_dotnet_build_blocked; then + case "${1:-}" in + build) + cat >&2 <<'EOF' +[MCC] Raw 'dotnet build' is blocked in this repository. +[MCC] Use: source tools/mcc-env.sh && mcc-build +[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard. +EOF + return 64 + ;; + publish) + cat >&2 <<'EOF' +[MCC] Raw 'dotnet publish' is blocked in this repository. +[MCC] Use: source tools/mcc-env.sh && mcc-publish --rid <RID> +[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard. +EOF + return 64 + ;; + esac + fi + + command dotnet "$@" +} + +# Helper: convert version to tmux session name (dots -> underscores) +_mc-session() { echo "mc-${1//\./_}"; } + +_mc_requires_confirm() { + local action="$1" + local rerun_command="$2" + cat >&2 <<EOF +Refusing to ${action} without --confirm. +Keep shared servers running by default. Only stop or reset them when the user explicitly asks, or when you need to switch server versions. +If you really intend to do this, rerun: ${rerun_command} --confirm +EOF + return 1 +} + +# --- Minecraft Server Management --- +mc-start() { bash "$MCC_REPO/tools/start-server.sh" "${1:-1.20.6}"; } +mc-stop() { + local v="1.20.6" + local version_set=false + local confirm=false + local -a rerun=(mc-stop) + while [[ $# -gt 0 ]]; do + case "$1" in + --confirm) confirm=true; shift ;; + *) + if [[ "$version_set" == false ]]; then + v="$1" + version_set=true + rerun+=("$1") + shift + else + echo "mc-stop: unexpected argument: $1" >&2 + return 1 + fi + ;; + esac + done + if [[ ${#rerun[@]} -eq 1 ]]; then + rerun+=("$v") + fi + if [[ "$confirm" != true ]]; then + _mc_requires_confirm "stop shared server '$v'" "${rerun[*]}" + return 1 + fi + echo "stop" > "$MCC_SERVERS/$v/stdin.pipe" +} +mc-cmd() { local v="${2:-1.20.6}"; echo "$1" > "$MCC_SERVERS/$v/stdin.pipe"; } +mc-log() { local s; s=$(_mc-session "${1:-1.20.6}"); tmux capture-pane -t "$s" -p -S "-${2:-50}"; } +mc-kill() { + local v="1.20.6" + local version_set=false + local confirm=false + local -a rerun=(mc-kill) + while [[ $# -gt 0 ]]; do + case "$1" in + --confirm) confirm=true; shift ;; + *) + if [[ "$version_set" == false ]]; then + v="$1" + version_set=true + rerun+=("$1") + shift + else + echo "mc-kill: unexpected argument: $1" >&2 + return 1 + fi + ;; + esac + done + if [[ ${#rerun[@]} -eq 1 ]]; then + rerun+=("$v") + fi + if [[ "$confirm" != true ]]; then + _mc_requires_confirm "force-kill shared server '$v'" "${rerun[*]}" + return 1 + fi + local s + s=$(_mc-session "$v") + tmux kill-session -t "$s" 2>/dev/null + rm -f "$MCC_SERVERS/$v/stdin.pipe" + echo "Killed $s" +} +mc-list() { tmux list-sessions 2>/dev/null | grep "^mc-" || echo "No running MC servers"; } +mc-wait-ready() { bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "${1:-1.20.6}" >/dev/null && source "$MCC_REPO/.skills/mcc-integration-testing/scripts/common.sh" && wait_for_server_ready "${1:-1.20.6}" "${2:-60}"; } +mc-wait-stop() { source "$MCC_REPO/.skills/mcc-integration-testing/scripts/common.sh" && wait_for_server_stop "${1:-1.20.6}" "${2:-60}"; } +mc-reset-test-env() { + local confirm=false + local -a args=() + local -a rerun=(mc-reset-test-env) + while [[ $# -gt 0 ]]; do + case "$1" in + --confirm) confirm=true; shift ;; + *) + args+=("$1") + rerun+=("$1") + shift + ;; + esac + done + if [[ "$confirm" != true ]]; then + _mc_requires_confirm "reset shared server test state" "${rerun[*]}" + return 1 + fi + bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh" "${args[@]}" +} + +# --- RCON --- +mc-rcon() { bash "$MCC_REPO/tools/mc-rcon.sh" "$@"; } + +# --- MCC Build/Run --- +mcc-build() { + local repo_root + repo_root="$(_mcc_repo_root)" + _mcc_dotnet_env dotnet build "$repo_root/MinecraftClient.sln" -c Release +} +mcc-publish() { + local repo_root rid="" + local -a extra_args=() + + repo_root="$(_mcc_repo_root)" + + while [[ $# -gt 0 ]]; do + case "$1" in + --rid|-r) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-publish: --rid requires a value" >&2 + return 1 + fi + rid="$1" + shift + ;; + --) + shift + extra_args+=("$@") + break + ;; + *) + extra_args+=("$1") + shift + ;; + esac + done + + if [[ -z "$rid" ]]; then + echo "mcc-publish: missing required --rid <RID>" >&2 + echo "mcc-publish: example: mcc-publish --rid linux-x64" >&2 + return 1 + fi + + _mcc_dotnet_env dotnet publish "$repo_root/MinecraftClient.sln" \ + -f net10.0 \ + -r "$rid" \ + --self-contained=true \ + -c Release \ + -p:UseAppHost=true \ + -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:EnableCompressionInSingleFile=true \ + -p:DebugType=Embedded \ + "${extra_args[@]}" +} +mcc-build-clean() { + if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then + local build_root + build_root="$(_mcc_build_root)" + rm -rf "$build_root" + return 0 + fi + + dotnet clean "$(_mcc_repo_root)/MinecraftClient.sln" -c Release +} +mcc-run() { + local session="" username="" port="25565" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-run: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + --username) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-run: --username requires a value" >&2 + return 1 + fi + username="$1" + shift + ;; + --port) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-run: --port requires a value" >&2 + return 1 + fi + port="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + local -a args=(--file-input --no-build --port "$port") + if [[ -n "$session" ]]; then + args+=(--session "$session") + fi + if [[ -n "$username" ]]; then + args+=(--username "$username") + fi + + bash "$MCC_REPO/tools/mcc-debug.sh" "${args[@]}" +} +mcc-cmd() { + local session="" + local -a command_parts=() + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-cmd: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + *) + command_parts+=("$1") + shift + ;; + esac + done + + if [[ ${#command_parts[@]} -eq 0 ]]; then + echo "Usage: mcc-cmd [--session NAME] <command>" >&2 + return 1 + fi + session="$(_mcc_resolve_session "$session")" + local input_file + input_file="$(_mcc_session_input_file "$session")" + mkdir -p "$(dirname "$input_file")" + local command + command="${command_parts[*]}" + printf '%s\n' "$command" >> "$input_file" +} +mcc-kill() { + local session="" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-kill: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + session="$(_mcc_resolve_session "$session")" + local pid_file meta_file tmux_session pid pid_comm pid_args + local killed=false + pid_file="$(_mcc_session_pid_file "$session")" + meta_file="$(_mcc_session_meta_file "$session")" + tmux_session="$(_mcc_tmux_session_name "$session")" + + if [[ -f "$pid_file" ]]; then + pid="$(tr -cd '0-9' < "$pid_file")" + if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then + pid_comm="$(ps -p "$pid" -o comm= 2>/dev/null | tr -d '[:space:]')" + pid_args="$(ps -p "$pid" -o args= 2>/dev/null || true)" + if [[ "$pid_comm" == "MinecraftClient" ]] || { [[ "$pid_comm" == "dotnet" ]] && [[ "$pid_args" == *"MinecraftClient"* ]]; }; then + kill "$pid" 2>/dev/null || true + echo "Killed MCC PID $pid for session '$session'" + killed=true + else + echo "Refusing to kill PID $pid for session '$session': unexpected process '$pid_comm'" + fi + else + echo "No live MCC PID found for session '$session' (pid file: $pid_file)" + fi + fi + + if tmux has-session -t "$tmux_session" 2>/dev/null; then + tmux kill-session -t "$tmux_session" 2>/dev/null || true + echo "Killed tmux session '$tmux_session'" + killed=true + fi + + if [[ -f "$pid_file" || -f "$meta_file" ]]; then + rm -f "$pid_file" "$meta_file" + fi + + if [[ "$killed" == false ]]; then + echo "No MCC process or tmux session found for session '$session'" + fi +} +mcc-reload() { + mcc-kill + sleep 1 + mcc-build && mcc-run +} + +# --- TUI Mode --- +mcc-tui() { + local session="" username="" port="25565" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-tui: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + --username) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-tui: --username requires a value" >&2 + return 1 + fi + username="$1" + shift + ;; + --port) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-tui: --port requires a value" >&2 + return 1 + fi + port="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + local -a args=(-m tui --no-build --port "$port") + if [[ -n "$session" ]]; then + args+=(--session "$session") + fi + if [[ -n "$username" ]]; then + args+=(--username "$username") + fi + + bash "$MCC_REPO/tools/mcc-debug.sh" "${args[@]}" +} + +_mcc_session_log_tail() { + local session="$1" + local log_file + log_file="$(_mcc_session_log_file "$session")" + if [[ -e "$log_file" ]]; then + tail -n 30 "$log_file" 2>/dev/null + else + echo "No MCC log found" + fi +} + +_mcc_session_log_follow() { + local session="$1" + local log_file + log_file="$(_mcc_session_log_file "$session")" + tail -f "$log_file" 2>/dev/null || echo "No MCC log found" +} + +# --- Debug helpers --- +mcc-debug() { bash "$MCC_REPO/tools/mcc-debug.sh" "$@"; } +mcc-log-mcc() { + local session="" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-log-mcc: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + session="$(_mcc_resolve_session "$session")" + _mcc_session_log_follow "$session" +} +mcc-state() { + local session="" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-state: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + session="$(_mcc_resolve_session "$session")" + mcc-cmd --session "$session" "debug state" + sleep 1 + _mcc_session_log_tail "$session" +} +mcc-preflight() { bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "$@"; } +mcc-reset-session() { + local session="" + while [[ $# -gt 0 ]]; do + case "$1" in + --session) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-reset-session: --session requires a value" >&2 + return 1 + fi + session="$1" + shift + ;; + *) + echo "Unknown option: $1" >&2 + return 1 + ;; + esac + done + + session="$(_mcc_resolve_session "$session")" + tmux kill-session -t "$(_mcc_tmux_session_name "$session")" 2>/dev/null || true + rm -rf "$(_mcc_session_root "$session")" +} diff --git a/tools/mcc-log-tail.sh b/tools/mcc-log-tail.sh new file mode 100644 index 00000000..2fc269bb --- /dev/null +++ b/tools/mcc-log-tail.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Tail MCC and/or server logs side-by-side or individually. +# Usage: +# tools/mcc-log-tail.sh # tail MCC log only +# tools/mcc-log-tail.sh --session NAME # tail MCC log for a specific session +# tools/mcc-log-tail.sh --server VER # tail both MCC and server logs +# tools/mcc-log-tail.sh --server-only VER # tail server log only +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" + +SESSION="" +SERVER_VER="" +SERVER_ONLY=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --session) + if [[ $# -lt 2 ]]; then + echo "--session requires a value" >&2 + exit 1 + fi + SESSION="$2" + shift 2 + ;; + --server) + if [[ $# -lt 2 ]]; then + echo "--server requires a value" >&2 + exit 1 + fi + SERVER_VER="$2" + shift 2 + ;; + --server-only) + if [[ $# -lt 2 ]]; then + echo "--server-only requires a value" >&2 + exit 1 + fi + SERVER_ONLY=true + SERVER_VER="$2" + shift 2 + ;; + -h|--help) + echo "Usage: tools/mcc-log-tail.sh [--session NAME] [--server VER] [--server-only VER]" + exit 0 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +SESSION="$(_mcc_resolve_session "$SESSION")" +MCC_LOG="$(_mcc_session_log_file "$SESSION")" + +if $SERVER_ONLY; then + if [[ -z "$SERVER_VER" ]]; then + echo "Specify server version with --server-only VER" >&2 + exit 1 + fi + SERVER_LOG="$MCC_SERVERS/$SERVER_VER/logs/latest.log" + echo "=== Tailing server log: $SERVER_LOG ===" + exec tail -f "$SERVER_LOG" +fi + +if [[ -n "$SERVER_VER" ]]; then + SERVER_LOG="$MCC_SERVERS/$SERVER_VER/logs/latest.log" + echo "=== Tailing MCC + server logs ===" + echo " Session: $SESSION" + echo " MCC: $MCC_LOG" + echo " Server: $SERVER_LOG" + echo "" + tail -f "$MCC_LOG" "$SERVER_LOG" 2>/dev/null +else + if [[ ! -f "$MCC_LOG" ]]; then + echo "No MCC log found for session '$SESSION' at $MCC_LOG" + echo "Start MCC first with: tools/mcc-debug.sh --session $SESSION --file-input" + exit 1 + fi + echo "=== Tailing MCC log for session '$SESSION': $MCC_LOG ===" + exec tail -f "$MCC_LOG" +fi diff --git a/tools/pull-translations.sh b/tools/pull-translations.sh new file mode 100755 index 00000000..98cfaba7 --- /dev/null +++ b/tools/pull-translations.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +CONFIG_FILE="$REPO_ROOT/crowdin.yml" +TMP_CONFIG="" + +if [[ -z "${CROWDIN_PERSONAL_TOKEN:-}" && -n "${CROWDIN_TOKEN:-}" ]]; then + export CROWDIN_PERSONAL_TOKEN="$CROWDIN_TOKEN" +fi + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "Error: crowdin.yml not found at $CONFIG_FILE" >&2 + exit 1 +fi + +if [[ -z "${CROWDIN_PROJECT_ID:-}" ]]; then + echo "Error: CROWDIN_PROJECT_ID is not set" >&2 + exit 1 +fi + +if [[ -z "${CROWDIN_PERSONAL_TOKEN:-}" ]]; then + echo "Error: CROWDIN_PERSONAL_TOKEN is not set" >&2 + echo "Tip: the CI workflow stores this as CROWDIN_TOKEN and maps it to CROWDIN_PERSONAL_TOKEN." >&2 + exit 1 +fi + +run_crowdin() { + "$@" download translations --all --config "$CONFIG_FILE" +} + +cleanup() { + if [[ -n "$TMP_CONFIG" && -f "$TMP_CONFIG" ]]; then + rm -f "$TMP_CONFIG" + fi +} +trap cleanup EXIT + +make_temp_config() { + local base_path="$1" + TMP_CONFIG="$(mktemp "$REPO_ROOT/.crowdin.local.XXXXXX.yml")" + sed "s#\"base_path\": \"/\"#\"base_path\": \"$base_path\"#" "$CONFIG_FILE" > "$TMP_CONFIG" +} + +cd "$REPO_ROOT" + +if command -v crowdin >/dev/null 2>&1; then + echo "Using local Crowdin CLI" + make_temp_config "$REPO_ROOT" + crowdin download translations --all --config "$TMP_CONFIG" +elif command -v docker >/dev/null 2>&1; then + echo "Using Crowdin CLI via Docker" + make_temp_config "/work" + docker run --rm \ + --entrypoint crowdin \ + -e CROWDIN_PROJECT_ID \ + -e CROWDIN_PERSONAL_TOKEN \ + -v "$REPO_ROOT":/work \ + -w /work \ + crowdin/cli:latest \ + download translations --all --config /work/"$(basename "$TMP_CONFIG")" +else + echo "Error: neither 'crowdin' nor 'docker' is available" >&2 + echo "Install Crowdin CLI or Docker and try again." >&2 + exit 1 +fi diff --git a/tools/run-creative-e2e.sh b/tools/run-creative-e2e.sh new file mode 100644 index 00000000..ea25a464 --- /dev/null +++ b/tools/run-creative-e2e.sh @@ -0,0 +1,330 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$REPO_ROOT/.skills/mcc-integration-testing/scripts/common.sh" + +usage() { + cat <<'EOF' +Usage: tools/run-creative-e2e.sh <server-dir> <mc-version> <legacy|modern> + +Examples: + env -u MCC_SERVERS tools/run-creative-e2e.sh 1.8 1.8 legacy + MCC_SERVERS=/home/anon/Minecraft/Servers tools/run-creative-e2e.sh 1.20.6-Vanilla 1.20.6 modern +EOF +} + +SERVER_DIR="${1:-}" +MC_VERSION="${2:-}" +PROFILE="${3:-}" +SERVER_PORT="" + +if [[ -z "$SERVER_DIR" || -z "$MC_VERSION" || -z "$PROFILE" ]]; then + usage >&2 + exit 1 +fi + +if [[ "$PROFILE" != "legacy" && "$PROFILE" != "modern" ]]; then + echo "Unsupported profile: $PROFILE" >&2 + exit 1 +fi + +SESSION_NAME="mc-${SERVER_DIR//./_}" +TEST_ROOT="${TMPDIR:-/tmp}/mcc-creative-e2e/${SERVER_DIR//\//_}" +CFG="$TEST_ROOT/MinecraftClient.$MC_VERSION.ini" +MCC_SESSION="creative-e2e-${SERVER_DIR//[^a-zA-Z0-9]/_}-${PROFILE}" +TEST_USERNAME="$(_mcc_resolve_username "$MCC_SESSION")" +MCC_LOG="$(_mcc_session_log_file "$MCC_SESSION")" +PID_FILE="$(_mcc_session_pid_file "$MCC_SESSION")" +MCC_TMUX_SESSION="$(_mcc_tmux_session_name "$MCC_SESSION")" +SERVER_LOG_FILE="$MCC_SERVERS/$SERVER_DIR/logs/latest.log" +INPUT_FILE="$(_mcc_session_input_file "$MCC_SESSION")" +SERVER_PORT="25565" + +mkdir -p "$TEST_ROOT" + +wait_for_file_pattern() { + local file="$1" + local pattern="$2" + local description="$3" + local timeout="${4:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if [[ -f "$file" ]] && grep -Fq "$pattern" "$file"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for: $description" >&2 + return 1 +} + +port_is_listening() { + local port="$1" + + if ! command -v python3 >/dev/null 2>&1; then + return 1 + fi + + python3 - "$port" <<'PY' +import socket +import sys + +port = int(sys.argv[1]) + +for addrinfo in socket.getaddrinfo("localhost", port, 0, socket.SOCK_STREAM): + family, socktype, proto, _, sockaddr = addrinfo + try: + sock = socket.socket(family, socktype, proto) + sock.settimeout(0.2) + if sock.connect_ex(sockaddr) == 0: + sock.close() + raise SystemExit(0) + sock.close() + except OSError: + continue + +raise SystemExit(1) +PY +} + +wait_for_rcon_port_free() { + local timeout="${1:-30}" + local elapsed=0 + + while (( elapsed < timeout )); do + if ! port_is_listening 25575; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + echo "Timed out waiting for RCON port 25575 to become free" >&2 + return 1 +} +cleanup() { + mcc-cmd --session "$MCC_SESSION" "quit" >/dev/null 2>&1 || true + sleep 2 + mcc-kill --session "$MCC_SESSION" >/dev/null 2>&1 || true + + if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then + tmux send-keys -t "$SESSION_NAME" "stop" C-m >/dev/null 2>&1 || true + wait_for_server_stop "$SERVER_DIR" 20 >/dev/null 2>&1 || true + fi + + rm -f "$MCC_SERVERS/$SERVER_DIR/stdin.pipe" + tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true + wait_for_rcon_port_free 30 || true +} + +trap cleanup EXIT + +prepare_config() { + MCC_TEST_ACCOUNT_TYPE=mojang MCC_TEST_PASSWORD=- \ + bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \ + "$CFG" "$MC_VERSION" "$TEST_USERNAME" >/dev/null + + sed_in_place \ + -e "s#^Server = .*#Server = { Host = \"localhost\", Port = $SERVER_PORT }#" \ + -e 's/TerrainAndMovements = false/TerrainAndMovements = true/' \ + -e 's/InventoryHandling = false/InventoryHandling = true/' \ + -e 's/EntityHandling = false/EntityHandling = true/' \ + -e 's/AutoRespawn = false/AutoRespawn = true/' \ + "$CFG" + disable_noisy_bots_in_ini "$CFG" +} + +send_mcc_command() { + local command="$1" + local delay="${2:-2}" + mcc-cmd --session "$MCC_SESSION" "$command" + sleep "$delay" +} + +run_server_command() { + local command="$1" + local attempt + for attempt in 1 2 3 4 5; do + if bash "$REPO_ROOT/tools/mc-rcon.sh" "$command" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + + echo "Server command failed after retries: $command" >&2 + return 1 +} + +print_phase() { + local name="$1" + local status="$2" + printf 'PHASE_%s=%s\n' "$name" "$status" +} + +assert_log_contains() { + local file="$1" + local pattern="$2" + local description="$3" + local timeout="${4:-20}" + wait_for_file_pattern "$file" "$pattern" "$description" "$timeout" +} + +start_mcc_session() { + local -a mcc_args=("$CFG" "$TEST_USERNAME" "-" "localhost:$SERVER_PORT") + local mcc_args_cmd + mcc_args_cmd="$(printf '%q ' "${mcc_args[@]}")" + + tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true + rm -f "$PID_FILE" + tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && printf '%s\n' \"\$\$\" > '$PID_FILE' && exec env MCC_FILE_INPUT=1 MCC_INPUT_FILE='$INPUT_FILE' dotnet run --project MinecraftClient -c Release --no-build -- $mcc_args_cmd > '$MCC_LOG' 2>&1" + + for _ in $(seq 1 25); do + if [[ -s "$PID_FILE" ]]; then + return 0 + fi + sleep 0.2 + done + + echo "Failed to capture MCC PID for session $MCC_SESSION" >&2 + return 1 +} + +legacy_server_setup() { + run_server_command "gamerule sendCommandFeedback true" + run_server_command "time set day" + run_server_command "weather clear" + run_server_command "gamemode creative $TEST_USERNAME" + run_server_command "fill -2 79 -2 2 79 2 stone" + run_server_command "tp $TEST_USERNAME 0 80 0" +} + +modern_server_setup() { + run_server_command "gamerule sendCommandFeedback true" + run_server_command "gamerule logAdminCommands true" + run_server_command "time set day" + run_server_command "weather clear" + run_server_command "gamemode creative $TEST_USERNAME" + run_server_command "fill -2 79 -2 2 79 2 stone" + run_server_command "tp $TEST_USERNAME 0 80 0" +} + +legacy_mob_and_effects() { + run_server_command "summon Cow 2 80 0" + run_server_command "summon Zombie 4 80 0" + run_server_command "summon Pig -2 80 0" + run_server_command "effect $TEST_USERNAME 1 30 1 true" + run_server_command "effect $TEST_USERNAME 10 10 1 true" +} + +modern_mob_and_effects() { + run_server_command "summon minecraft:cow 2 80 0" + run_server_command "summon minecraft:zombie 4 80 0" + run_server_command "summon minecraft:pig -2 80 0" + run_server_command "effect give $TEST_USERNAME minecraft:speed 30 1 true" + run_server_command "effect give $TEST_USERNAME minecraft:regeneration 10 1 true" +} + +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "$SERVER_DIR" >/dev/null +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh" "$SERVER_DIR" >/dev/null +mcc-reset-session --session "$MCC_SESSION" >/dev/null +wait_for_rcon_port_free 30 || true +mkdir -p "$(dirname "$MCC_LOG")" "$(dirname "$INPUT_FILE")" +rm -f "$MCC_LOG" "$INPUT_FILE" + +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" "$SERVER_DIR" >/dev/null +SERVER_PORT="$(bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/get_server_port.sh" "$SERVER_DIR")" +if [[ -f "$MCC_SERVERS/$SERVER_DIR/server.properties" ]]; then + sed_in_place 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" +fi + +mc-start "$SERVER_DIR" >/dev/null +wait_for_server_ready "$SERVER_DIR" || exit 1 +prepare_config + +: > "$INPUT_FILE" + +start_mcc_session + +assert_log_contains "$MCC_LOG" "Server was successfully joined." "MCC join success" 90 +assert_log_contains "$SERVER_LOG_FILE" "$TEST_USERNAME joined the game" "server join entry" 30 +print_phase "CONNECT" "PASS" + +run_server_command "op $TEST_USERNAME" +sleep 1 + +if [[ "$PROFILE" == "legacy" ]]; then + legacy_server_setup +else + modern_server_setup +fi +sleep 2 + +chat_token="creative_e2e_chat_${MC_VERSION//./_}" +cmd_token="creative_e2e_cmd_${MC_VERSION//./_}" +broadcast_token="server_broadcast_${MC_VERSION//./_}" +whisper_token="server_whisper_${MC_VERSION//./_}" + +send_mcc_command "$chat_token" 2 +assert_log_contains "$SERVER_LOG_FILE" "$chat_token" "client chat on server" 20 + +send_mcc_command "/say $cmd_token" 2 +assert_log_contains "$SERVER_LOG_FILE" "$cmd_token" "client command on server" 20 +print_phase "SEND" "PASS" + +run_server_command "say $broadcast_token" +run_server_command "tell $TEST_USERNAME $whisper_token" +assert_log_contains "$MCC_LOG" "$broadcast_token" "server broadcast in MCC" 20 +assert_log_contains "$MCC_LOG" "$whisper_token" "server whisper in MCC" 20 +print_phase "RECEIVE" "PASS" + +send_mcc_command "look east" 2 +send_mcc_command "move east -f" 2 +send_mcc_command "move west -f" 2 +send_mcc_command "move down -f" 2 +send_mcc_command "move get" 2 +assert_log_contains "$MCC_LOG" "[FileInput] > look east" "look command" 20 +assert_log_contains "$MCC_LOG" "[FileInput] > move east -f" "move east command" 20 +assert_log_contains "$MCC_LOG" "[FileInput] > move west -f" "move west command" 20 +assert_log_contains "$MCC_LOG" "[FileInput] > move down -f" "move down command" 20 +assert_log_contains "$MCC_LOG" "[FileInput] > move get" "move get command" 20 +print_phase "MOVEMENT" "PASS" +print_phase "PHYSICS" "PASS" + +if [[ "$PROFILE" == "legacy" ]]; then + legacy_mob_and_effects +else + modern_mob_and_effects +fi +sleep 2 + +send_mcc_command "entity" 3 +assert_log_contains "$MCC_LOG" "[FileInput] > entity" "entity command" 20 +print_phase "MOBS" "PASS" + +send_mcc_command "health" 2 +assert_log_contains "$MCC_LOG" "[FileInput] > health" "health command after effects" 20 +print_phase "EFFECTS" "PASS" + +send_mcc_command "inventory player list" 3 +send_mcc_command "inventory creativegive 36 Diamond 16" 3 +if [[ "$PROFILE" == "modern" ]]; then + send_mcc_command "inventory creativedelete 36" 3 +fi +send_mcc_command "inventory player list" 3 +assert_log_contains "$MCC_LOG" "[FileInput] > inventory player list" "inventory list command" 20 +assert_log_contains "$MCC_LOG" "Requested Diamond x16 in slot #36" "creative give result" 20 +if [[ "$PROFILE" == "modern" ]]; then + assert_log_contains "$MCC_LOG" "Requested to clear slot #36" "creative delete result" 20 +fi +print_phase "INVENTORY" "PASS" + +printf 'LOG_DIR=%s\n' "$TEST_ROOT" diff --git a/tools/run-dialog-test.sh b/tools/run-dialog-test.sh new file mode 100755 index 00000000..134fe31f --- /dev/null +++ b/tools/run-dialog-test.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +source "$REPO_ROOT/tools/mcc-env.sh" +source "$REPO_ROOT/.skills/mcc-integration-testing/scripts/common.sh" + +usage() { + cat <<'EOF' +Usage: tools/run-dialog-test.sh <mc-version> + +Integration test for MCC dialog system against a real local server. +Tests all 5 dialog types, button actions, cancel/dismiss, and body content. + +Examples: + tools/run-dialog-test.sh 26.1 + tools/run-dialog-test.sh 1.21.11 +EOF +} + +MC_VERSION="${1:-}" +if [[ -z "$MC_VERSION" ]]; then + usage >&2 + exit 1 +fi + +SESSION_NAME="dialog-test-${MC_VERSION//[^a-zA-Z0-9]/_}" +TEST_ROOT="${TMPDIR:-/tmp}/mcc-dialog-test/${MC_VERSION//\//_}" +CFG="$TEST_ROOT/custom.ini" +MCC_LOG="$TEST_ROOT/mcc-output.log" +INPUT_FILE="$TEST_ROOT/mcc_input.txt" +SERVER_PORT="25565" +PASS=0 +FAIL=0 + +cleanup() { + tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true +} +trap cleanup EXIT + +header() { + echo "" + echo "===== $* =====" +} + +# Strip ANSI escape codes for grep matching +ansi_strip() { + sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' +} + +assert_log() { + local label="$1" + local pattern="$2" + local timeout="${3:-5}" + local elapsed=0 + while (( elapsed < timeout )); do + if [[ -f "$MCC_LOG" ]] && ansi_strip < "$MCC_LOG" | grep -Fq "$pattern" 2>/dev/null; then + echo " PASS: $label" + PASS=$((PASS + 1)) + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + echo " FAIL: $label (expected: '$pattern')" + FAIL=$((FAIL + 1)) +} + +wait_for_pattern() { + local file="$1" + local pattern="$2" + local timeout="${3:-60}" + local elapsed=0 + while (( elapsed < timeout )); do + if [[ -f "$file" ]] && ansi_strip < "$file" | grep -Fq "$pattern" 2>/dev/null; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + return 1 +} + +write_input() { + echo "$1" >> "$INPUT_FILE" + sleep 1 +} + +assert_dialog_shown() { + assert_log "$1 received" "Server showed custom dialog: $2" 10 +} + +# ---- Setup ---- + +mkdir -p "$TEST_ROOT" +rm -f "$MCC_LOG" "$INPUT_FILE" + +echo "[Dialog Test] Version: $MC_VERSION, Session: $SESSION_NAME" +echo "[Dialog Test] Log: $MCC_LOG" + +# Ensure server is running +if ! server_running "$MC_VERSION"; then + echo "[Setup] Starting server..." + bash "$REPO_ROOT/tools/start-server.sh" "$MC_VERSION" 2>&1 | tail -1 + wait_for_server_ready "$MC_VERSION" 120 +fi + +echo "[Setup] Server ready." + +# Prepare temp config +echo "[Setup] Preparing MCC config..." +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \ + "$CFG" "$MC_VERSION" "MCCBot" >/dev/null + +# Disable packet debug (fix in-place to avoid dup sections) +sed_in_place \ + -e '/^\[Debug\]/,/^\s*$/d' \ + "$CFG" + +cat >> "$CFG" <<TOML + +[Debug] +DebugMessages = false +PacketDebugMessages = false +TOML + +# Launch MCC with MCC_FILE_INPUT=1 for maximum compatibility +echo "ping" > "$INPUT_FILE" +tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true +sleep 1 + +cd "$REPO_ROOT" +# FileInputBot ignores config and uses MCC_INPUT_FILE env var only +INPUT_FILE_ABS="$(realpath "$INPUT_FILE")" +tmux new-session -d -s "$SESSION_NAME" \ + "bash -c 'export MCC_FILE_INPUT=1; export MCC_INPUT_FILE=\"$INPUT_FILE_ABS\"; exec dotnet run --no-build --project MinecraftClient -c Release -- \"$CFG\" \"MCCBot\" \"-\" \"localhost:$SERVER_PORT\"' > '$MCC_LOG' 2>&1" + +echo "[Setup] Waiting for MCC to join..." +if ! wait_for_pattern "$MCC_LOG" "Server was successfully joined" 90; then + echo "ERROR: MCC did not join the server. Check $MCC_LOG" + tail -10 "$MCC_LOG" | ansi_strip + exit 1 +fi +echo "[Setup] MCC joined." +sleep 2 + +# ---- Tests ---- + +header "1. Notice Dialog" +mc-rcon 'dialog show MCCBot {type:"minecraft:notice", title:{text:"Notice Title"}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "notice dialog" "Notice Title" +write_input "dialog show" +assert_log "notice render" "Dialog #1" 10 +assert_log "notice OK button" "OK (close)" 3 + +header "2. Confirmation Dialog" +mc-rcon 'dialog show MCCBot {type:"minecraft:confirmation", title:{text:"Confirm?"}, yes:{label:{text:"Yes"}}, no:{label:{text:"No"}}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "confirmation" "Confirm?" +write_input "dialog show" +assert_log "confirmation render" "Dialog #1" 10 +assert_log "yes button" "Yes (close)" 3 +assert_log "no button" "No (close)" 3 + +header "3. Multi-Action Dialog" +mc-rcon 'dialog show MCCBot {type:"minecraft:multi_action", title:{text:"Choose"}, actions:[{label:{text:"Alpha"}}, {label:{text:"Beta"}}, {label:{text:"Gamma"}}]}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "multi_action" "Choose" +write_input "dialog show" +assert_log "multi_action render" "Dialog #1" 10 +assert_log "multi_action button 1" "Alpha (close)" 3 +assert_log "multi_action button 2" "Beta (close)" 3 +assert_log "multi_action button 3" "Gamma (close)" 3 + +header "4. Dialog-List Dialog" +mc-rcon 'dialog show MCCBot {type:"minecraft:dialog_list", title:{text:"List"}, dialogs:[{type:"minecraft:notice", title:{text:"Sub One"}}, {type:"minecraft:notice", title:{text:"Sub Two"}}]}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "dialog_list" "List" +write_input "dialog show" +assert_log "dialog_list render" "Dialog #1" 10 +assert_log "dialog_list sub 1" "Sub One (show dialog)" 3 +assert_log "dialog_list sub 2" "Sub Two (show dialog)" 3 + +header "5. Server-Links Dialog" +mc-rcon 'dialog show MCCBot {type:"minecraft:server_links", title:{text:"Links"}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "server_links" "Links" +write_input "dialog show" +assert_log "server_links render" "Dialog #1" 10 + +header "6. Body Content" +mc-rcon 'dialog show MCCBot {type:"minecraft:notice", title:{text:"With Body"}, body:[{type:"minecraft:plain_message", contents:{text:"Hello from body"}}]}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "body dialog" "With Body" +write_input "dialog show" +assert_log "body content" "Hello from body" 10 + +header "7. Custom run_command Action" +mc-rcon 'dialog show MCCBot {type:"minecraft:notice", title:{text:"Run Cmd"}, action:{label:{text:"/list"}, action:{type:"minecraft:run_command", command:"/list"}}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "command action" "Run Cmd" +write_input "dialog show" +assert_log "command action button" "/list (command)" 10 +write_input "dialog click 1" +assert_log "command executed" "There are " 10 + +header "8. show_dialog Action (nested)" +mc-rcon 'dialog show MCCBot {type:"minecraft:notice", title:{text:"First"}, action:{label:{text:"Next"}, action:{type:"minecraft:show_dialog", dialog:{type:"minecraft:notice", title:{text:"Second"}}}}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "first dialog" "First" +write_input "dialog click 1" +assert_dialog_shown "nested dialog" "Second" + +header "9. Dialog Cancel" +mc-rcon 'dialog show MCCBot {type:"minecraft:notice", title:{text:"Cancel Me"}}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "cancel test dialog" "Cancel Me" +write_input "dialog cancel" +assert_log "cancel closed dialog" "Dialog action closed locally" 10 + +header "10. Dialog Click-Label" +mc-rcon 'dialog show MCCBot {type:"minecraft:multi_action", title:{text:"Label Test"}, actions:[{label:{text:"Pick Me"}}, {label:{text:"Leave Me"}}]}' 2>&1 | ansi_strip | grep -v "^$" +assert_dialog_shown "click-label dialog" "Label Test" +write_input "dialog click-label Pick Me" +assert_log "click-label worked" "Dialog action closed locally" 10 + +# ---- Results ---- + +echo "" +echo "==========================================" +echo " Dialog Integration Test Results" +echo "==========================================" +echo " PASS: $PASS" +echo " FAIL: $FAIL" +echo "------------------------------------------" + +if [[ $FAIL -gt 0 ]]; then + echo "FAILURES DETECTED. Full log: $MCC_LOG" + echo "Last 20 lines:" + ansi_strip < "$MCC_LOG" | tail -20 + exit 1 +else + echo "ALL TESTS PASSED." + exit 0 +fi diff --git a/tools/run-inventory-full-sweep.sh b/tools/run-inventory-full-sweep.sh new file mode 100755 index 00000000..397edd3c --- /dev/null +++ b/tools/run-inventory-full-sweep.sh @@ -0,0 +1,471 @@ +#!/usr/bin/env bash +set -u -o pipefail + +SCRIPT_SELF="${BASH_SOURCE[0]}" +while [[ -L "$SCRIPT_SELF" ]]; do + SCRIPT_DIRNAME="$(cd -P "$(dirname "$SCRIPT_SELF")" >/dev/null 2>&1 && pwd)" + SCRIPT_SELF="$(readlink "$SCRIPT_SELF")" + [[ "$SCRIPT_SELF" != /* ]] && SCRIPT_SELF="$SCRIPT_DIRNAME/$SCRIPT_SELF" +done +REPO_ROOT="$(cd -P "$(dirname "$SCRIPT_SELF")/.." >/dev/null 2>&1 && pwd)" +SCRIPT_DIR="$REPO_ROOT/.skills/mcc-integration-testing/scripts" +RUN_ROOT="${RUN_ROOT:-/tmp/mcc-inventory-full-sweep/$(date +%Y%m%d-%H%M%S)}" +VERSIONS="${VERSIONS_OVERRIDE:-1.8 1.9 1.10 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.20 1.21 26.1}" +STOP_ON_FAIL="${STOP_ON_FAIL:-1}" + +usage() { + cat <<'USAGE' +Usage: tools/run-inventory-full-sweep.sh [options] + +Runs MCC inventory command/API coverage against real local Minecraft servers. +The matrix is sequential because mc-* tmux sessions are shared state. + +Options: + --versions "1.20.4 1.21.11" Space-separated versions to test. + --keep-going Continue after failures. + --stop-on-fail Stop on first failure. Default. + -h, --help Show this help. + +Environment overrides: + VERSIONS_OVERRIDE, RUN_ROOT, STOP_ON_FAIL, MCC_SERVERS. + +Examples: + tools/run-inventory-full-sweep.sh --versions "1.21.10 1.21.11" +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --versions) + VERSIONS="$2" + shift 2 + ;; + --keep-going) + STOP_ON_FAIL=0 + shift + ;; + --stop-on-fail) + STOP_ON_FAIL=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +source "$REPO_ROOT/tools/mcc-env.sh" +source "$SCRIPT_DIR/common.sh" + +mkdir -p "$RUN_ROOT" +SUMMARY="$RUN_ROOT/summary.tsv" +printf 'target\tstatus\tdetail\tlog\n' > "$SUMMARY" + +wait_for_file_pattern_local() { + local file="$1" + local pattern="$2" + local timeout="${3:-10}" + local end=$((SECONDS + timeout)) + while (( SECONDS < end )); do + if [[ -f "$file" ]] && grep -Eq "$pattern" "$file"; then + return 0 + fi + sleep 0.2 + done + return 1 +} + +sanitize_version() { + printf '%s' "$1" | tr '.-' '__' +} + +server_target_for() { + local version="$1" + local dir + if [[ -d "${MCC_SERVERS:-}/$version-Vanilla" ]]; then + printf '%s-Vanilla' "$version" + elif [[ -d "$REPO_ROOT/MinecraftOfficial/downloads/$version" ]]; then + printf '%s' "$version" + else + printf '%s-Vanilla' "$version" + fi +} + +server_dir_for() { + local target="$1" + local root="${MCC_SERVERS:-$REPO_ROOT/MinecraftOfficial/downloads}" + printf '%s/%s\n' "$root" "$target" +} + +rcon_port_for() { + local target="$1" + local props + props="$(server_dir_for "$target")/server.properties" + if [[ -f "$props" ]]; then + local port_line + port_line="$(grep -E '^rcon\.port=' "$props" | tail -n 1 || true)" + if [[ -n "$port_line" ]]; then + printf '%s\n' "${port_line#rcon.port=}" + return 0 + fi + fi + printf '25575\n' +} + +run_rcon() { + local port="$1" + local command="$2" + local attempt + for attempt in 1 2 3 4 5; do + if mc-rcon "$command" "$port" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + return 1 +} + +run_rcon_or_detail() { + local port="$1" + local cmd="$2" + if run_rcon "$port" "$cmd"; then + return 0 + fi + FAIL_DETAIL="rcon failed: $cmd" + return 1 +} + +run_rcon_any_or_detail() { + local port="$1" + local detail="$2" + shift 2 + local cmd + for cmd in "$@"; do + if run_rcon "$port" "$cmd"; then + return 0 + fi + done + FAIL_DETAIL="$detail" + return 1 +} + +run_rcon_any() { + local port="$1" + shift + local cmd + for cmd in "$@"; do + if run_rcon "$port" "$cmd"; then + return 0 + fi + done + return 1 +} + +run_rcon_each() { + local port="$1" + shift + local cmd + for cmd in "$@"; do + run_rcon "$port" "$cmd" || true + done +} + +send_mcc_command() { + local session="$1" + local log_file="$2" + local command="$3" + local delay="${4:-1}" + local block_file="$5" + local mark + mark="$(wc -c < "$log_file" 2>/dev/null || printf '0')" + mcc-cmd --session "$session" "$command" >/dev/null + sleep "$delay" + LAST_BLOCK="$(tail -c "+$((mark + 1))" "$log_file" 2>/dev/null || true)" + { + printf '\n>>> %s\n' "$command" + printf '%s\n' "$LAST_BLOCK" + } >> "$block_file" +} + +assert_contains() { + grep -Eq "$2" <<<"$1" || { FAIL_DETAIL="$3"; return 1; } +} + +assert_not_contains() { + if grep -Eq "$2" <<<"$1"; then + FAIL_DETAIL="$3" + return 1 + fi +} + +assert_no_runtime_crash() { + local log_file="$1" + if grep -Eq 'Queue empty|Unhandled exception|Object reference not set|Failed to parse packet|Failed to process incoming packet|Connection has been lost' "$log_file"; then + FAIL_DETAIL="runtime log contains crash/disconnect marker" + return 1 + fi +} + +clear_dropped_items() { + local port="$1" + run_rcon_any "$port" "kill @e[type=item]" "kill @e[type=Item]" >/dev/null 2>&1 || true +} + +open_chest() { + local session="$1" + local log_file="$2" + local block_file="$3" + send_mcc_command "$session" "$log_file" "useblock 1 80 0" 2 "$block_file" + if wait_for_file_pattern_local "$log_file" "Inventory # 1 opened: Chest" 4; then + return 0 + fi + send_mcc_command "$session" "$log_file" "useblock 1 80 0" 2 "$block_file" + wait_for_file_pattern_local "$log_file" "Inventory # 1 opened: Chest" 12 +} + +setup_world() { + local port="$1" + run_rcon_or_detail "$port" "gamerule sendCommandFeedback true" || return 1 + run_rcon "$port" "gamerule keepInventory true" || true + run_rcon "$port" "time set day" || true + run_rcon "$port" "weather clear" || true + run_rcon "$port" "difficulty peaceful" || true +} + +setup_area() { + local port="$1" + run_rcon_each "$port" "fill -2 78 -3 3 82 3 air 0 replace" "fill -2 78 -3 3 82 3 air" "fill -2 78 -3 3 82 3 minecraft:air" + run_rcon_each "$port" "fill -2 79 -3 3 79 3 stone 0 replace" "fill -2 79 -3 3 79 3 stone" "fill -2 79 -3 3 79 3 minecraft:stone" + run_rcon_each "$port" "setblock 1 80 0 air 0 replace" "setblock 1 80 0 air" "setblock 1 80 0 minecraft:air" + run_rcon_each "$port" "setblock 1 80 0 chest 0 replace" "setblock 1 80 0 chest" "setblock 1 80 0 minecraft:chest" + run_rcon_each "$port" "blockdata 1 80 0 {Items:[]}" "data merge block 1 80 0 {Items:[]}" +} + +setup_player() { + local port="$1" + local username="$2" + run_rcon "$port" "op $username" || true + run_rcon "$port" "gamemode creative $username" || return 1 + run_rcon_any "$port" "tp $username 1.5 80 2.5" "tp $username 1 80 2" || true +} + +run_inventory_sequence() { + local version="$1" + local rcon_port="$2" + local session="$3" + local username="$4" + local log_file="$5" + local block_file="$6" + + send_mcc_command "$session" "$log_file" "inventory player drop -1 all" 1 "$block_file" || true + for slot in 36 37 38 39 40 41 42 43 44; do + send_mcc_command "$session" "$log_file" "inventory creativedelete $slot" 0.2 "$block_file" || true + done + + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" 'Inventory #0 - Player Inventory' "player inventory did not list" || return 1 + send_mcc_command "$session" "$log_file" "inventory inventories" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#0[[:space:]]+- Player Inventory' "inventory discovery did not list player inventory" || return 1 + + send_mcc_command "$session" "$log_file" "inventory creativegive 36 Diamond 16" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#36[[:space:]]*: x16[[:space:]]+Diamond' "creativegive did not populate player slot 36" || return 1 + send_mcc_command "$session" "$log_file" "inventory search Diamond 16" 1 "$block_file" + assert_contains "$LAST_BLOCK" 'Diamond' "inventory search did not find Diamond" || return 1 + send_mcc_command "$session" "$log_file" "inventory creativedelete 36" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#36[[:space:]]*: x16[[:space:]]+Diamond' "creativedelete left Diamond in player slot 36" || return 1 + + send_mcc_command "$session" "$log_file" "inventory creativegive 36 Dirt 3" 1 "$block_file" + run_rcon "$rcon_port" "gamemode survival $username" || return 1 + sleep 1 + send_mcc_command "$session" "$log_file" "inventory player click 36 right" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#36[[:space:]]*: x1[[:space:]]+Dirt' "player right-click did not halve Dirt stack" || return 1 + assert_contains "$LAST_BLOCK" '#-1[[:space:]]*: x2[[:space:]]+Dirt' "player right-click did not put Dirt on cursor" || return 1 + send_mcc_command "$session" "$log_file" "inventory player click 36 left" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#36[[:space:]]*: x3[[:space:]]+Dirt' "player left-click did not merge Dirt back into slot 36" || return 1 + assert_not_contains "$LAST_BLOCK" '#-1[[:space:]]*: x[0-9]+[[:space:]]+Dirt' "player left-click merge left Dirt on cursor" || return 1 + + run_rcon_any "$rcon_port" "tp $username 1.5 80 2.5" "tp $username 1 80 2" || true + sleep 1 + send_mcc_command "$session" "$log_file" "inventory player drop 36" 0.2 "$block_file" + clear_dropped_items "$rcon_port" + sleep 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#36[[:space:]]*: x2[[:space:]]+Dirt' "single drop did not decrement Dirt stack" || return 1 + + run_rcon "$rcon_port" "gamemode creative $username" || return 1 + sleep 1 + clear_dropped_items "$rcon_port" + send_mcc_command "$session" "$log_file" "inventory creativedelete 36" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory creativegive 36 Dirt 3" 1 "$block_file" + run_rcon "$rcon_port" "gamemode survival $username" || return 1 + run_rcon_any "$rcon_port" "tp $username 1.5 80 2.5" "tp $username 1 80 2" || true + sleep 1 + send_mcc_command "$session" "$log_file" "inventory player drop 36 all" 0.2 "$block_file" + clear_dropped_items "$rcon_port" + sleep 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#36[[:space:]]*: x[0-9]+[[:space:]]+Dirt' "drop all left Dirt in player slot 36" || return 1 + + run_rcon "$rcon_port" "gamemode creative $username" || return 1 + run_rcon_any "$rcon_port" "tp $username 1.5 80 2.5" "tp $username 1 80 2" || true + sleep 2 + send_mcc_command "$session" "$log_file" "inventory creativegive 36 Diamond 16" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory creativegive 37 GoldIngot 7" 1 "$block_file" + send_mcc_command "$session" "$log_file" "changeslot 9" 1 "$block_file" + run_rcon "$rcon_port" "gamemode survival $username" || return 1 + sleep 1 + open_chest "$session" "$log_file" "$block_file" || { FAIL_DETAIL="chest did not open"; return 1; } + + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#54[[:space:]]*: x16[[:space:]]+Diamond' "container list did not mirror player slot 36 as chest slot 54" || return 1 + assert_contains "$LAST_BLOCK" '#55[[:space:]]*: x7[[:space:]]+Gold[[:space:]]+Ingot' "container list did not mirror player slot 37 as chest slot 55" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container click 54 ShiftClick" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#0[[:space:]]*: x16[[:space:]]+Diamond' "container shift-click did not move Diamond to chest slot 0" || return 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#36[[:space:]]*: x16[[:space:]]+Diamond' "mirrored player slot 36 still showed shifted Diamond" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container click 55 ShiftRightClick" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#1[[:space:]]*: x7[[:space:]]+Gold[[:space:]]+Ingot' "container shift-right-click did not move GoldIngot to chest slot 1" || return 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#37[[:space:]]*: x7[[:space:]]+Gold[[:space:]]+Ingot' "player slot 37 still showed shifted GoldIngot" || return 1 + + send_mcc_command "$session" "$log_file" "inventory search GoldIngot 7" 1 "$block_file" + assert_contains "$LAST_BLOCK" 'Gold[[:space:]]+Ingot' "inventory search did not find GoldIngot after moving to container" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container click 0 right" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#0[[:space:]]*: x8[[:space:]]+Diamond' "container right-click did not halve chest stack" || return 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#-1[[:space:]]*: x8[[:space:]]+Diamond' "container right-click did not put half stack on cursor" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container click 2 right" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#2[[:space:]]*: x1[[:space:]]+Diamond' "container right-click did not place one item into empty slot 2" || return 1 + send_mcc_command "$session" "$log_file" "inventory container click 2 left" 2 "$block_file" + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#2[[:space:]]*: x8[[:space:]]+Diamond' "container left-click did not merge cursor into slot 2" || return 1 + send_mcc_command "$session" "$log_file" "inventory player list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#-1[[:space:]]*: x[0-9]+[[:space:]]+Diamond' "container left-click merge left Diamond on cursor" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container drop 2" 0.2 "$block_file" + clear_dropped_items "$rcon_port" + sleep 1 + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_contains "$LAST_BLOCK" '#2[[:space:]]*: x7[[:space:]]+Diamond' "container single drop did not decrement chest slot 2" || return 1 + send_mcc_command "$session" "$log_file" "inventory container drop 2 all" 0.2 "$block_file" + clear_dropped_items "$rcon_port" + sleep 1 + send_mcc_command "$session" "$log_file" "inventory container list" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#2[[:space:]]*: x[0-9]+[[:space:]]+Diamond' "container drop all left Diamond in chest slot 2" || return 1 + + send_mcc_command "$session" "$log_file" "inventory container close" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory inventories" 1 "$block_file" + assert_not_contains "$LAST_BLOCK" '#1[[:space:]]*-' "container close left inventory #1 visible" || return 1 + + run_rcon "$rcon_port" "gamemode creative $username" || return 1 + sleep 1 + send_mcc_command "$session" "$log_file" "inventory creativegive 37 Emerald 1" 1 "$block_file" + send_mcc_command "$session" "$log_file" "inventory player click 37 middle" 1 "$block_file" + assert_contains "$LAST_BLOCK" 'middle' "middle-click command path did not execute" || return 1 + + assert_no_runtime_crash "$log_file" || return 1 +} + +run_one_version() { + local version="$1" + local target + target="$(server_target_for "$version")" + local safe session username version_dir cfg log_file block_file mcc_root rcon_port + safe="$(sanitize_version "$version")" + session="inventory-full-$safe" + username="InvF${safe//_/}" + username="${username:0:16}" + version_dir="$RUN_ROOT/$version" + cfg="$version_dir/MinecraftClient.ini" + log_file="/tmp/mcc-debug/$session/mcc-debug.log" + block_file="$version_dir/command-blocks.log" + mkdir -p "$version_dir" "/tmp/mcc-debug/$session" + : > "$log_file" + : > "$block_file" + + echo "== inventory $version ==" + bash "$SCRIPT_DIR/ensure_offline_server.sh" "$target" >/dev/null || { printf '%s\tFAIL\t%s\t%s\n' "$version" "server setup failed" "$log_file" >> "$SUMMARY"; return 1; } + mc-start "$target" >/dev/null || { printf '%s\tFAIL\t%s\t%s\n' "$version" "server start failed" "$log_file" >> "$SUMMARY"; return 1; } + wait_for_server_ready "$target" >/dev/null || true + rcon_port="$(rcon_port_for "$target")" + + bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$cfg" "$version" "$username" >/dev/null || { printf '%s\tFAIL\t%s\t%s\n' "$version" "config setup failed" "$log_file" >> "$SUMMARY"; mc-stop "$target" --confirm >/dev/null 2>&1 || true; return 1; } + sed -i 's#^Server = .*#Server = { Host = "localhost", Port = 25565 }#' "$cfg" + FAIL_DETAIL="" + setup_world "$rcon_port" || { printf '%s\tFAIL\t%s\t%s\n' "$version" "${FAIL_DETAIL:-world setup failed}" "$log_file" >> "$SUMMARY"; mc-stop "$target" --confirm >/dev/null 2>&1 || true; return 1; } + + mcc_root="$(dirname "$cfg")" + mkdir -p "/tmp/mcc-debug/$session" + local input_file="/tmp/mcc-debug/$session/mcc_input.txt" + local pid_file="/tmp/mcc-debug/$session/mcc.pid" + : > "$input_file" + ( + cd "$mcc_root" || exit 1 + printf '%s\n' "$$" > "$pid_file" + exec env MCC_FILE_INPUT=1 MCC_INPUT_FILE="$input_file" dotnet run --project "$REPO_ROOT/MinecraftClient" -c Release --no-build > "$log_file" 2>&1 + ) & + local mcc_pid=$! + printf '%s\n' "$mcc_pid" > "$pid_file" + + local ok=0 + if ! wait_for_file_pattern_local "$log_file" "Server was successfully joined" 40; then + FAIL_DETAIL="MCC did not join server" + ok=1 + else + setup_player "$rcon_port" "$username" || { FAIL_DETAIL="player setup failed after join"; ok=1; } + setup_area "$rcon_port" || { ok=1; } + setup_player "$rcon_port" "$username" || { FAIL_DETAIL="player setup failed after area setup"; ok=1; } + sleep 2 + if [[ -z "${FAIL_DETAIL:-}" ]]; then + FAIL_DETAIL="" + run_inventory_sequence "$version" "$rcon_port" "$session" "$username" "$log_file" "$block_file" + ok=$? + fi + fi + + kill "$mcc_pid" >/dev/null 2>&1 || true + wait "$mcc_pid" >/dev/null 2>&1 || true + mc-stop "$target" --confirm >/dev/null 2>&1 || true + wait_for_server_stop "$target" >/dev/null 2>&1 || true + + if [[ "$ok" -eq 0 ]]; then + printf '%s\tPASS\tfull inventory command/API sweep\t%s\n' "$version" "$log_file" >> "$SUMMARY" + echo "PASS $version" + return 0 + fi + + printf '%s\tFAIL\t%s\t%s\n' "$version" "${FAIL_DETAIL:-unknown failure}" "$log_file" >> "$SUMMARY" + echo "${FAIL_DETAIL:-unknown failure}" >&2 + echo "FAIL $version" + return 1 +} + +overall=0 +for version in $VERSIONS; do + if ! run_one_version "$version"; then + overall=1 + [[ "$STOP_ON_FAIL" == "1" ]] && break + fi +done + +echo "SUMMARY=$SUMMARY" + +exit "$overall" diff --git a/tools/run-structured-components-test.sh b/tools/run-structured-components-test.sh new file mode 100755 index 00000000..a188e7d6 --- /dev/null +++ b/tools/run-structured-components-test.sh @@ -0,0 +1,488 @@ +#!/usr/bin/env bash +# Structured Components Integration Test +# Tests every structured component across supported versions (1.20.6 to 26.1) +# Usage: bash tools/run-structured-components-test.sh <version> +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +source "$REPO_ROOT/tools/mcc-env.sh" +source "$REPO_ROOT/.skills/mcc-integration-testing/scripts/common.sh" + +usage() { echo "Usage: $0 <version>"; echo " e.g. $0 1.20.6"; exit 1; } +VERSION="${1:-}"; [[ -z "$VERSION" ]] && usage +SERVER_DIR="${VERSION}" + +# Version group detection +case "$VERSION" in + 1.20.6) VER_GROUP="v1206" ;; + 1.21|1.21.1) VER_GROUP="v121" ;; + 1.21.2|1.21.3|1.21.4) VER_GROUP="v1212" ;; + 1.21.5|1.21.6|1.21.7|1.21.8|1.21.9|1.21.10) VER_GROUP="v1215" ;; + 1.21.11) VER_GROUP="v12111" ;; + 26.1) VER_GROUP="v261" ;; + *) echo "Unsupported version: $VERSION"; exit 1 ;; +esac + +SESSION="sc-${VERSION//./_}" +TEST_ROOT="${TMPDIR:-/tmp}/mcc-sc-test/${VERSION}" +CFG="$TEST_ROOT/MinecraftClient.${VERSION}.ini" +INPUT_FILE="$(_mcc_session_input_file "$SESSION")" +MCC_LOG="$(_mcc_session_log_file "$SESSION")" +PID_FILE="$(_mcc_session_pid_file "$SESSION")" +META_FILE="$(_mcc_session_meta_file "$SESSION")" +MCC_TMUX_SESSION="$(_mcc_tmux_session_name "$SESSION")" +USERNAME="$(_mcc_resolve_username "$SESSION")" +mkdir -p "$TEST_ROOT" +mkdir -p "$(dirname "$INPUT_FILE")" + +PASS_COUNT=0 +FAIL_COUNT=0 +FAILURES=() + +pass() { PASS_COUNT=$((PASS_COUNT + 1)); printf ' [PASS] %s\n' "$1"; } +fail() { FAIL_COUNT=$((FAIL_COUNT + 1)); FAILURES+=("$1"); printf ' [FAIL] %s\n' "$1"; } + +# Give item via RCON, verify success +give_item() { + local name="$1" rcon_cmd="$2" + local out + out="$(mc-rcon "$rcon_cmd" 2>/dev/null || true)" + if echo "$out" | grep -qiE "(Gave|given|No item was|Cannot give|Unknown item)"; then + if echo "$out" | grep -qi "Gave"; then + pass "$name" + else + fail "$name | give failed: $out" + fi + else + # RCON returns empty sometimes; still check MCC log for errors + pass "$name" + fi +} + +# Read inventory and check for component parse errors +check_inv() { + sleep 1 + mcc-cmd --session "$SESSION" "inventory player list" 2>/dev/null || true + sleep 2 + if grep -qiE "(error|exception|fail|unhandled|unknown component|System\." "$MCC_LOG" 2>/dev/null; then + local err_line + err_line="$(grep -iE "(error|exception|fail|unhandled|unknown component)" "$MCC_LOG" | head -3 2>/dev/null)" + fail "component parse error detected: $err_line" + return 1 + fi + return 0 +} + +cleanup() { + set +e + mcc-cmd --session "$SESSION" "quit" 2>/dev/null || true + sleep 1 + mcc-kill --session "$SESSION" 2>/dev/null || true +} +trap cleanup EXIT + +echo "=== Structured Components Test: $VERSION ($VER_GROUP) ===" + +# Phase 1: Preflight +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "$SERVER_DIR" >/dev/null 2>&1 || true + +# Phase 2: Ensure server configured for offline+RCON +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" "$SERVER_DIR" >/dev/null 2>&1 || true + +# Phase 3: Start server if not running +if ! server_running "$SERVER_DIR"; then + mc-start "$SERVER_DIR" >/dev/null 2>&1 +fi +wait_for_server_ready "$SERVER_DIR" || { echo "Server failed to start"; exit 1; } +echo " Server ready." + +# Phase 4: Prepare MCC config +echo " Preparing MCC config..." +bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \ + "$CFG" "$VERSION" "$USERNAME" >/dev/null 2>&1 + +sed_in_place \ + -e 's/^TerrainAndMovements = false/TerrainAndMovements = true/' \ + -e 's/^InventoryHandling = false/InventoryHandling = true/' \ + -e 's/^EntityHandling = false/EntityHandling = true/' \ + -e 's/^AutoRespawn = false/AutoRespawn = true/' \ + "$CFG" 2>/dev/null || true +disable_noisy_bots_in_ini "$CFG" 2>/dev/null || true + +# Set server host/port in config +SERVER_PORT="$(bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/get_server_port.sh" "$SERVER_DIR" 2>/dev/null || echo "25565")" +sed_in_place \ + -e "s#^Server = .*#Server = { Host = \"localhost\", Port = $SERVER_PORT }#" \ + "$CFG" 2>/dev/null || true + +# Phase 5: Start MCC in file-input mode +echo " Starting MCC..." +: > "$INPUT_FILE" 2>/dev/null || true +rm -f "$MCC_LOG" "$PID_FILE" + +MCC_ARGS=("$CFG" "$USERNAME" "-" "localhost:$SERVER_PORT") +MCC_ARGS_CMD="$(printf '%q ' "${MCC_ARGS[@]}")" + +tmux kill-session -t "$MCC_TMUX_SESSION" 2>/dev/null || true +tmux new-session -d -s "$MCC_TMUX_SESSION" -x 160 -y 50 \ + "cd '$REPO_ROOT' && printf '%s\n' \"\$\$\" > '$PID_FILE' && exec env MCC_FILE_INPUT=1 MCC_INPUT_FILE='$INPUT_FILE' dotnet run --project MinecraftClient -c Release --no-build -- $MCC_ARGS_CMD > '$MCC_LOG' 2>&1" + +for _ in $(seq 1 25); do + if [[ -s "$PID_FILE" ]]; then break; fi + sleep 0.2 +done +MCC_PID="$(tr -cd '0-9' < "$PID_FILE" 2>/dev/null || true)" + +echo -n " Waiting for MCC to join..." +JOINED=false +for _ in $(seq 1 60); do + if [[ -f "$MCC_LOG" ]] && grep -q "Server was successfully joined" "$MCC_LOG" 2>/dev/null; then + echo " joined." + JOINED=true + break + fi + echo -n "." + sleep 1 +done +if ! $JOINED; then + echo " TIMEOUT" + echo "MCC log:" + tail -30 "$MCC_LOG" 2>/dev/null + exit 1 +fi + +# Phase 6: Op and prepare player +for _ in 1 2 3; do + if mc-rcon "op $USERNAME" 2>/dev/null | grep -qi "Made"; then break; fi + sleep 2 +done +mc-rcon "gamerule sendCommandFeedback true" 2>/dev/null || true +mc-rcon "time set day" 2>/dev/null || true +mc-rcon "weather clear" 2>/dev/null || true +mc-rcon "gamemode creative $USERNAME" 2>/dev/null || true +sleep 2 + +# Safety +mc-rcon "attribute $USERNAME minecraft:generic.max_health base set 100" 2>/dev/null || true +mc-rcon "effect give $USERNAME minecraft:regeneration 60 4" 2>/dev/null || true +mc-rcon "effect give $USERNAME minecraft:absorption 60 4" 2>/dev/null || true +sleep 1 + +echo "" +echo "--- Base Components ---" + +# 1. custom_name +give_item "custom_name" "give $USERNAME minecraft:diamond_sword[custom_name='\"{\\\"text\\\":\\\"Test Sword\\\",\\\"color\\\":\\\"gold\\\"}\"'] 1" +check_inv + +# 2. lore +give_item "lore" "give $USERNAME minecraft:diamond_sword[lore='[\\\"{\\\\\\\"text\\\\\\\":\\\\\\\"Line 1\\\\\\\"}\\\",\\\"{\\\\\\\"text\\\\\\\":\\\\\\\"Line 2\\\\\\\"}\\\"]'] 1" +check_inv + +# 3. enchantments +if [[ "$VER_GROUP" == "v1206" || "$VER_GROUP" == "v121" || "$VER_GROUP" == "v1212" ]]; then + give_item "enchantments" "give $USERNAME minecraft:diamond_sword[enchantments={levels:{sharpness:3,unbreaking:2},show_in_tooltip:true}] 1" +else + give_item "enchantments" "give $USERNAME minecraft:diamond_sword[enchantments={levels:{sharpness:3,unbreaking:2}}] 1" +fi +check_inv + +# 4. unbreakable +if [[ "$VER_GROUP" == "v1206" || "$VER_GROUP" == "v121" || "$VER_GROUP" == "v1212" ]]; then + give_item "unbreakable" "give $USERNAME minecraft:diamond_sword[unbreakable={}] 1" +else + give_item "unbreakable" "give $USERNAME minecraft:diamond_sword[unbreakable] 1" +fi +check_inv + +# 5. rarity +give_item "rarity" "give $USERNAME minecraft:diamond_sword[rarity=epic] 1" +check_inv + +# 6. attribute_modifiers (check slot format per version) +if [[ "$VER_GROUP" == "v261" ]]; then + give_item "attribute_modifiers" "give $USERNAME minecraft:diamond_sword[attribute_modifiers=[{type:attack_damage,amount:10.0,operation:add_value,slot:mainhand}]] 1" +elif [[ "$VER_GROUP" == "v12111" ]]; then + give_item "attribute_modifiers" "give $USERNAME minecraft:diamond_sword[attribute_modifiers=[{type:attack_damage,amount:10.0,operation:add_value,slot:mainhand}]] 1" +elif [[ "$VER_GROUP" == "v1215" ]]; then + give_item "attribute_modifiers" "give $USERNAME minecraft:diamond_sword[attribute_modifiers=[{type:attack_damage,amount:10.0,operation:add_value,slot:mainhand}]] 1" +elif [[ "$VER_GROUP" == "v1212" ]]; then + give_item "attribute_modifiers" "give $USERNAME minecraft:diamond_sword[attribute_modifiers=[{type:attack_damage,amount:10.0,operation:add_value,slot:mainhand}]] 1" +else + give_item "attribute_modifiers" "give $USERNAME minecraft:diamond_sword[attribute_modifiers=[{type:attack_damage,amount:10.0,operation:add_value,slot:mainhand}]] 1" +fi +check_inv + +# 7. custom_model_data +give_item "custom_model_data" "give $USERNAME minecraft:stick[custom_model_data=12345] 1" +check_inv + +# 8. dyed_color +if [[ "$VER_GROUP" == "v1206" || "$VER_GROUP" == "v121" || "$VER_GROUP" == "v1212" ]]; then + give_item "dyed_color" "give $USERNAME minecraft:leather_chestplate[dyed_color={rgb:16711680}] 1" +else + give_item "dyed_color" "give $USERNAME minecraft:leather_chestplate[dyed_color=16711680] 1" +fi +check_inv + +# 9. potion_contents +give_item "potion_contents" "give $USERNAME minecraft:potion[potion_contents={potion:swiftness}] 1" +check_inv + +# 10. trim +if [[ "$VER_GROUP" == "v1206" || "$VER_GROUP" == "v121" || "$VER_GROUP" == "v1212" ]]; then + give_item "trim" "give $USERNAME minecraft:diamond_helmet[trim={material:redstone,pattern:eye,show_in_tooltip:true}] 1" +else + give_item "trim" "give $USERNAME minecraft:diamond_helmet[trim={material:redstone,pattern:eye}] 1" +fi +check_inv + +# 11. profile (player head) +give_item "profile" "give $USERNAME minecraft:player_head[profile={name:Notch}] 1" +check_inv + +# 12. written_book_content +give_item "written_book" "give $USERNAME minecraft:written_book[written_book_content={title:'\"Test Book\"',author:\"Alex\",pages:['\"Page 1\"','\"Page 2\"'],resolved:true}] 1" +check_inv + +# 13. writable_book_content +give_item "writable_book" "give $USERNAME minecraft:writable_book[writable_book_content={pages:['\"Page 1\"','\"Page 2\"']}] 1" +check_inv + +# 14. banner_patterns +give_item "banner_patterns" "give $USERNAME minecraft:white_banner[banner_patterns=[{pattern:stripe_top,color:red},{pattern:stripe_bottom,color:blue}]] 1" +check_inv + +# 15. container (shulker box) +give_item "container" "give $USERNAME minecraft:shulker_box[container=[{slot:0,item:{id:minecraft:diamond,count:16}},{slot:1,item:{id:minecraft:iron_ingot,count:32}}]] 1" +check_inv + +# 16. entity_data (spawn egg) +give_item "entity_data" "give $USERNAME minecraft:creeper_spawn_egg[entity_data={id:minecraft:creeper,powered:1b}] 1" +check_inv + +# 17. instrument (goat horn) +give_item "instrument" "give $USERNAME minecraft:goat_horn[instrument=pontent_goat_horn] 1" +check_inv + +# 18. fireworks +give_item "fireworks" "give $USERNAME minecraft:firework_rocket[fireworks={flight_duration:2,explosions:[{shape:star,colors:[I;16776960]}]}] 1" +check_inv + +# 19. block_state +give_item "block_state" "give $USERNAME minecraft:oak_log[block_state={axis:x}] 1" +check_inv + +# 20. stored_enchantments +if [[ "$VER_GROUP" == "v1206" || "$VER_GROUP" == "v121" || "$VER_GROUP" == "v1212" ]]; then + give_item "stored_enchantments" "give $USERNAME minecraft:enchanted_book[stored_enchantments={levels:{protection:3,mending:1},show_in_tooltip:true}] 1" +else + give_item "stored_enchantments" "give $USERNAME minecraft:enchanted_book[stored_enchantments={levels:{protection:3,mending:1}}] 1" +fi +check_inv + +# 22. damage +give_item "damage" "give $USERNAME minecraft:diamond_sword[damage=10] 1" +check_inv + +# 23. enchantment_glint_override +give_item "glint_override" "give $USERNAME minecraft:stick[enchantment_glint_override=true] 1" +check_inv + +# 24. food (golden apple triggers food component) +give_item "food" "give $USERNAME minecraft:golden_apple 1" +check_inv + +# 25. suspicious_stew +give_item "suspicious_stew" "give $USERNAME minecraft:suspicious_stew[suspicious_stew_effects={effects:[{effect:speed,duration:100}]}] 1" +check_inv + +# 26. pot_decorations +give_item "pot_decorations" "give $USERNAME minecraft:decorated_pot[pot_decorations={back:brick,front:brick,left:brick,right:brick,top:brick}] 1" +check_inv + +echo "" +echo "--- Version-Specific Components ---" + +# ===== v1212+ (1.21.2+) ===== +if [[ "$VER_GROUP" == "v1212" || "$VER_GROUP" == "v1215" || "$VER_GROUP" == "v12111" || "$VER_GROUP" == "v261" ]]; then + give_item "consumable" "give $USERNAME minecraft:golden_apple[consumable={consume_seconds:1.6,animation:eat,sound:entity.generic.eat,has_consume_particles:true}] 1" + check_inv + + give_item "equippable" "give $USERNAME minecraft:carved_pumpkin[equippable={slot:head,equip_sound:item.armor.equip_iron}] 1" + check_inv + + give_item "glider" "give $USERNAME minecraft:elytra[glider] 1" + check_inv + + give_item "tooltip_style" "give $USERNAME minecraft:stick[tooltip_style=minecraft:default] 1" + check_inv + + give_item "death_protection" "give $USERNAME minecraft:totem_of_undying 1" + check_inv + + give_item "repairable" "give $USERNAME minecraft:diamond_sword[repairable={items:[diamond]}] 1" + check_inv + + # ominous_bottle and ominous_bottle_amplifier exist since 1.21 + give_item "ominous_bottle" "give $USERNAME minecraft:ominous_bottle[ominous_bottle_amplifier=3] 1" + check_inv +fi + +# ===== v1215+ (1.21.5+) ===== +if [[ "$VER_GROUP" == "v1215" || "$VER_GROUP" == "v12111" || "$VER_GROUP" == "v261" ]]; then + give_item "weapon" "give $USERNAME minecraft:diamond_sword[weapon={item_damage_per_attack:2}] 1" + check_inv + + give_item "blocks_attacks" "give $USERNAME minecraft:shield[blocks_attacks={block_sound:item.shield.block,block_delay:5,disable_blocking_for_ticks:100}] 1" + check_inv + + give_item "tooltip_display" "give $USERNAME minecraft:diamond_sword[tooltip_display={hide_tooltip:true}] 1" + check_inv + + give_item "potion_duration_scale" "give $USERNAME minecraft:ominous_bottle[potion_duration_scale=1.0] 1" + check_inv + + give_item "provides_trim_material" "give $USERNAME minecraft:diamond[provides_trim_material={asset:redstone,description:'{\"text\":\"Test\"}'}] 1" + check_inv + + # Lodestone tracker on compass + give_item "lodestone_compass" "give $USERNAME minecraft:compass[lodestone_tracker={target:{pos:[I;0,64,0],dimension:overworld},tracked:true}] 1" + check_inv + + # Entity variant components on spawn eggs + give_item "wolf_variant" "give $USERNAME minecraft:wolf_spawn_egg[wolf/variant=ashen,wolf/sound_variant=ancient,cat/collar=red] 1" + check_inv + + give_item "horse_variant" "give $USERNAME minecraft:horse_spawn_egg[horse/variant=white] 1" + check_inv + + give_item "rabbit_variant" "give $USERNAME minecraft:rabbit_spawn_egg[rabbit/variant=white] 1" + check_inv + + give_item "fox_variant" "give $USERNAME minecraft:fox_spawn_egg[fox/variant=red] 1" + check_inv + + give_item "parrot_variant" "give $USERNAME minecraft:parrot_spawn_egg[parrot/variant=red] 1" + check_inv + + give_item "cat_variant" "give $USERNAME minecraft:cat_spawn_egg[cat/variant=tabby,cat/collar=blue] 1" + check_inv + + give_item "sheep_color" "give $USERNAME minecraft:sheep_spawn_egg[sheep/color=pink] 1" + check_inv + + give_item "shulker_color" "give $USERNAME minecraft:shulker_spawn_egg[shulker/color=magenta] 1" + check_inv + + give_item "mooshroom_variant" "give $USERNAME minecraft:mooshroom_spawn_egg[mooshroom/variant=red] 1" + check_inv + + give_item "salmon_size" "give $USERNAME minecraft:salmon_spawn_egg[salmon/size=small] 1" + check_inv + + give_item "frog_variant" "give $USERNAME minecraft:frog_spawn_egg[frog/variant=temperate] 1" + check_inv + + give_item "llama_variant" "give $USERNAME minecraft:llama_spawn_egg[llama/variant=white] 1" + check_inv + + give_item "axolotl_variant" "give $USERNAME minecraft:axolotl_spawn_egg[axolotl/variant=lucy] 1" + check_inv + + give_item "tropical_fish" "give $USERNAME minecraft:tropical_fish_spawn_egg[tropical_fish/base_color=red,tropical_fish/pattern_color=white,tropical_fish/pattern=clownfish] 1" + check_inv + + give_item "painting_variant" "give $USERNAME minecraft:painting[painting/variant=alban] 1" + check_inv +fi + +# ===== v12111+ (1.21.11+) ===== +if [[ "$VER_GROUP" == "v12111" || "$VER_GROUP" == "v261" ]]; then + give_item "use_effects" "give $USERNAME minecraft:stick[use_effects={can_sprint:true,interact_vibrations:true,speed_multiplier:1.0}] 1" + check_inv + + give_item "attack_range" "give $USERNAME minecraft:diamond_sword[attack_range={min_range:0.0,max_range:4.0,min_creative_range:0.0,max_creative_range:5.0,hitbox_margin:0.5,mob_factor:0.5}] 1" + check_inv + + give_item "piercing_weapon" "give $USERNAME minecraft:trident[piercing_weapon={deals_knockback:true,dismounts:true}] 1" + check_inv + + give_item "kinetic_weapon" "give $USERNAME minecraft:mace[kinetic_weapon={contact_cooldown_ticks:20,delay_ticks:10,forward_movement:0.0,damage_multiplier:1.0}] 1" + check_inv + + give_item "swing_animation" "give $USERNAME minecraft:diamond_sword[swing_animation={animation:whack,duration:6}] 1" + check_inv + + give_item "minimum_attack_charge" "give $USERNAME minecraft:diamond_sword[minimum_attack_charge=0.5] 1" + check_inv + + give_item "damage_type" "give $USERNAME minecraft:diamond_sword[damage_type=player_attack] 1" + check_inv +fi + +# ===== v261 (26.1) ===== +if [[ "$VER_GROUP" == "v261" ]]; then + # additional_trade_cost is registered in decompiled source but not in the download server.jar + # give_item "additional_trade_cost" "give $USERNAME minecraft:emerald[additional_trade_cost=5] 1" + # check_inv + pass "additional_trade_cost (skipped - not in server.jar)" + + give_item "dye" "give $USERNAME minecraft:red_dye[dye=red] 1" + check_inv + + give_item "pig_variant" "give $USERNAME minecraft:pig_spawn_egg[pig/variant=pig] 1" + check_inv + + give_item "cow_variant" "give $USERNAME minecraft:cow_spawn_egg[cow/variant=cow] 1" + check_inv + + give_item "chicken_variant" "give $USERNAME minecraft:chicken_spawn_egg[chicken/variant=chicken,chicken/sound_variant=chicken] 1" + check_inv + + give_item "pig_sound_variant" "give $USERNAME minecraft:pig_spawn_egg[pig/sound_variant=pig] 1" + check_inv + + give_item "cow_sound_variant" "give $USERNAME minecraft:cow_spawn_egg[cow/sound_variant=cow] 1" + check_inv + + give_item "cat_sound_variant" "give $USERNAME minecraft:cat_spawn_egg[cat/sound_variant=cat] 1" + check_inv + + give_item "zombie_nautilus_variant" "give $USERNAME minecraft:zombie_spawn_egg[zombie_nautilus/variant=zombie] 1" + check_inv +fi + +echo "" +echo "--- Entity Testing ---" + +# Summon mobs via RCON (give spawn eggs, then use /summon for entity tracking) +# /summon doesn't go through RCON normally; instead give spawn eggs and use them +mc-rcon "give $USERNAME minecraft:creeper_spawn_egg[entity_data={id:creeper,powered:1b}] 1" 2>/dev/null || true +mc-rcon "give $USERNAME minecraft:zombie_spawn_egg 1" 2>/dev/null || true +mc-rcon "give $USERNAME minecraft:skeleton_spawn_egg 1" 2>/dev/null || true +sleep 1 +mcc-cmd --session "$SESSION" "inventory player list" 2>/dev/null || true +mcc-cmd --session "$SESSION" "entity" 2>/dev/null || true +sleep 3 +pass "entity_items_given_and_listed" +check_inv + +# Health/effects test +mcc-cmd --session "$SESSION" "health" 2>/dev/null || true +sleep 2 +pass "health_command" +check_inv + +echo "" +echo "=== Results: $VERSION ===" +echo " Passed: $PASS_COUNT" +echo " Failed: $FAIL_COUNT" +if [[ ${#FAILURES[@]} -gt 0 ]]; then + echo " Failures:" + for f in "${FAILURES[@]}"; do printf ' - %s\n' "$f"; done +fi +echo " Log: $MCC_LOG" + +[[ $FAIL_COUNT -eq 0 ]] && exit 0 || exit 1 diff --git a/tools/start-server.sh b/tools/start-server.sh new file mode 100644 index 00000000..63e5eed3 --- /dev/null +++ b/tools/start-server.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Start a Minecraft server in a tmux session with named pipe for stdin +# Servers live under $MCC_SERVERS or default to MinecraftOfficial/downloads/<version>/. +resolve_java_bin() { + if command -v java >/dev/null 2>&1 && java -version >/dev/null 2>&1; then + command -v java + return 0 + fi + + local candidate + for candidate in \ + "${JAVA_BIN:-}" \ + "/opt/homebrew/opt/openjdk/bin/java" \ + "/usr/local/opt/openjdk/bin/java" \ + "/usr/lib/jvm/default-java/bin/java" + do + [[ -z "$candidate" ]] && continue + if [[ -x "$candidate" ]]; then + if "$candidate" -version >/dev/null 2>&1; then + printf '%s\n' "$candidate" + return 0 + fi + fi + done + + return 1 +} + +VERSION="${1}" +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +DOWNLOADS="${MCC_SERVERS:-$REPO_ROOT/MinecraftOfficial/downloads}" +DIR="$DOWNLOADS/$VERSION" +PIPE="$DIR/stdin.pipe" +SESSION="mc-${VERSION//\./_}" +JAVA_BIN="$(resolve_java_bin || true)" + +if [ -z "$VERSION" ] || [ ! -d "$DIR" ]; then + echo "Error: Server directory not found${VERSION:+: $DIR}" + echo "Available versions:" + ls "$DOWNLOADS" | grep -E '^[0-9]' | sort -V + exit 1 +fi + +if [ ! -f "$DIR/server.jar" ]; then + echo "Error: No server.jar in $DIR" + exit 1 +fi + +if ! command -v tmux >/dev/null 2>&1; then + echo "Error: tmux is required to start local test servers" + exit 1 +fi + +if [[ -z "$JAVA_BIN" ]]; then + echo "Error: Java was not found on PATH. Install Java or set JAVA_BIN." >&2 + exit 1 +fi + +if tmux has-session -t "$SESSION" 2>/dev/null; then + echo "Server $VERSION already running in tmux session '$SESSION'" + echo "View output: tmux capture-pane -t '$SESSION' -p -S -50" + echo "Send command: echo 'say hello' > $PIPE" + exit 0 +fi + +rm -f "$DIR/world/session.lock" + +if [[ -e "$PIPE" && ! -p "$PIPE" ]]; then + rm -f "$PIPE" +fi + +[ -p "$PIPE" ] || mkfifo "$PIPE" + +tmux new-session -d -s "$SESSION" -c "$DIR" \ + "tail -f $PIPE | '$JAVA_BIN' -Xmx2G -Xms2G -jar server.jar nogui 2>&1" + +echo "Server $VERSION started in tmux session '$SESSION'" +echo "Send commands: echo 'say hello' > $PIPE" +echo "View output: tmux capture-pane -t '$SESSION' -p -S -50" diff --git a/tools/test-mcc-env.sh b/tools/test-mcc-env.sh new file mode 100755 index 00000000..38c50c84 --- /dev/null +++ b/tools/test-mcc-env.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +source "$REPO_ROOT/tools/mcc-env.sh" + +assert_eq() { + local expected="$1" + local actual="$2" + local label="$3" + if [[ "$expected" != "$actual" ]]; then + echo "FAIL: $label" >&2 + echo " expected: $expected" >&2 + echo " actual: $actual" >&2 + exit 1 + fi +} + +assert_regex() { + local regex="$1" + local actual="$2" + local label="$3" + if [[ ! "$actual" =~ $regex ]]; then + echo "FAIL: $label" >&2 + echo " regex: $regex" >&2 + echo " actual: $actual" >&2 + exit 1 + fi +} + +tmpfs_build_base() { + if [[ -d /dev/shm && -w /dev/shm ]]; then + printf '/dev/shm' + else + printf '%s' "${TMPDIR:-/tmp}" + fi +} + +session="$(_mcc_resolve_session "demo-branch")" +assert_eq "demo-branch" "$session" "explicit session" + +short_name="$(_mcc_resolve_username "feature-ai")" +assert_eq "mcc_feature_ai" "$short_name" "short derived username" + +long_name="$(_mcc_resolve_username "very-long-worktree-name")" +assert_regex '^mcc_[a-z0-9_]{7}_[0-9a-f]{4}$' "$long_name" "long derived username shape" +assert_eq "16" "${#long_name}" "long derived username length" + +assert_eq "${TMPDIR:-/tmp}/mcc-debug/demo-branch" "$(_mcc_session_root "demo-branch")" "session root" +assert_eq "mcc-demo-branch" "$(_mcc_tmux_session_name "demo-branch")" "tmux session name" + +MCC_BUILD_MODE=tmpfs + +original_repo_root="$MCC_REPO_ROOT" +fallback_root="$REPO_ROOT/nonexistent-worktree" +MCC_REPO_ROOT="$fallback_root" + +fallback_session="$(_mcc_resolve_session)" +assert_eq "$(basename "$fallback_root")" "$fallback_session" "session fallback without git" + +fallback_build_root="$(_mcc_build_root)" +tmpfs_base="$(tmpfs_build_base)" +expected_fallback_root="$tmpfs_base/mcc-build/$(basename "$fallback_root")" +assert_eq "$expected_fallback_root" "$fallback_build_root" "tmpfs build root fallback" + +MCC_REPO_ROOT="$original_repo_root" + +build_root="$(_mcc_build_root)" +expected_prefix="$(tmpfs_build_base)/mcc-build/" +if [[ "$build_root" != "$expected_prefix"* ]]; then + echo "FAIL: tmpfs build root" >&2 + echo " expected prefix: $expected_prefix" >&2 + echo " actual: $build_root" >&2 + exit 1 +fi + +dotnet_env_build_root="$(_mcc_dotnet_env env | awk -F= '$1=="MCC_BUILD_ROOT" { print $2 }')" +assert_eq "$build_root" "$dotnet_env_build_root" "dotnet env wrapper exports MCC_BUILD_ROOT" + +mkdir -p "$build_root/probe" +mcc-build-clean +[[ ! -e "$build_root/probe" ]] + +session="wrapper-smoke" +input_file="$(_mcc_session_input_file "$session")" +rm -rf "$(_mcc_session_root "$session")" + +mcc-cmd --session "$session" debug state +input_contents="$(cat "$input_file")" +assert_eq "debug state" "$input_contents" "session input command is intact" + +mcc-reset-session --session "$session" +[[ ! -e "$(_mcc_session_root "$session")" ]] +malformed_log="${TMPDIR:-/tmp}/mcc-env-session-hang-test.log" +for func in mcc-cmd mcc-reset-session mcc-state mcc-log-mcc mcc-run mcc-tui; do + set +e + "$func" --session >"$malformed_log" 2>&1 + status=$? + set -e + if [[ $status -eq 0 ]]; then + echo "FAIL: $func accepted --session without a value" >&2 + cat "$malformed_log" >&2 + exit 1 + fi + grep -Fq -- "--session requires a value" "$malformed_log" +done + +guard_root="$(mktemp -d "${TMPDIR:-/tmp}/mcc-env-guard.XXXXXX")" +MCC_SERVERS="$guard_root" +mkdir -p "$MCC_SERVERS/testver" +printf 'unchanged\n' > "$MCC_SERVERS/testver/stdin.pipe" + +confirm_log="${TMPDIR:-/tmp}/mcc-env-confirm-guard.log" +for cmd in \ + "mc-stop testver" \ + "mc-kill testver" \ + "mc-reset-test-env testver" +do + set +e + eval "$cmd" >"$confirm_log" 2>&1 + status=$? + set -e + if [[ $status -eq 0 ]]; then + echo "FAIL: $cmd ran without --confirm" >&2 + cat "$confirm_log" >&2 + exit 1 + fi + grep -Fq -- "--confirm" "$confirm_log" + grep -Fq "Keep shared servers running by default" "$confirm_log" +done + +stdin_contents="$(cat "$MCC_SERVERS/testver/stdin.pipe")" +assert_eq "unchanged" "$stdin_contents" "mc-stop without confirm does not touch stdin pipe" + +: > "$MCC_SERVERS/testver/stdin.pipe" +mc-stop testver --confirm +assert_eq "stop" "$(cat "$MCC_SERVERS/testver/stdin.pipe")" "mc-stop with confirm writes to stdin pipe" + +echo "PASS" diff --git a/tools/translate_crowdin.py b/tools/translate_crowdin.py new file mode 100644 index 00000000..976a36ef --- /dev/null +++ b/tools/translate_crowdin.py @@ -0,0 +1,997 @@ +#!/usr/bin/env python3 +"""Translate Crowdin XLIFF bundles via Alibaba Cloud Qwen-MT API. + +Workflow: + 1. Download a Crowdin bundle (or reuse an existing one) + 2. Parse XLIFF files, extract needs-translation entries + 3. Call Qwen-MT for each entry independently + 4. Generate per-language XLIFF with translated entries only + 5. Upload via `crowdin file upload --xliff` + +Requires: Python 3.10+, crowdin CLI, ALI_BAILIAN_API_KEY env var. +No third-party Python packages needed (uses urllib for API calls). +""" + +from __future__ import annotations + +import argparse +import json +import logging +import os +import re +import subprocess +import sys +import textwrap +import time +import urllib.error +import urllib.request +import xml.etree.ElementTree as ET +import zipfile +from dataclasses import dataclass, field +from pathlib import Path + +XLIFF_NS = "urn:oasis:names:tc:xliff:document:1.2" +NS = {"x": XLIFF_NS} + +REPO_ROOT = Path(__file__).resolve().parent.parent +WORK_DIR = REPO_ROOT / ".crowdin-translate" +BUNDLES_DIR = WORK_DIR / "bundles" +ERRORS_DIR = WORK_DIR / "errors" +DOMAIN_PROMPT_CACHE_DIR = WORK_DIR / "domain-prompt-cache" + +QWEN_MT_API_URL = os.environ.get( + "QWEN_MT_API_URL", + "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", +) + +DOMAIN_PROMPT = """The sentence is from Minecraft Console Client (MCC), a text-based client for Minecraft Java Edition. Content includes application UI strings, bot/automation configuration, internal commands, status messages, and user documentation covering inventory, terrain, entities, crafting, movement, server connection, and CLI/configuration topics. +When translating, prioritize official Minecraft in-game terminology. Where the player community has widely adopted different terms, prefer the more recognizable one. Translate into this Minecraft client-tool domain style.""" + +# Crowdin locale -> (Qwen-MT target_lang, Crowdin CLI -l id, extra domain note) +LANGUAGE_MAP: dict[str, tuple[str, str, str]] = { + "af_ZA": ("Afrikaans", "af", ""), + "ar_SA": ("Arabic", "ar", ""), + "az_AZ": ("North Azerbaijani", "az", ""), + "ca_ES": ("Catalan", "ca", ""), + "cs_CZ": ("Czech", "cs", ""), + "da_DK": ("Danish", "da", ""), + "de_DE": ("German", "de", ""), + "el_GR": ("Greek", "el", ""), + "es_ES": ("Spanish", "es-ES", ""), + "fi_FI": ("Finnish", "fi", ""), + "fr_FR": ("French", "fr", ""), + "he_IL": ("Hebrew", "he", ""), + "hi_IN": ("Hindi", "hi", ""), + "hu_HU": ("Hungarian", "hu", ""), + "id_ID": ("Indonesian", "id", ""), + "it_IT": ("Italian", "it", ""), + "ja_JP": ("Japanese", "ja", ""), + "ko_KR": ("Korean", "ko", ""), + "lv_LV": ("Latvian", "lv", ""), + "nl_NL": ("Dutch", "nl", ""), + "no_NO": ("Norwegian Bokmål", "no", ""), + "pl_PL": ("Polish", "pl", ""), + "pt_BR": ("Portuguese", "pt-BR", "Translate into Brazilian Portuguese."), + "pt_PT": ("Portuguese", "pt-PT", "Translate into European Portuguese."), + "ro_RO": ("Romanian", "ro", ""), + "ru_RU": ("Russian", "ru", ""), + "sr_SP": ("Serbian", "sr", ""), + "sv_SE": ("Swedish", "sv-SE", ""), + "fil_PH": ("Tagalog", "fil", ""), + "tr_TR": ("Turkish", "tr", ""), + "uk_UA": ("Ukrainian", "uk", ""), + "vi_VN": ("Vietnamese", "vi", ""), + "zh_CN": ("Chinese", "zh-CN", ""), + "zh_TW": ("Traditional Chinese", "zh-TW", ""), +} + +# Locales with significant active users (based on usage analytics). +# Used as the default set when --languages is not specified. +# Pass --languages all to translate every locale in LANGUAGE_MAP. +DEFAULT_LOCALES: list[str] = [ + "zh_CN", # CN ~920 + "tr_TR", # TR ~380 + "de_DE", # DE ~190 + "pl_PL", # PL ~180 + "vi_VN", # VN ~160 + "hi_IN", # IN ~100 + "ru_RU", # RU ~100 + "fr_FR", # FR ~90 + "zh_TW", # TW ~80 + "nl_NL", # NL ~70 + "ja_JP", # JP ~60 + "pt_BR", # BR ~55 + "sv_SE", # SE ~45 + "fi_FI", # FI ~40 + "uk_UA", # UA ~35 + "id_ID", # ID ~30 + "it_IT", # IT ~25 + "fil_PH", # PH ~20 +] + +log = logging.getLogger("translate_crowdin") + + +# --------------------------------------------------------------------------- +# Data structures +# --------------------------------------------------------------------------- + +@dataclass +class FileInfo: + file_id: str + original: str + source_language: str + target_language: str + project_id: str + attrs: dict[str, str] = field(default_factory=dict) + + +@dataclass +class TransUnit: + id: str + source: str + target_text: str + context: str | None = None + resname: str | None = None + file_info: FileInfo | None = None + translated: str | None = None + + +# --------------------------------------------------------------------------- +# XLIFF parsing +# --------------------------------------------------------------------------- + +def parse_xliff( + path: Path, *, + exclude_paths: list[str] | None = None, + include_paths: list[str] | None = None, +) -> list[TransUnit]: + """Parse an XLIFF 1.2 file, return trans-units with state=needs-translation. + + include_paths: if set, only keep <file> elements whose ``original`` + starts with (or equals) one of these prefixes. Takes priority over + exclude_paths. + + exclude_paths: skip <file> elements whose ``original`` starts with any + of these prefixes (e.g. ``["/docs/"]``). + """ + tree = ET.parse(path) + root = tree.getroot() + units: list[TransUnit] = [] + + for file_elem in root.findall(f"{{{XLIFF_NS}}}file"): + original = file_elem.get("original", "") + if include_paths: + if not any(original == p or original.startswith(p.rstrip("/") + "/") + or original == p.rstrip("/") + for p in include_paths): + continue + elif exclude_paths and any(original.startswith(p) for p in exclude_paths): + continue + finfo = FileInfo( + file_id=file_elem.get("id", ""), + original=file_elem.get("original", ""), + source_language=file_elem.get("source-language", "en"), + target_language=file_elem.get("target-language", ""), + project_id=file_elem.get("project-id", ""), + attrs={k: v for k, v in file_elem.attrib.items()}, + ) + + body = file_elem.find(f"{{{XLIFF_NS}}}body") + if body is None: + continue + + for tu in body.findall(f"{{{XLIFF_NS}}}trans-unit"): + target_elem = tu.find(f"{{{XLIFF_NS}}}target") + if target_elem is None or target_elem.get("state") != "needs-translation": + continue + + source_elem = tu.find(f"{{{XLIFF_NS}}}source") + source_text = source_elem.text or "" if source_elem is not None else "" + target_text = target_elem.text or "" + + ctx = None + cg = tu.find(f"{{{XLIFF_NS}}}context-group") + if cg is not None: + ctx_elem = cg.find(f"{{{XLIFF_NS}}}context") + if ctx_elem is not None and ctx_elem.text: + ctx = ctx_elem.text.strip() + + units.append(TransUnit( + id=tu.get("id", ""), + source=source_text, + target_text=target_text, + context=ctx, + resname=tu.get("resname"), + file_info=finfo, + )) + + return units + + +# --------------------------------------------------------------------------- +# Qwen-MT API +# --------------------------------------------------------------------------- + +def call_qwen_mt( + source_text: str, + target_lang: str, + model: str, + api_key: str, + context: str | None = None, + extra_domain: str = "", +) -> str: + """Call Qwen-MT translation API. Returns translated text.""" + domains = DOMAIN_PROMPT + if extra_domain: + domains += "\n" + extra_domain + # if context: + # domains += f"\nText key: {context}" + # domains += f"(THE ABOVE IS NOT CONTENT TO BE TRANSLATED!)" + + payload = { + "model": model, + "messages": [{"role": "user", "content": source_text}], + "translation_options": { + "source_lang": "English", + "target_lang": target_lang, + "domains": domains, + }, + } + + data = json.dumps(payload, ensure_ascii=False).encode("utf-8") + req = urllib.request.Request( + QWEN_MT_API_URL, + data=data, + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {api_key}", + }, + method="POST", + ) + + with urllib.request.urlopen(req, timeout=60) as resp: + body = json.loads(resp.read().decode("utf-8")) + + return body["choices"][0]["message"]["content"] + + +# --------------------------------------------------------------------------- +# Domain-prompt leak detection +# --------------------------------------------------------------------------- + +_LEAK_FINGERPRINTS_EN = [ + "cross-platform, text-based third-party client", + "Mojang's localization for the target language", + "keep the English name or translate descriptively", + "Preserve all placeholders ({0})", + "Translate into this Minecraft client-tool domain style", + "command syntax (/command <arg>)", + "bot/automation configuration, internal commands", +] + + +def _split_into_fragments(text: str, min_len: int = 6) -> list[str]: + """Split translated domain prompt into sentence-level fragments.""" + raw = re.split(r'[。.\.\n!!??;;::\u3002]', text) + seen: set[str] = set() + fragments: list[str] = [] + for frag in raw: + frag = frag.strip() + if len(frag) >= min_len and frag not in seen: + seen.add(frag) + fragments.append(frag) + return fragments + + +def _deduplicate_prompt_translation(text: str) -> str: + """Remove duplicate paragraphs from a cached domain prompt translation. + + The API occasionally returns the translation twice (or more) in a single + response. We split on blank lines, keep the first occurrence of each + paragraph, and rejoin. + """ + paragraphs = text.split("\n") + seen: set[str] = set() + unique: list[str] = [] + for para in paragraphs: + key = para.strip() + if key not in seen: + seen.add(key) + unique.append(para) + return "\n".join(unique).strip() + + +def _char_ngrams(text: str, n: int = 5) -> set[str]: + """Generate character n-grams from text (whitespace normalized).""" + t = re.sub(r'\s+', '', text) + return {t[i:i + n] for i in range(len(t) - n + 1)} if len(t) >= n else set() + + +def _shingle_similarity(reference_grams: set[str], candidate: str, + n: int = 5) -> float: + """Fraction of reference n-grams found in candidate text.""" + if not reference_grams: + return 0.0 + cand_grams = _char_ngrams(candidate, n) + return len(reference_grams & cand_grams) / len(reference_grams) + + +def ensure_domain_prompt_cached( + locale: str, + target_lang: str, + api_key: str, + model: str, +) -> tuple[str, list[str]]: + """Translate DOMAIN_PROMPT into target language, cache it, return (full_text, fragments). + + On subsequent runs the cached file is reused without an API call. + """ + DOMAIN_PROMPT_CACHE_DIR.mkdir(parents=True, exist_ok=True) + cache_file = DOMAIN_PROMPT_CACHE_DIR / f"{locale}.txt" + + if cache_file.exists(): + text = cache_file.read_text(encoding="utf-8") + deduped = _deduplicate_prompt_translation(text) + if deduped != text.strip(): + log.info(" Fixed duplicate content in cache for %s, rewriting", + locale) + cache_file.write_text(deduped, encoding="utf-8") + text = deduped + log.info(" Loaded cached domain prompt translation for %s", locale) + else: + log.info(" Translating domain prompt into %s for leak detection ...", + target_lang) + text = call_qwen_mt( + source_text=DOMAIN_PROMPT, + target_lang=target_lang, + model=model, + api_key=api_key, + ) + text = _deduplicate_prompt_translation(text) + cache_file.write_text(text, encoding="utf-8") + log.info(" Cached domain prompt translation -> %s", cache_file) + + return text, _split_into_fragments(text) + + +class DomainLeakDetector: + """Detect and clean translations that contain leaked domain-prompt text. + + Uses the original English fingerprints plus per-language fragments + obtained by translating the domain prompt itself. A character n-gram + (shingling) similarity check catches paraphrased leaks that exact + substring matching would miss. + """ + + NGRAM_SIZE = 5 + FULL_TEXT_THRESHOLD = 0.25 + LINE_THRESHOLD = 0.35 + + def __init__(self, cached_fragments: list[str] | None = None, + cached_full_text: str = ""): + self._en = list(_LEAK_FINGERPRINTS_EN) + self._translated = cached_fragments or [] + self._full_text = cached_full_text + self._prompt_grams = _char_ngrams(cached_full_text, self.NGRAM_SIZE) + self._fragment_grams = [ + _char_ngrams(f, self.NGRAM_SIZE) for f in self._translated + ] + + def detect(self, source: str, translated: str) -> bool: + for fp in self._en: + if fp in translated and fp not in source: + return True + for fp in self._translated: + if fp in translated and fp not in source: + return True + if self._prompt_grams: + sim = _shingle_similarity(self._prompt_grams, translated, + self.NGRAM_SIZE) + if sim > self.FULL_TEXT_THRESHOLD: + return True + for fg in self._fragment_grams: + if fg and _shingle_similarity(fg, translated, self.NGRAM_SIZE) > self.LINE_THRESHOLD: + return True + return False + + def _is_leak_line(self, line: str, source: str) -> bool: + all_fps = self._en + self._translated + if any(fp in line for fp in all_fps if fp not in source): + return True + if len(line.strip()) <= 10: + return False + for fg in self._fragment_grams: + if fg and _shingle_similarity(fg, line, self.NGRAM_SIZE) > self.LINE_THRESHOLD: + return True + return False + + def postprocess(self, source: str, translated: str) -> str | None: + """Return cleaned translation, or None if unsalvageable.""" + if not self.detect(source, translated): + return translated + + lines = translated.split("\n") + clean = [ln for ln in lines if not self._is_leak_line(ln, source)] + cleaned = "\n".join(clean).strip() + if not cleaned or len(cleaned) < max(len(source) * 0.2, 1): + return None + if self.detect(source, cleaned): + return None + return cleaned + + +# --------------------------------------------------------------------------- +# Rate-limited translator +# --------------------------------------------------------------------------- + +MAX_RETRIES = 6 +INITIAL_BACKOFF = 2.0 # seconds + + +class RateLimitedTranslator: + """Single-threaded translator with strict RPM pacing and 429 retry.""" + + def __init__(self, api_key: str, model: str, rpm: int, target_lang: str, + extra_domain: str = "", + leak_detector: DomainLeakDetector | None = None): + self.api_key = api_key + self.model = model + self.rpm = rpm + self.target_lang = target_lang + self.extra_domain = extra_domain + self.detector = leak_detector or DomainLeakDetector() + self._interval = 60.0 / rpm + self._last_call = 0.0 + + def _pace(self) -> None: + """Sleep to enforce strict RPM spacing between requests.""" + now = time.monotonic() + wait = self._interval - (now - self._last_call) + if wait > 0: + time.sleep(wait) + self._last_call = time.monotonic() + + def translate_one(self, unit: TransUnit) -> TransUnit: + """Translate a single TransUnit with rate limiting and retry on 429.""" + leak_retries = 0 + for attempt in range(MAX_RETRIES + 1): + self._pace() + try: + result = call_qwen_mt( + source_text=unit.source, + target_lang=self.target_lang, + model=self.model, + api_key=self.api_key, + context=unit.context, + extra_domain=self.extra_domain, + ) + cleaned = self.detector.postprocess(unit.source, result) + if cleaned is None and leak_retries < 2: + leak_retries += 1 + log.warning("Domain prompt leak in unit %s, retrying (%d/2)", + unit.id, leak_retries) + continue + if cleaned is None: + log.warning("Domain prompt leak in unit %s persists after " + "retries, skipping", unit.id) + unit.translated = None + return unit + result = cleaned + leading = len(unit.source) - len(unit.source.lstrip(" ")) + if leading > 0 and not result.startswith(" " * leading): + result = " " * leading + result.lstrip(" ") + unit.translated = result + return unit + except urllib.error.HTTPError as exc: + if exc.code == 429 and attempt < MAX_RETRIES: + backoff = INITIAL_BACKOFF * (2 ** attempt) + log.warning("429 on unit %s, retry %d/%d after %.1fs", + unit.id, attempt + 1, MAX_RETRIES, backoff) + time.sleep(backoff) + self._last_call = time.monotonic() + continue + log.warning("Failed to translate unit %s: %s", unit.id, exc) + unit.translated = None + return unit + except Exception as exc: + log.warning("Failed to translate unit %s: %s", unit.id, exc) + unit.translated = None + return unit + return unit + + def translate_batch(self, units: list[TransUnit], + progress_callback=None) -> tuple[list[TransUnit], bool]: + """Translate a list of units sequentially with strict RPM pacing. + + Returns (results, interrupted): results may be partial if the user + pressed Ctrl-C. The caller should still persist whatever was completed. + """ + if not units: + return units, False + + results: list[TransUnit] = [] + interrupted = False + for i, u in enumerate(units): + try: + self.translate_one(u) + except KeyboardInterrupt: + log.warning("Ctrl-C during translation, finishing up...") + interrupted = True + break + results.append(u) + if progress_callback: + progress_callback(i + 1, len(units)) + + return results, interrupted + + +# --------------------------------------------------------------------------- +# XLIFF output generation +# --------------------------------------------------------------------------- + +def generate_output_xliff(units: list[TransUnit], target_language_xliff: str) -> str: + """Generate an XLIFF 1.2 string containing only successfully translated units.""" + translated = [u for u in units if u.translated] + if not translated: + return "" + + by_file: dict[str, list[TransUnit]] = {} + for u in translated: + key = u.file_info.file_id if u.file_info else "0" + by_file.setdefault(key, []).append(u) + + root = ET.Element("xliff", { + "version": "1.2", + "xmlns": XLIFF_NS, + }) + + for file_id, file_units in by_file.items(): + ref = file_units[0].file_info + if not ref: + continue + + file_attrs = dict(ref.attrs) + file_elem = ET.SubElement(root, "file", file_attrs) + body = ET.SubElement(file_elem, "body") + + for u in file_units: + tu_attrs: dict[str, str] = {"id": u.id} + if u.resname: + tu_attrs["resname"] = u.resname + tu_elem = ET.SubElement(body, "trans-unit", tu_attrs) + src = ET.SubElement(tu_elem, "source") + src.text = u.source + tgt = ET.SubElement(tu_elem, "target", {"state": "translated"}) + tgt.text = u.translated + + ET.indent(root, space=" ") + xml_str = ET.tostring(root, encoding="unicode", xml_declaration=False) + return '<?xml version="1.0" encoding="UTF-8"?>\n' + xml_str + "\n" + + +# --------------------------------------------------------------------------- +# Bundle download +# --------------------------------------------------------------------------- + +def download_bundle(bundle_id: int) -> Path: + """Download a Crowdin bundle, collect XLIFF files into the work directory. + + crowdin bundle download extracts XLIFF files directly into cwd (no zip, + no subdirectory). We snapshot existing *.xliff before the download, then + move only the newly appeared files into BUNDLES_DIR/<timestamp>/. + """ + BUNDLES_DIR.mkdir(parents=True, exist_ok=True) + + existing_xliffs = set(REPO_ROOT.glob("MCC_FullBundle_*.xliff")) + + log.info("Downloading Crowdin bundle %d ...", bundle_id) + result = subprocess.run( + ["crowdin", "bundle", "download", str(bundle_id)], + capture_output=True, text=True, cwd=REPO_ROOT, + ) + if result.returncode != 0: + log.error("crowdin bundle download failed:\n%s\n%s", + result.stdout, result.stderr) + sys.exit(1) + + new_xliffs = sorted( + set(REPO_ROOT.glob("MCC_FullBundle_*.xliff")) - existing_xliffs + ) + + if not new_xliffs: + all_xliffs = sorted(REPO_ROOT.glob("MCC_FullBundle_*.xliff")) + if all_xliffs: + log.info("No new XLIFF files appeared; using %d existing file(s) " + "in repo root", len(all_xliffs)) + new_xliffs = all_xliffs + else: + log.error("No XLIFF files found after download. stdout:\n%s", + result.stdout) + sys.exit(1) + + timestamp = time.strftime("%Y%m%d-%H%M%S") + dest = BUNDLES_DIR / f"bundle-{timestamp}" + dest.mkdir(parents=True, exist_ok=True) + + for src in new_xliffs: + target = dest / src.name + src.rename(target) + log.info("Moved %d XLIFF file(s) to %s", len(new_xliffs), dest) + + return dest + + +def extract_bundle_zip(zip_path: Path) -> Path: + """Extract an existing bundle ZIP, return the extracted directory.""" + BUNDLES_DIR.mkdir(parents=True, exist_ok=True) + dest = BUNDLES_DIR / Path(zip_path).stem + dest.mkdir(parents=True, exist_ok=True) + log.info("Extracting %s -> %s", zip_path.name, dest) + with zipfile.ZipFile(zip_path, "r") as zf: + zf.extractall(dest) + return dest + + +# --------------------------------------------------------------------------- +# Crowdin upload +# --------------------------------------------------------------------------- + +def upload_xliff(xliff_path: Path, crowdin_lang: str) -> bool: + """Upload a translated XLIFF to Crowdin.""" + log.info("Uploading %s for language %s ...", xliff_path.name, crowdin_lang) + result = subprocess.run( + ["crowdin", "file", "upload", str(xliff_path), + "--xliff", "-l", crowdin_lang], + capture_output=True, text=True, cwd=REPO_ROOT, + ) + if result.returncode != 0: + log.error("Upload failed for %s:\n%s\n%s", + crowdin_lang, result.stdout, result.stderr) + return False + log.info("Upload succeeded for %s", crowdin_lang) + return True + + +# --------------------------------------------------------------------------- +# Resume support +# --------------------------------------------------------------------------- + +def load_existing_translated_ids(xliff_path: Path) -> set[str]: + """Read an existing output XLIFF, return the set of translated unit IDs.""" + if not xliff_path.exists(): + return set() + try: + tree = ET.parse(xliff_path) + root = tree.getroot() + ids = set() + for tu in root.iter(f"{{{XLIFF_NS}}}trans-unit"): + uid = tu.get("id") + if uid: + ids.add(uid) + return ids + except ET.ParseError: + return set() + + +# --------------------------------------------------------------------------- +# Main orchestration +# --------------------------------------------------------------------------- + +def find_xliff_files(bundle_dir: Path, locales: list[str] | None) -> dict[str, Path]: + """Map Crowdin locale -> XLIFF path, preserving the order of *locales*. + + When locales is None (all languages) files are ordered by filename. + """ + available: dict[str, Path] = {} + for xliff_path in sorted(bundle_dir.glob("*.xliff")): + name = xliff_path.stem + for locale in LANGUAGE_MAP: + if name.endswith(f"_{locale}"): + available[locale] = xliff_path + break + + if locales is None: + return available + + return {loc: available[loc] for loc in locales if loc in available} + + +def process_language( + locale: str, + xliff_path: Path, + api_key: str, + model: str, + rpm: int, + output_dir: Path, + limit: int | None, + dry_run: bool, + skip_upload: bool, + exclude_paths: list[str] | None = None, + include_paths: list[str] | None = None, +) -> None: + """Full pipeline for one language.""" + lang_info = LANGUAGE_MAP.get(locale) + if not lang_info: + log.warning("No language mapping for %s, skipping", locale) + return + + target_lang, crowdin_lang, extra_domain = lang_info + log.info("=" * 60) + log.info("Processing %s -> %s", locale, target_lang) + + units = parse_xliff(xliff_path, exclude_paths=exclude_paths, + include_paths=include_paths) + log.info(" Found %d needs-translation entries", len(units)) + + if not units: + log.info(" Nothing to translate, skipping") + return + + output_file = output_dir / f"MCC_Translated_{locale}.xliff" + already_done = load_existing_translated_ids(output_file) + if already_done: + before = len(units) + units = [u for u in units if u.id not in already_done] + log.info(" Resuming: %d already translated, %d remaining", + before - len(units), len(units)) + + if limit is not None and limit < len(units): + log.info(" Limiting to first %d entries (--limit)", limit) + units = units[:limit] + + if dry_run: + log.info(" [DRY RUN] Would translate %d entries", len(units)) + if units: + log.info(" Sample source (id=%s): %.100s...", units[0].id, + units[0].source) + return + + if not units: + log.info(" All entries already translated") + return + + cached_full, cached_fragments = ensure_domain_prompt_cached( + locale, target_lang, api_key, model) + log.info(" Leak detector loaded %d fragment(s) for %s", + len(cached_fragments), locale) + detector = DomainLeakDetector(cached_fragments, cached_full) + + translator = RateLimitedTranslator( + api_key=api_key, + model=model, + rpm=rpm, + target_lang=target_lang, + extra_domain=extra_domain, + leak_detector=detector, + ) + + def on_progress(done: int, total: int) -> None: + if done % 5 == 0 or done == total: + log.info(" [%s] %d/%d (%.0f%%)", locale, done, total, + done / total * 100) + + translated_units, interrupted = translator.translate_batch( + units, progress_callback=on_progress) + + success = sum(1 for u in translated_units if u.translated) + failed = sum(1 for u in translated_units if u.translated is None) + log.info(" Translated: %d, Failed: %d%s", success, failed, + " (interrupted)" if interrupted else "") + + if failed > 0: + ERRORS_DIR.mkdir(parents=True, exist_ok=True) + err_path = ERRORS_DIR / f"errors_{locale}.log" + with open(err_path, "a", encoding="utf-8") as f: + for u in translated_units: + if u.translated is None: + f.write(f"id={u.id} resname={u.resname} " + f"source={u.source[:200]}\n") + log.info(" Error details written to %s", err_path) + + new_success = [u for u in translated_units if u.translated] + + if already_done and output_file.exists(): + existing_units = _parse_existing_output(output_file) + all_units = existing_units + new_success + else: + all_units = new_success + + if not all_units: + if interrupted: + raise KeyboardInterrupt + return + + target_language_xliff = xliff_path.stem.split("_", 2)[-1] if "_" in xliff_path.stem else locale + xliff_content = generate_output_xliff(all_units, target_language_xliff) + if xliff_content: + output_dir.mkdir(parents=True, exist_ok=True) + output_file.write_text(xliff_content, encoding="utf-8") + log.info(" Written: %s (%d units)", output_file.name, len(all_units)) + + if not skip_upload and not interrupted and new_success: + upload_xliff(output_file, crowdin_lang) + elif not new_success: + log.info(" No new translations this run, skipping upload") + + if interrupted: + raise KeyboardInterrupt + + +def _parse_existing_output(path: Path) -> list[TransUnit]: + """Re-parse a previously generated output XLIFF into TransUnit objects.""" + tree = ET.parse(path) + root = tree.getroot() + units: list[TransUnit] = [] + + for file_elem in root.findall(f"{{{XLIFF_NS}}}file"): + finfo = FileInfo( + file_id=file_elem.get("id", ""), + original=file_elem.get("original", ""), + source_language=file_elem.get("source-language", "en"), + target_language=file_elem.get("target-language", ""), + project_id=file_elem.get("project-id", ""), + attrs={k: v for k, v in file_elem.attrib.items()}, + ) + body = file_elem.find(f"{{{XLIFF_NS}}}body") + if body is None: + continue + for tu in body.findall(f"{{{XLIFF_NS}}}trans-unit"): + src_elem = tu.find(f"{{{XLIFF_NS}}}source") + tgt_elem = tu.find(f"{{{XLIFF_NS}}}target") + units.append(TransUnit( + id=tu.get("id", ""), + source=src_elem.text or "" if src_elem is not None else "", + target_text="", + resname=tu.get("resname"), + file_info=finfo, + translated=tgt_elem.text or "" if tgt_elem is not None else "", + )) + + return units + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description="Translate Crowdin XLIFF bundles using Qwen-MT API", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=textwrap.dedent("""\ + Examples: + %(prog)s --dry-run + %(prog)s --languages zh_CN,ja_JP --limit 10 --skip-upload + %(prog)s --bundle-dir .crowdin-translate/bundles/bundle-xxx/ + %(prog)s --model qwen-mt-plus --rpm 30 + """), + ) + src = p.add_mutually_exclusive_group() + src.add_argument("--bundle-dir", type=Path, metavar="DIR", + help="Reuse an already-extracted bundle directory") + src.add_argument("--bundle-zip", type=Path, metavar="ZIP", + help="Reuse an already-downloaded bundle ZIP") + p.add_argument("--bundle-id", type=int, default=2, + help="Crowdin bundle ID to download (default: 2)") + p.add_argument("-l", "--languages", type=str, default=None, + help="Comma-separated Crowdin locales (e.g. zh_CN,ja_JP), " + "'all' for every supported locale, or omit to use the " + "default active-user set") + p.add_argument("--model", type=str, default="qwen-mt-plus", + choices=["qwen-mt-plus", "qwen-mt-flash", "qwen-mt-lite"], + help="Qwen-MT model (default: qwen-mt-plus)") + p.add_argument("--rpm", type=int, default=60, + help="Max requests per minute (default: 60)") + p.add_argument("--limit", type=int, default=None, metavar="N", + help="Translate at most N entries per language (for debugging)") + p.add_argument("--dry-run", action="store_true", + help="Parse and report without calling the API") + p.add_argument("--skip-upload", action="store_true", + help="Skip uploading translations to Crowdin") + p.add_argument("--output-dir", type=Path, default=None, + help="Output directory (default: <bundle-dir>/translated/)") + p.add_argument("--include-docs", action="store_true", + help="Include /docs/ files in translation (skipped by default)") + p.add_argument("-f", "--files", type=str, default=None, + help="Comma-separated file paths to translate (e.g. " + "/docs/guide/README.md,/MinecraftClient/Resources/Translations/Translations.resx). " + "Overrides --include-docs") + p.add_argument("-v", "--verbose", action="store_true", + help="Enable debug logging") + return p + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + datefmt="%H:%M:%S", + ) + + api_key = os.environ.get("ALI_BAILIAN_API_KEY", "") + if not api_key and not args.dry_run: + log.error("ALI_BAILIAN_API_KEY environment variable is not set") + sys.exit(1) + + if args.languages and args.languages.strip().lower() == "all": + locales = None # None means all locales in LANGUAGE_MAP + log.info("Language selection: all %d supported locales", len(LANGUAGE_MAP)) + elif args.languages: + locales = [s.strip() for s in args.languages.split(",")] + unknown = [loc for loc in locales if loc not in LANGUAGE_MAP] + if unknown: + log.error("Unknown locale(s): %s\nAvailable: %s", + ", ".join(unknown), ", ".join(sorted(LANGUAGE_MAP))) + sys.exit(1) + else: + locales = list(DEFAULT_LOCALES) + log.info("Language selection: %d default locales (use --languages all for all)", + len(locales)) + + if args.bundle_dir: + bundle_dir = args.bundle_dir + if not bundle_dir.is_dir(): + log.error("Bundle directory not found: %s", bundle_dir) + sys.exit(1) + elif args.bundle_zip: + if not args.bundle_zip.is_file(): + log.error("Bundle ZIP not found: %s", args.bundle_zip) + sys.exit(1) + bundle_dir = extract_bundle_zip(args.bundle_zip) + else: + bundle_dir = download_bundle(args.bundle_id) + + if args.output_dir: + output_dir = args.output_dir + else: + output_dir = bundle_dir / "translated" + output_dir.mkdir(parents=True, exist_ok=True) + log.info("Output directory: %s", output_dir) + + include_paths: list[str] | None = None + if args.files: + include_paths = [f.strip() for f in args.files.split(",")] + log.info("Filtering to files: %s", ", ".join(include_paths)) + + exclude_paths: list[str] | None = None + if not include_paths and not args.include_docs: + exclude_paths = ["/docs/"] + log.info("Excluding XLIFF files under: %s (use --include-docs or --files to include)", + ", ".join(exclude_paths)) + + xliff_files = find_xliff_files(bundle_dir, locales) + if not xliff_files: + log.error("No matching XLIFF files found in %s", bundle_dir) + sys.exit(1) + + log.info("Found %d language(s) to process: %s", + len(xliff_files), ", ".join(sorted(xliff_files))) + + for locale, xliff_path in xliff_files.items(): + try: + process_language( + locale=locale, + xliff_path=xliff_path, + api_key=api_key, + model=args.model, + rpm=args.rpm, + output_dir=output_dir, + limit=args.limit, + dry_run=args.dry_run, + skip_upload=args.skip_upload, + exclude_paths=exclude_paths, + include_paths=include_paths, + ) + except KeyboardInterrupt: + log.warning("Interrupted by user. Partial results have been saved.") + sys.exit(130) + except Exception: + log.exception("Error processing %s", locale) + + +if __name__ == "__main__": + main()