diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..9a44df4c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,130 @@ +name: _step.build + +on: + workflow_call: + inputs: + release: + type: boolean + required: false + default: false + target_subproject: + description: see release.yml, leave it empty to build all + type: string + required: false + default: '' + # [FEATURE] MIXIN_AUDITOR + mixin_audit: + description: run mixin audit for Minecraft after build + type: boolean + required: false + default: false + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 21 + + - name: Cache gradle files + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ./.gradle/loom-cache + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle.properties', '**/*.accesswidener', 'settings.json') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Build with gradle + run: | + chmod +x gradlew + if [ -z "${{ inputs.target_subproject }}" ]; then + echo "Building all subprojects" + ./gradlew build + else + args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:build/' | paste -sd ' ') + echo "Building with arguments=$args" + ./gradlew $args + fi + env: + BUILD_ID: ${{ github.run_number }} + BUILD_RELEASE: ${{ inputs.release }} + + # [FEATURE] MIXIN_AUDITOR +# - name: Run mixin audit check for Minecraft client +# if: ${{ inputs.mixin_audit }} +# timeout-minutes: 10 +# run: | +# mkdir -p ./run +# echo eula=true > ./run/eula.txt # server needs this +# ./gradlew runClientMixinAudit + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: build-artifacts + path: versions/*/build/libs/ + + - name: Collect mod jars + run: | + shopt -s extglob + mkdir mod-jars + for jar in versions/*/build/libs/!(*-@(dev|sources|shadow)).jar; do + cp -p "$jar" mod-jars/ + done + ls -l mod-jars + + # This is the artifact recommended for users to download + - name: Upload mod jars + uses: actions/upload-artifact@v4 + with: + name: mod-jars + path: mod-jars/*.jar + +# # [FEATURE] FALLENS_MAVEN +# - name: Publish with gradle +# if: inputs.release || github.ref == 'refs/heads/dev' +# run: | +# if [ -z "${{ inputs.target_subproject }}" ]; then +# echo "Publishing all subprojects" +# ./gradlew publish +# else +# args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:publish/' | paste -sd ' ') +# echo "Publishing with arguments=$args" +# ./gradlew $args +# fi +# env: +# BUILD_ID: ${{ github.run_number }} +# BUILD_RELEASE: ${{ inputs.release }} +# FALLENS_MAVEN_TOKEN: ${{ secrets.FALLENS_MAVEN_TOKEN }} + + summary: + runs-on: ubuntu-latest + needs: + - build + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: build-artifacts + + - name: Make build summary + run: | + pip install jproperties + python .github/workflows/scripts/summary.py + env: + TARGET_SUBPROJECT: ${{ inputs.target_subproject }} diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 7fae4032..c8314b7b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,43 +1,20 @@ -# build Java CI with Gradle +name: Dev Builds on: - push: - # ignore *.md *.yml file changes - # run on 'multiversion' branch - branches: [ multiversion ] - paths-ignore: - - '**.md' - - '**.yml' + push: + paths: + - "*.gradle" + - "gradle.properties" + - "src/**" + - "versions/**" + - ".github/**" + pull_request: -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Caches Gradle packages - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - ./.gradle/loom-cache - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Set up JDK 21 - uses: actions/setup-java@v3 - with: - java-version: '21' - distribution: 'temurin' - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - - name: Build with Gradle - run: ./gradlew buildAndGather --stacktrace --no-daemon - - name: Upload build artifact - uses: actions/upload-artifact@v3.1.0 - with: - name: Artifacts from ${{ github.sha }} - path: build/libs/ + build: + uses: ./.github/workflows/build.yml + secrets: inherit +# # [FEATURE] MIXIN_AUDITOR +# with: +# mixin_audit: true diff --git a/.github/workflows/matrix_prep.yml b/.github/workflows/matrix_prep.yml new file mode 100644 index 00000000..fbe6ef81 --- /dev/null +++ b/.github/workflows/matrix_prep.yml @@ -0,0 +1,32 @@ +name: _step.matrix_prepare + +on: + workflow_call: + inputs: + target_subproject: + description: see release.yml, for generating matrix entries + type: string + required: false + default: '' + outputs: + matrix: + description: The generated run matrix + value: ${{ jobs.matrix_prep.outputs.matrix }} + + +jobs: + matrix_prep: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - id: setmatrix + run: python .github/workflows/scripts/matrix.py + env: + TARGET_SUBPROJECT: ${{ inputs.target_subproject }} + + outputs: + matrix: ${{ steps.setmatrix.outputs.matrix }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..715d44a1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,200 @@ +name: Release + +# release: (release title) +# dispatch (all): Manual release for $target_release_tag +# dispatch (specified): Manual release for $target_release_tag (subproject: $target_subproject) +run-name: |- + ${{ github.event_name == 'workflow_dispatch' && format('Manual release for {0}{1}', inputs.target_release_tag, inputs.target_subproject && format(' (subproject: {0})', inputs.target_subproject) || '') || '' }} + +on: + release: + types: + - published + workflow_dispatch: + inputs: + target_subproject: + description: |- + The subproject name(s) of the specified Minecraft version to be released, seperated with ",". + By default all subprojects will be released + type: string + required: false + default: '' + target_release_tag: + description: The tag of the release you want to append the artifact to + type: string + required: true + + +jobs: + show_action_parameters: + runs-on: ubuntu-latest + steps: + - name: Show action parameters + run: | + cat < $GITHUB_STEP_SUMMARY + ## Action Parameters + - target_subproject: \`${{ github.event.inputs.target_subproject }}\` + - target_release_tag: \`${{ github.event.inputs.target_release_tag }}\` + EOF + + matrix_prep: + uses: ./.github/workflows/matrix_prep.yml + with: + target_subproject: ${{ github.event.inputs.target_subproject }} + + # ensure the input release tag is valid + validate_release: + runs-on: ubuntu-latest + steps: + - name: Get github release information + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: cardinalby/git-get-release-action@1.2.5 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tag: ${{ github.event.inputs.target_release_tag }} + + build: + uses: ./.github/workflows/build.yml + secrets: inherit + needs: + - validate_release + with: + target_subproject: ${{ github.event.inputs.target_subproject }} + release: true + + release: + needs: + - matrix_prep + - build + runs-on: ubuntu-latest + + # allow the mod publish step to add asserts to release + # https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token + permissions: + contents: write + + strategy: + matrix: ${{ fromJson(needs.matrix_prep.outputs.matrix) }} + + steps: + - uses: actions/checkout@v4 + + - name: Display context + run: | + echo ref_name = ${{ github.ref_name }} + echo target_subproject = ${{ github.event.inputs.target_subproject }} + echo target_release_tag = ${{ github.event.inputs.target_release_tag }} + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: build-artifacts + + - name: Get github release information + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get_release + uses: cardinalby/git-get-release-action@1.2.5 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tag: ${{ github.event.inputs.target_release_tag }} + + - name: Generate publish related information + id: release_info + run: | + if [ $GITHUB_EVENT_NAME == 'release' ] + then + # Leave an empty value here, so Kir-Antipov/mc-publish will infer the tag from the action context + echo "tag_name=" >> $GITHUB_OUTPUT + elif [ $GITHUB_EVENT_NAME == 'workflow_dispatch' ] + then + echo "tag_name=${{ github.event.inputs.target_release_tag }}" >> $GITHUB_OUTPUT + else + echo Unknown github event name $GITHUB_EVENT_NAME + exit 1 + fi + + - name: Read common properties + id: properties_g + uses: BrycensRanch/read-properties-action@v1 + with: + file: gradle.properties + all: true + + - name: Read version-specific properties + id: properties_v + uses: BrycensRanch/read-properties-action@v1 + with: + file: ${{ format('versions/{0}/gradle.properties', matrix.subproject) }} + all: true + + - name: Prepare file information + id: file_info + run: | + shopt -s extglob + FILE_PATHS=$(ls ${{ format('build-artifacts/{0}/build/libs/!(*-@(dev|sources|shadow)).jar', matrix.subproject) }}) + if (( ${#FILE_PATHS[@]} != 1 )); then + echo "Error: Found ${#FILE_PATHS[@]} files, expected exactly 1" + exit 1 + else + FILE_PATH=${FILE_PATHS[0]} + fi + + FILE_NAME=$(basename $FILE_PATH) + FILE_HASH=$(sha256sum $FILE_PATH | awk '{ print $1 }') + echo "path=$FILE_PATH" >> $GITHUB_OUTPUT + echo "name=$FILE_NAME" >> $GITHUB_OUTPUT + echo "hash=$FILE_HASH" >> $GITHUB_OUTPUT + cat $GITHUB_OUTPUT + + - name: Prepare changelog + uses: actions/github-script@v7 + id: changelog + with: + script: return process.env.CHANGELOG + result-encoding: string + env: + CHANGELOG: |- + ${{ format('{0}{1}', github.event.release.body, steps.get_release.outputs.body) }} + + ------- + + Build Information + + - File name: `${{ steps.file_info.outputs.name }}` + - SHA-256: `${{ steps.file_info.outputs.hash }}` + - Built from: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Publish Minecraft Mods + uses: Kir-Antipov/mc-publish@v3.3 + with: + # https://modrinth.com/settings/pats +# modrinth-id: fo0Bar +# modrinth-token: ${{ secrets.MODRINTH_API_TOKEN }} + + # https://legacy.curseforge.com/account/api-tokens +# curseforge-id: 314159 +# curseforge-token: ${{ secrets.CF_API_TOKEN }} + + github-tag: ${{ steps.release_info.outputs.tag_name }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + files: ${{ steps.file_info.outputs.path }} + + name: ${{ format('{0} v{1} for mc{2}', steps.properties_g.outputs.mod_name, steps.properties_g.outputs.mod_version, steps.properties_v.outputs.minecraft_version) }} + version: ${{ format('v{1}-mc{0}', steps.properties_v.outputs.minecraft_version, steps.properties_g.outputs.mod_version) }} + version-type: release + + loaders: fabric + game-versions: ${{ steps.properties_v.outputs.game_versions }} + game-version-filter: any + dependencies: '' # declare the dependencies explicitly, so mc-publish won't try to load from fabric.mod.json + + github-changelog: ${{ format('{0}{1}', github.event.release.body, steps.get_release.outputs.body) }} + modrinth-changelog: ${{ steps.changelog.outputs.result }} + curseforge-changelog: ${{ steps.changelog.outputs.result }} + + retry-attempts: 3 + retry-delay: 10000 diff --git a/.github/workflows/scripts/matrix.py b/.github/workflows/scripts/matrix.py new file mode 100644 index 00000000..a20e5814 --- /dev/null +++ b/.github/workflows/scripts/matrix.py @@ -0,0 +1,46 @@ +""" +A script to scan through the versions directory and collect all folder names as the subproject list, +then output a json as the github action include matrix +""" +__author__ = 'Fallen_Breath' + +import json +import os +import sys + + +def main(): + target_subproject_env = os.environ.get('TARGET_SUBPROJECT', '') + target_subprojects = list(filter(None, target_subproject_env.split(',') if target_subproject_env != '' else [])) + print('target_subprojects: {}'.format(target_subprojects)) + + with open('settings.json') as f: + settings: dict = json.load(f) + + if len(target_subprojects) == 0: + subprojects = settings['versions'] + else: + subprojects = [] + for subproject in settings['versions']: + if subproject in target_subprojects: + subprojects.append(subproject) + target_subprojects.remove(subproject) + if len(target_subprojects) > 0: + print('Unexpected subprojects: {}'.format(target_subprojects), file=sys.stderr) + sys.exit(1) + + matrix_entries = [] + for subproject in subprojects: + matrix_entries.append({ + 'subproject': subproject, + }) + matrix = {'include': matrix_entries} + with open(os.environ['GITHUB_OUTPUT'], 'w') as f: + f.write('matrix={}\n'.format(json.dumps(matrix))) + + print('matrix:') + print(json.dumps(matrix, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/.github/workflows/scripts/summary.py b/.github/workflows/scripts/summary.py new file mode 100644 index 00000000..f5170883 --- /dev/null +++ b/.github/workflows/scripts/summary.py @@ -0,0 +1,74 @@ +""" +A script to scan through all valid mod jars in build-artifacts.zip/$version/build/libs, +and generate an artifact summary table for that to GitHub action step summary +""" +__author__ = 'Fallen_Breath' + +import functools +import glob +import hashlib +import json +import os + +import jproperties + + +def read_prop(file_name: str, key: str) -> str: + configs = jproperties.Properties() + with open(file_name, 'rb') as f: + configs.load(f) + return configs[key].data + + +def get_sha256_hash(file_path: str) -> str: + sha256_hash = hashlib.sha256() + + with open(file_path, 'rb') as f: + for buf in iter(functools.partial(f.read, 4096), b''): + sha256_hash.update(buf) + + return sha256_hash.hexdigest() + + +def main(): + target_subproject_env = os.environ.get('TARGET_SUBPROJECT', '') + target_subprojects = list(filter(None, target_subproject_env.split(',') if target_subproject_env != '' else [])) + print('target_subprojects: {}'.format(target_subprojects)) + + with open('settings.json') as f: + settings: dict = json.load(f) + + with open(os.environ['GITHUB_STEP_SUMMARY'], 'w') as f: + f.write('## Build Artifacts Summary\n\n') + f.write('| Subproject | for Minecraft | File | Size | SHA-256 |\n') + f.write('| --- | --- | --- | --- | --- |\n') + + warnings = [] + for subproject in settings['versions']: + if len(target_subprojects) > 0 and subproject not in target_subprojects: + print('skipping {}'.format(subproject)) + continue + game_versions = read_prop('versions/{}/gradle.properties'.format(subproject), 'game_versions') + game_versions = game_versions.strip().replace('\r', '').replace('\n', ', ') + file_paths = glob.glob('build-artifacts/{}/build/libs/*.jar'.format(subproject)) + file_paths = list(filter(lambda fp: not fp.endswith('-sources.jar') and not fp.endswith('-dev.jar') and not fp.endswith('-shadow.jar'), file_paths)) + if len(file_paths) == 0: + file_name = '*not found*' + sha256 = '*N/A*' + else: + file_name = '`{}`'.format(os.path.basename(file_paths[0])) + file_size = '{} B'.format(os.path.getsize(file_paths[0])) + sha256 = '`{}`'.format(get_sha256_hash(file_paths[0])) + if len(file_paths) > 1: + warnings.append('Found too many build files in subproject {}: {}'.format(subproject, ', '.join(file_paths))) + + f.write('| {} | {} | {} | {} | {} |\n'.format(subproject, game_versions, file_name, file_size, sha256)) + + if len(warnings) > 0: + f.write('\n### Warnings\n\n') + for warning in warnings: + f.write('- {}\n'.format(warning)) + + +if __name__ == '__main__': + main() diff --git a/build.gradle b/build.gradle index 14f5dae9..4013545d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.9-SNAPSHOT' apply false + id 'fabric-loom' version '1.10-SNAPSHOT' apply false // https://github.com/ReplayMod/preprocessor // https://github.com/Fallen-Breath/preprocessor id 'com.replaymod.preprocess' version '9d21b334a7' @@ -23,6 +23,7 @@ preprocess { def mc1211 = createNode('1.21.1', 1_21_01, 'yarn') def mc1213 = createNode('1.21.3', 1_21_03, 'yarn') def mc1214 = createNode('1.21.4', 1_21_04, 'yarn') + def mc1215 = createNode('1.21.5', 1_21_05, 'yarn') // mapping difference map // base 1194 -> 1193 -> 1192 ->1190-> 118 -> 117 -> 116 @@ -34,6 +35,7 @@ preprocess { mc1211.link(mc1210, file('versions/mapping-1.21.1-1.21.0.txt')) mc1213.link(mc1211, file('versions/mapping-1.21.3-1.21.1.txt')) mc1214.link(mc1213, file('versions/mapping-1.21.4-1.21.3.txt')) + mc1215.link(mc1214, file('versions/mapping-1.21.5-1.21.4.txt')) //mc1194.link(mc1193, file('versions/mapping-1.19.4-1.19.3.txt')) //mc1193.link(mc1192, file('versions/mapping-1.19.3-1.19.2.txt')) //mc1192.link(mc1190, file('versions/mapping-1.19.2-1.19.0.txt')) @@ -67,7 +69,7 @@ tasks.register('buildAndGather') { exclude '*-dev.jar', '*-sources.jar', '*-shadow.jar' } into buildLibs(rootProject) - duplicatesStrategy DuplicatesStrategy.INCLUDE + duplicatesStrategy = DuplicatesStrategy.INCLUDE } } } diff --git a/common.gradle b/common.gradle index cb93286c..36666070 100644 --- a/common.gradle +++ b/common.gradle @@ -3,22 +3,16 @@ apply plugin: 'com.replaymod.preprocess' int mcVersion = project.mcVersion -//preprocess { - //mcVersion = vars.get()["MC"] as Integer -//} - repositories { mavenCentral() - maven { url "https://maven.terraformersmc.com/releases/" } - maven { url "https://jitpack.io" } - maven { url "https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1" } - maven { url 'https://masa.dy.fi/maven' } - maven { url = "https://www.cursemaven.com" } - maven { url "https://maven.terraformersmc.com/releases/" } - maven { - url "https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1" - } - //maven { url 'https://maven.fallenbreath.me/releases' } + maven { url = 'https://maven.terraformersmc.com/releases/' } + maven { url = 'https://jitpack.io' } + maven { url = 'https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1' } + maven { url = 'https://masa.dy.fi/maven' } + maven { url = 'https://www.cursemaven.com' } + maven { url = 'https://maven.terraformersmc.com/releases/' } + maven { url = 'https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1' } + maven { url = 'https://maven.fallenbreath.me/releases' } } // https://github.com/FabricMC/fabric-loader/issues/783 @@ -66,7 +60,7 @@ dependencies { } // [FEATURE] MIXIN_AUDITOR - //modRuntimeOnly 'me.fallenbreath:mixin-auditor:0.1.0' +// modRuntimeOnly 'me.fallenbreath:mixin-auditor:0.1.0' //include(modImplementation("me.fallenbreath:conditional-mixin-fabric:${project.conditionalmixin_version}")) //include(annotationProcessor(implementation("io.github.llamalad7:mixinextras-fabric:${project.mixinextras_version}"))) } @@ -93,14 +87,14 @@ loom { vmArgs commonVmArgs } // [FEATURE] MIXIN_AUDITOR - //runs { - //def auditVmArgs = [*commonVmArgs, '-DmixinAuditor.audit=true'] - //serverMixinAudit { - //server() - //vmArgs auditVmArgs - //ideConfigGenerated false - //} - //} +// runs { +// def auditVmArgs = [*commonVmArgs, '-DmixinAuditor.audit=true'] +// clientMixinAudit { +// client() +// vmArgs auditVmArgs +// ideConfigGenerated false +// } +// } } String modVersionSuffix = '' diff --git a/gradle.properties b/gradle.properties index 2eff9be7..c7e08c1d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,12 +2,12 @@ org.gradle.jvmargs=-Xmx6G # Fabric Basic Properties - loader_version=0.16.9 + loader_version=0.16.12 # Mod Properties mod_id = litematica-printer mod_name = Litematica Printer - mod_version = 7.2.2 + mod_version = 7.2.3 maven_group = aria1th.extensions archives_base_name = litematica-printer diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..9bbc975c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c82..37f853b1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d..faf93008 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/jitpack.yml b/jitpack.yml index df53b48a..c7b708da 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,3 +1,3 @@ before_install: - - sdk install java 21.0.5-tem - - sdk use java 21.0.5-tem + - sdk install java 21.0.6-tem + - sdk use java 21.0.6-tem diff --git a/settings.json b/settings.json index e2a74870..84445aa3 100644 --- a/settings.json +++ b/settings.json @@ -8,6 +8,7 @@ "1.21.0", "1.21.1", "1.21.3", - "1.21.4" + "1.21.4", + "1.21.5" ] } \ No newline at end of file diff --git a/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java b/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java index c7ae39c7..d9866e89 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java @@ -19,8 +19,8 @@ import static io.github.eatmyvenom.litematicin.utils.InventoryUtils.ITEMS; @Mixin(value = ChunkRendererSchematicVbo.class, priority = 1200) -public class ChunkRendererSchematicVboMixin { - +public class ChunkRendererSchematicVboMixin +{ @Shadow protected ChunkCacheSchematic schematicWorldView; diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/BedrockBreaker.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/BedrockBreaker.java index f2e0159c..cd73a4ec 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/BedrockBreaker.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/BedrockBreaker.java @@ -265,7 +265,11 @@ public static void placePiston(MinecraftClient mc, BlockPos pos, Direction facin final ItemStack PistonStack = Items.PISTON.getDefaultStack(); InventoryUtils.swapToItem(mc, PistonStack); if (sync) { + //#if MC>=12105 + //$$ mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(mc).getSelectedSlot())); + //#else mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(mc).selectedSlot)); + //#endif } MessageHolder.sendDebugMessage("Places piston at " + pos.toShortString() + " with facing " + facing); //mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(mc.player.getInventory().selectedSlot)); @@ -458,7 +462,11 @@ public static void switchTool(MinecraftClient mc) { MessageHolder.sendDebugMessage("Swaps to Pickaxe " + stack); InventoryUtils.swapToItem(mc, stack); MessageHolder.sendDebugMessage("Holding stack " + mc.player.getMainHandStack()); + //#if MC>=12105 + //$$ mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(mc).getSelectedSlot())); + //#else mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(mc).selectedSlot)); + //#endif } diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/Breaker.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/Breaker.java index 6e862334..1e5509e9 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/Breaker.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/Breaker.java @@ -35,6 +35,10 @@ public Breaker() { public boolean startBreakingBlock(BlockPos pos, MinecraftClient mc) { this.breakingBlock = true; this.pos = pos; + if (mc.world == null || mc.player == null) + { + return false; + } // Check for best tool in inventory if (mc.world.getBlockState(pos).getHardness(mc.world, pos) == 0) { mc.interactionManager.attackBlock(pos, Direction.UP); @@ -78,6 +82,10 @@ public static int getBestItemSlotIdToMineBlock(MinecraftClient mc, BlockPos bloc } private static int getFastestToolSlot(MinecraftClient mc, int bestSlot, float bestSpeed, BlockState state) { + if (mc.player == null) + { + return bestSlot; + } for (int i = InventoryUtils.getInventory(mc.player).size(); i >= 0; i--) { float speed = getBlockBreakingSpeed(state, mc, i); if ((speed > bestSpeed && speed > 1.0F) @@ -99,7 +107,11 @@ public static float getBlockBreakingSpeed(BlockState block, MinecraftClient mc, if (slotId < -1 || slotId >= 36) { return 0; } + //#if MC>=12105 + //$$ float f = InventoryUtils.getInventory(mc.player).getMainStacks().get(slotId).getMiningSpeedMultiplier(block); + //#else float f = InventoryUtils.getInventory(mc.player).main.get(slotId).getMiningSpeedMultiplier(block); + //#endif if (f > 1.0F) { //#if MC>=12102 //$$ ItemStack itemStack = mc.player.getInventory().getMainHandStack(); diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/FakeAccurateBlockPlacement.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/FakeAccurateBlockPlacement.java index cba73aa1..47cacfbb 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/FakeAccurateBlockPlacement.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/FakeAccurateBlockPlacement.java @@ -373,7 +373,7 @@ else if (blockState.isOf(Blocks.LIGHTNING_ROD)) { placeBlock(blockPos, blockState); return true; } - Direction facing = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(blockState); //facing of block itself + Direction facing = Printer.getSimplifiedFirstPropertyFacingValue(blockState); //facing of block itself if (facing == null && blockState.getBlock() instanceof AbstractRailBlock) { facing = Printer.convertRailShapetoFace(blockState); } else if (blockState.getBlock() instanceof TorchBlock) { @@ -517,8 +517,8 @@ public static boolean canPlace(BlockState state, BlockPos pos) { MessageHolder.sendOrderMessage("No stateGrindStone was found"); return false; } else if (handlingState != null && (handlingState.getBlock() instanceof FacingBlock || handlingState.getBlock() instanceof HorizontalFacingBlock && !(handlingState.getBlock() instanceof WallMountedBlock))) { - Direction handling = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(handlingState); - Direction other = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(state); + Direction handling = Printer.getSimplifiedFirstPropertyFacingValue(handlingState); + Direction other = Printer.getSimplifiedFirstPropertyFacingValue(state); return handling == other; } return true; @@ -570,7 +570,7 @@ synchronized private static boolean placeBlock(BlockPos pos, BlockState blockSta if (pickedItem.getItem() == currentHandling && Printer.doSchematicWorldPickBlock(minecraftClient, blockState, pos)) { MessageHolder.sendOrderMessage("Placing " + blockState.getBlock().getTranslationKey() + " at " + pos.toShortString() + " stack at hand is " + player.getMainHandStack()); - MessageHolder.sendDebugMessage(player, "Placing " + blockState.getBlock().getTranslationKey() + " at " + pos.toShortString() + " facing : " + fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(blockState)); + MessageHolder.sendDebugMessage(player, "Placing " + blockState.getBlock().getTranslationKey() + " at " + pos.toShortString() + " facing : " + Printer.getSimplifiedFirstPropertyFacingValue(blockState)); MessageHolder.sendDebugMessage(player, "Player facing is set to : " + fakeDirection + " Yaw : " + fakeYaw + " Pitch : " + fakePitch + " ticks : " + requestedTicks + " for pos " + pos.toShortString()); interactBlock(minecraftClient, blockHitResult); InventoryUtils.decrementCount(isCreative(player)); diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java index b659e896..33d576f9 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java @@ -3,6 +3,9 @@ import fi.dy.masa.litematica.config.Configs; import fi.dy.masa.litematica.config.Hotkeys; import fi.dy.masa.litematica.materials.MaterialCache; +//#if MC>=12105 +//$$ import fi.dy.masa.malilib.util.EquipmentUtils; +//#endif import io.github.eatmyvenom.litematicin.LitematicaMixinMod; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; @@ -94,7 +97,9 @@ private static void calculateCache() { if (client == null || client.player == null || client.world == null) { return; } - //#if MC>=11700 + //#if MC>=12105 + //$$ for (ItemStack stack : client.player.getInventory().getMainStacks()) { + //#elseif MC>=11700 for (ItemStack stack : client.player.getInventory().main) { //#else //$$ for (ItemStack stack : client.player.inventory.main) { @@ -158,7 +163,9 @@ public static boolean isCreative(ClientPlayerEntity player) { public static boolean isToolLikeItem(Item item) { // ToolItem or FlintAndSteelItem or ShearsItem - //#if MC>=12102 + //#if MC>=12105 + //$$ return item instanceof FlintAndSteelItem || item instanceof ShearsItem; + //#elseif MC>=12102 //$$ return item instanceof MiningToolItem || item instanceof FlintAndSteelItem || item instanceof ShearsItem; //#else return item instanceof ToolItem || item instanceof FlintAndSteelItem || item instanceof ShearsItem; diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/ItemInputs.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/ItemInputs.java index db99e61c..457d4448 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/ItemInputs.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/ItemInputs.java @@ -55,7 +55,11 @@ public static boolean matchStacks(List required, List current, copiedStack.decrement(current.get(i).getStack().getCount()); copy.add(copiedStack); } + //#if MC>=12105 + //$$ int[] countArray = player.getInventory().getMainStacks().stream().mapToInt(ItemStack::getCount).toArray(); + //#else int[] countArray = player.getInventory().main.stream().mapToInt(ItemStack::getCount).toArray(); + //#endif for (ItemStack itemStack : copy) { if (itemStack.isEmpty()) { continue; @@ -63,7 +67,11 @@ public static boolean matchStacks(List required, List current, //MessageHolder.sendUniqueDebugMessage("Checking for stack " + itemStack); int requiredAmount = itemStack.getCount(); int i = 0; + //#if MC>=12105 + //$$ for (ItemStack playerStacks : player.getInventory().getMainStacks()) { + //#else for (ItemStack playerStacks : player.getInventory().main) { + //#endif if (countArray[i] <= 0) { i++; continue; diff --git a/src/main/java/io/github/eatmyvenom/litematicin/utils/Printer.java b/src/main/java/io/github/eatmyvenom/litematicin/utils/Printer.java index 7057a474..22032b9c 100644 --- a/src/main/java/io/github/eatmyvenom/litematicin/utils/Printer.java +++ b/src/main/java/io/github/eatmyvenom/litematicin/utils/Printer.java @@ -11,10 +11,15 @@ import fi.dy.masa.litematica.util.*; import fi.dy.masa.litematica.util.RayTraceUtils.RayTraceWrapper; import fi.dy.masa.litematica.world.SchematicWorldHandler; +//#if MC>=12105 +//$$ import fi.dy.masa.malilib.util.EquipmentUtils; +//#endif import fi.dy.masa.malilib.util.IntBoundingBox; import fi.dy.masa.malilib.util.LayerRange; import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import org.jetbrains.annotations.Nullable; + import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.block.*; @@ -99,14 +104,24 @@ private static boolean simulateFacingData(BlockState state, BlockPos blockPos, V } catch (Exception e) { //doors wtf MessageHolder.sendMessageUncheckedUnique("Cannot get tested orientation of given block "+ state.getBlock().getName()); //fallback to player horizontal facing... - return player.getHorizontalFacing() == fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(state); + return player.getHorizontalFacing() == getSimplifiedFirstPropertyFacingValue(state); } if (testState == null) { MessageHolder.sendMessageUncheckedUnique("Cannot get tested orientation of given block "+ state.getBlock().getName()); - return player.getHorizontalFacing() == fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(state); + return player.getHorizontalFacing() == getSimplifiedFirstPropertyFacingValue(state); } - Direction testFacing = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(testState); - return testFacing == fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(state); + Direction testFacing = getSimplifiedFirstPropertyFacingValue(testState); + return testFacing == getSimplifiedFirstPropertyFacingValue(state); + } + + @Nullable + public static Direction getSimplifiedFirstPropertyFacingValue(BlockState stateIn) + { + //#if MC>=12105 + //$$ return fi.dy.masa.malilib.util.game.BlockUtils.getFirstPropertyFacingValue(stateIn).orElse(null); + //#else + return fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateIn); + //#endif } public static boolean canPickBlock(MinecraftClient mc, BlockState preference, BlockPos pos) { @@ -123,7 +138,9 @@ public static boolean canPickBlock(MinecraftClient mc, BlockState preference, Bl if (!stack.isEmpty() && stack.getItem() != Items.AIR) { PlayerInventory inv = getInventory(mc.player); if (!isCreative(mc.player)) { - //#if MC>=12102 + //#if MC>=12105 + //$$ if (EquipmentUtils.isRegularTool(stack) || stack.getItem() instanceof FlintAndSteelItem) { + //#elseif MC>=12102 //$$ if (stack.getItem() instanceof MiningToolItem || stack.getItem() instanceof FlintAndSteelItem) { //#else if (stack.getItem() instanceof ToolItem || stack.getItem() instanceof FlintAndSteelItem) { @@ -677,10 +694,8 @@ synchronized public static ActionResult doPrinterAction(MinecraftClient mc) { Block sBlock = stateSchematic.getBlock(); // Blocks are equal, but have different states if (cBlock.getName().equals(sBlock.getName())) { - Direction facingSchematic = fi.dy.masa.malilib.util.BlockUtils - .getFirstPropertyFacingValue(stateSchematic); - Direction facingClient = fi.dy.masa.malilib.util.BlockUtils - .getFirstPropertyFacingValue(stateClient); + Direction facingSchematic = getSimplifiedFirstPropertyFacingValue(stateSchematic); + Direction facingClient = getSimplifiedFirstPropertyFacingValue(stateClient); if (facingSchematic == facingClient) { int clickTimes = 0; @@ -834,8 +849,8 @@ else if (stateSchematic.isOf(Blocks.FARMLAND) && PRINTER_PRINT_DIRT_VARIANTS.get (Objects.equals(ClientRailShape, "east_west") || Objects.equals(ClientRailShape, "north_south")); } } else if (sBlock instanceof ObserverBlock || sBlock instanceof PistonBlock || sBlock instanceof RepeaterBlock || sBlock instanceof ComparatorBlock || sBlock instanceof FenceGateBlock || sBlock instanceof TrapdoorBlock) { - Direction facingSchematic = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); - Direction facingClient = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateClient); + Direction facingSchematic = getSimplifiedFirstPropertyFacingValue(stateSchematic); + Direction facingClient = getSimplifiedFirstPropertyFacingValue(stateClient); ShouldFix = facingSchematic != facingClient; ShapeBoolean = facingClient.getOpposite().equals(facingSchematic); } @@ -1079,7 +1094,7 @@ else if (stateSchematic.isOf(Blocks.FARMLAND) && PRINTER_PRINT_DIRT_VARIANTS.get return ActionResult.SUCCESS; } } - Direction facing = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); + Direction facing = getSimplifiedFirstPropertyFacingValue(stateSchematic); if (facing != null) { facing = facing.getOpposite(); } @@ -1267,7 +1282,7 @@ else if (stateSchematic.isOf(Blocks.FARMLAND) && PRINTER_PRINT_DIRT_VARIANTS.get } continue; } else if (canPlaceFace(FacingData.getFacingData(stateSchematic), stateSchematic, primaryFacing, horizontalFacing)) { // no gui - Direction required = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); + Direction required = getSimplifiedFirstPropertyFacingValue(stateSchematic); required = applyPlacementFacing(stateSchematic, required, stateClient); Vec3d hitVec = applyHitVec(npos, stateSchematic, required); if (doSchematicWorldPickBlock(mc, stateSchematic, pos)) { @@ -2217,7 +2232,7 @@ private static boolean ObserverUpdateOrder(MinecraftClient mc, World world, Bloc if (stateSchematic.get(ObserverBlock.POWERED)) { return false; } - Direction facingSchematic = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); + Direction facingSchematic = getSimplifiedFirstPropertyFacingValue(stateSchematic); assert facingSchematic != null; boolean observerCantAvoid = ObserverCantAvoid(mc, world, facingSchematic, pos); if (observerCantAvoid) { @@ -2262,7 +2277,7 @@ private static BlockPos ObserverUpdateOrderPos(MinecraftClient mc, World world, if (stateSchematic.get(ObserverBlock.POWERED)) { return null; } - Direction facingSchematic = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); + Direction facingSchematic = getSimplifiedFirstPropertyFacingValue(stateSchematic); assert facingSchematic != null; boolean observerCantAvoid = ObserverCantAvoid(mc, world, facingSchematic, pos); if (observerCantAvoid) { @@ -2309,7 +2324,7 @@ private static boolean canPlaceFace(FacingData facedata, BlockState stateSchemat if (stateSchematic.isOf(Blocks.GRINDSTONE)) { return true; } - Direction facing = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(stateSchematic); + Direction facing = getSimplifiedFirstPropertyFacingValue(stateSchematic); if (stateSchematic.getBlock() instanceof AbstractRailBlock) { facing = convertRailShapetoFace(stateSchematic); } @@ -2829,7 +2844,7 @@ public static Vec3d applyCarpetProtocolHitVec(BlockPos pos, BlockState state) { //$$ double y = pos.getY(); //$$ double z = pos.getZ(); //$$ Block block = state.getBlock(); - //$$ Direction facing = fi.dy.masa.malilib.util.BlockUtils.getFirstPropertyFacingValue(state); + //$$ Direction facing = getSimplifiedFirstPropertyFacingValue(state); //$$ int railEnumCode = getRailShapeOrder(state); //$$ final int propertyIncrement = 16; //$$ if (facing == null && railEnumCode == 32 && !(block instanceof SlabBlock)) { diff --git a/versions/1.20.6/gradle.properties b/versions/1.20.6/gradle.properties index 1a22994b..770bc46d 100644 --- a/versions/1.20.6/gradle.properties +++ b/versions/1.20.6/gradle.properties @@ -2,7 +2,7 @@ # check these on https://fabricmc.net/use minecraft_version=1.20.6 yarn_mappings=1.20.6+build.3 - fabricapi_version=0.97.8+1.20.6 + fabricapi_version=0.100.8+1.20.6 minecraft_version_out = 1.20.6 # Fabric Mod Metadata @@ -18,5 +18,5 @@ malilib_fileid = 1.20.6-0.19.1 # (Jitpack.io) https://github.com/sakura-ryoko/litematica litematica_projectid = "sakura-jitpack" - litematica_fileid = 1.20.6-0.18.2 + litematica_fileid = d02978908f essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.0/gradle.properties b/versions/1.21.0/gradle.properties index 6218eeab..44e7f06d 100644 --- a/versions/1.21.0/gradle.properties +++ b/versions/1.21.0/gradle.properties @@ -2,7 +2,7 @@ # check these on https://fabricmc.net/use minecraft_version=1.21 yarn_mappings=1.21+build.9 - fabricapi_version=0.100.1+1.21 + fabricapi_version=0.102.0+1.21 minecraft_version_out = 1.21 # Fabric Mod Metadata @@ -15,8 +15,8 @@ # Mod Properties # (Jitpack.io) https://github.com/sakura-ryoko/malilib malilib_projectid = "sakura-jitpack" - malilib_fileid = 1.21-0.21.5-sakura.1 + malilib_fileid = 1.21-0.21.7 # (Jitpack.io) https://github.com/sakura-ryoko/litematica litematica_projectid = "sakura-jitpack" - litematica_fileid = 1.21-0.19.54-sakura.1 + litematica_fileid = 1.21-0.19.56 essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.1/gradle.properties b/versions/1.21.1/gradle.properties index 1595746f..7d15234e 100644 --- a/versions/1.21.1/gradle.properties +++ b/versions/1.21.1/gradle.properties @@ -2,7 +2,7 @@ # check these on https://fabricmc.net/use minecraft_version=1.21.1 yarn_mappings=1.21.1+build.3 - fabricapi_version=0.102.1+1.21.1 + fabricapi_version=0.115.0+1.21.1 minecraft_version_out = 1.21.1 # Fabric Mod Metadata @@ -15,8 +15,8 @@ # Mod Properties # (Jitpack.io) https://github.com/sakura-ryoko/malilib malilib_projectid = "sakura-jitpack" - malilib_fileid = 1.21-0.21.5-sakura.1 + malilib_fileid = 1.21-0.21.7 # (Jitpack.io) https://github.com/sakura-ryoko/litematica litematica_projectid = "sakura-jitpack" - litematica_fileid = 1.21-0.19.54-sakura.1 + litematica_fileid = 1.21-0.19.56 essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.3/gradle.properties b/versions/1.21.3/gradle.properties index 62eb8111..d83c7842 100644 --- a/versions/1.21.3/gradle.properties +++ b/versions/1.21.3/gradle.properties @@ -2,7 +2,7 @@ # check these on https://fabricmc.net/use minecraft_version=1.21.3 yarn_mappings=1.21.3+build.2 - fabricapi_version=0.106.1+1.21.3 + fabricapi_version=0.114.0+1.21.3 minecraft_version_out = 1.21.3 # Fabric Mod Metadata @@ -15,8 +15,8 @@ # Mod Properties # (Jitpack.io) https://github.com/sakura-ryoko/malilib malilib_projectid = "sakura-jitpack" - malilib_fileid = 1.21.3-0.22.3-sakura.1 + malilib_fileid = 1.21.3-0.22.5 # (Jitpack.io) https://github.com/sakura-ryoko/litematica litematica_projectid = "sakura-jitpack" - litematica_fileid = 1.21.3-0.20.2-sakura.1 + litematica_fileid = 1.21.3-0.20.4 essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.4/gradle.properties b/versions/1.21.4/gradle.properties index 4b38a77a..253169ac 100644 --- a/versions/1.21.4/gradle.properties +++ b/versions/1.21.4/gradle.properties @@ -1,8 +1,8 @@ # Fabric Properties # check these on https://fabricmc.net/use minecraft_version=1.21.4 - yarn_mappings=1.21.4+build.1 - fabricapi_version=0.110.5+1.21.4 + yarn_mappings=1.21.4+build.8 + fabricapi_version=0.117.0+1.21.4 minecraft_version_out = 1.21.4 # Fabric Mod Metadata @@ -15,8 +15,8 @@ # Mod Properties # (Jitpack.io) https://github.com/sakura-ryoko/malilib malilib_projectid = "sakura-jitpack" - malilib_fileid = 1.21.4-0.23.0-sakura.1 + malilib_fileid = 1.21.4-0.23.2 # (Jitpack.io) https://github.com/sakura-ryoko/litematica litematica_projectid = "sakura-jitpack" - litematica_fileid = 1.21.4-0.21.0-sakura.1 + litematica_fileid = 1.21.4-0.21.2 essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.5/gradle.properties b/versions/1.21.5/gradle.properties new file mode 100644 index 00000000..2734b4dd --- /dev/null +++ b/versions/1.21.5/gradle.properties @@ -0,0 +1,22 @@ +# Fabric Properties + # check these on https://fabricmc.net/use + minecraft_version=1.21.5 + yarn_mappings=1.21.5+build.1 + fabricapi_version=0.119.6+1.21.5 + minecraft_version_out = 1.21.5 + +# Fabric Mod Metadata + minecraft_dependency=1.21.5 + +# Build Information +# The target mc versions for the mod during mod publishing, separated with \n + game_versions=1.21.5 + +# Mod Properties + # (Jitpack.io) https://github.com/sakura-ryoko/malilib + malilib_projectid = "sakura-jitpack" + malilib_fileid = 1.21.5-0.24.0-sakura.5 + # (Jitpack.io) https://github.com/sakura-ryoko/litematica + litematica_projectid = "sakura-jitpack" + litematica_fileid = 1.21.5-0.22.0-sakura.5 + essentialclient_filename = essential-client-1.20.1-1.3.6.jar diff --git a/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java new file mode 100644 index 00000000..d4f33595 --- /dev/null +++ b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/ChunkRendererSchematicVboMixin.java @@ -0,0 +1,40 @@ +package io.github.eatmyvenom.litematicin.mixin.Litematica; + +import java.util.Set; + +import net.minecraft.block.BlockState; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.Item; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import fi.dy.masa.litematica.render.schematic.BufferAllocatorCache; +import fi.dy.masa.litematica.render.schematic.ChunkCacheSchematic; +import fi.dy.masa.litematica.render.schematic.ChunkRenderDataSchematic; +import fi.dy.masa.litematica.render.schematic.ChunkRendererSchematicVbo; + +import static io.github.eatmyvenom.litematicin.LitematicaMixinMod.RENDER_ONLY_HOLDING_ITEMS; +import static io.github.eatmyvenom.litematicin.utils.InventoryUtils.ITEMS; + +@Mixin(value = ChunkRendererSchematicVbo.class, priority = 1200) +public class ChunkRendererSchematicVboMixin { + + @Shadow + protected ChunkCacheSchematic schematicWorldView; + + @Inject(method = "renderBlocksAndOverlay", at = @At("HEAD"), cancellable = true, remap = false) + private void onRenderBlocksAndOverlay(BlockPos pos, ChunkRenderDataSchematic data, BufferAllocatorCache allocators, Set usedLayers, MatrixStack matrixStack, CallbackInfo ci) { + if (!RENDER_ONLY_HOLDING_ITEMS.getBooleanValue()) return; + BlockState stateSchematic = this.schematicWorldView.getBlockState(pos); + Item item = stateSchematic.getBlock().asItem(); + if (!ITEMS.contains(item)) { + ci.cancel(); + } + } + +} diff --git a/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/SchematicVerifierMixin.java b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/SchematicVerifierMixin.java new file mode 100644 index 00000000..a39bcda2 --- /dev/null +++ b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/mixin/Litematica/SchematicVerifierMixin.java @@ -0,0 +1,79 @@ +package io.github.eatmyvenom.litematicin.mixin.Litematica; + +import java.util.HashSet; +import com.google.common.collect.ArrayListMultimap; +import io.github.eatmyvenom.litematicin.LitematicaMixinMod; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import fi.dy.masa.litematica.schematic.verifier.SchematicVerifier; +import fi.dy.masa.litematica.util.ItemUtils; +import fi.dy.masa.litematica.world.WorldSchematic; + +@Mixin(SchematicVerifier.class) +public class SchematicVerifierMixin { + @Shadow + private WorldSchematic worldSchematic; + + @Shadow + @Final + private ArrayListMultimap, BlockPos> wrongStatesPositions; + + @Shadow + @Final + private static MutablePair MUTABLE_PAIR; + + @Shadow + @Final + private HashSet> ignoredMismatches; + + @Shadow + @Final + private Object2ObjectOpenHashMap blockMismatches; + + @Shadow + private ClientWorld worldClient; + + @Inject(method = "checkBlockStates", at = @At("HEAD"), cancellable = true) + private void handleInventory(int x, int y, int z, BlockState stateSchematic, BlockState stateClient, CallbackInfo ci) { + if (!LitematicaMixinMod.VERIFY_INVENTORY.getBooleanValue()) { + return; + } + MUTABLE_PAIR.setLeft(stateSchematic); + MUTABLE_PAIR.setRight(stateClient); + if (!this.ignoredMismatches.contains(MUTABLE_PAIR)) { + if (stateClient == stateSchematic) { + if (stateSchematic.getBlock() instanceof BlockWithEntity) { + WorldSchematic schematic = this.worldSchematic; + BlockPos pos = new BlockPos(x, y, z); + BlockEntity entity = schematic.getBlockEntity(pos); + if (entity instanceof LootableContainerBlockEntity containerBlockEntity) { + if (!containerBlockEntity.isEmpty()) { + SchematicVerifier.BlockMismatch mismatch = new SchematicVerifier.BlockMismatch(SchematicVerifier.MismatchType.WRONG_STATE, stateSchematic, stateClient, 1); + this.wrongStatesPositions.put(Pair.of(stateSchematic, stateClient), new BlockPos(x, y, z)); + this.blockMismatches.put(pos, mismatch); + ItemUtils.setItemForBlock(this.worldClient, pos, stateClient); + ItemUtils.setItemForBlock(this.worldSchematic, pos, stateSchematic); + ci.cancel(); + } + } + } + } + } + } +} + diff --git a/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java new file mode 100644 index 00000000..2106ad22 --- /dev/null +++ b/versions/1.21.5/src/main/java/io/github/eatmyvenom/litematicin/utils/InventoryUtils.java @@ -0,0 +1,623 @@ +package io.github.eatmyvenom.litematicin.utils; + +import java.util.*; +import java.util.function.Predicate; +import io.github.eatmyvenom.litematicin.LitematicaMixinMod; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.ContainerComponent; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.*; +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; + +import fi.dy.masa.malilib.util.EquipmentUtils; +import fi.dy.masa.litematica.config.Configs; +import fi.dy.masa.litematica.config.Hotkeys; +import fi.dy.masa.litematica.materials.MaterialCache; + +import static io.github.eatmyvenom.litematicin.LitematicaMixinMod.*; +import static io.github.eatmyvenom.litematicin.utils.Printer.*; + +public class InventoryUtils +{ + private static int ptr = -1; + + public final static HashSet ITEMS = new HashSet<>(); + public static int lastCount = 0; + public static int itemChangeCount = 0; + public static Item handlingItem = null; + public static Item previousItem = null; //only used for checks + public static int trackedSelectedSlot = -1; + + private static long tickCount = 0; + private static long lastWorkedTick = 0; + + private static String cachedPickBlockableSlots = ""; + + private static final HashSet pickBlockableSlots = new HashSet<>(); + public static HashMap usedSlots = new LinkedHashMap<>(); + public static HashMap slotCounts = new LinkedHashMap<>(); + + public static void tick() { + tickCount++; + if (RENDER_ONLY_HOLDING_ITEMS.getBooleanValue() && tickCount % 20 == 0) { + calculateCache(); + } + if (INVENTORY_CACHE_TICKS.getIntegerValue() != 0 && tickCount - lastWorkedTick > INVENTORY_CACHE_TICKS.getIntegerValue()){ + clearCache(); + } + if (!isSleeping && Configs.Generic.EASY_PLACE_MODE.getBooleanValue() && Configs.Generic.EASY_PLACE_HOLD_ENABLED.getBooleanValue() && Hotkeys.EASY_PLACE_ACTIVATION.getKeybind().isKeybindHeld()) { + for (int i = 0; i < 9; i++) { + if (!usedSlots.containsKey(i)) { + continue; + } + if (slotCounts.getOrDefault(i,0) <= 0) { + usedSlots.remove(i); + slotCounts.remove(i); + } + } + } else { + clearCache(); + } + } + + public static void clearCache(){ + if (!usedSlots.isEmpty()) MessageHolder.sendOrderMessage("Clearing cache"); + trackedSelectedSlot = -1; + previousItem = null; + handlingItem = null; + usedSlots.clear(); + slotCounts.clear(); + lastWorkedTick = tickCount; + } + + + @SuppressWarnings("PatternVariableCanBeUsed") // because for compatibility with 1.16.5 + private static void calculateCache() { + ITEMS.clear(); + MinecraftClient client = MinecraftClient.getInstance(); + if (client == null || client.player == null || client.world == null) { + return; + } + //#if MC>=12105 + for (ItemStack stack : client.player.getInventory().getMainStacks()) { + //#elseif MC>=11700 + //$$ for (ItemStack stack : client.player.getInventory().main) { + //#else + //$$ for (ItemStack stack : client.player.inventory.main) { + //#endif + Item item = stack.getItem(); + if (item != null) { + ITEMS.add(item); + } + if (item instanceof BlockItem) { + BlockItem blockItem = (BlockItem) item; + if (blockItem.getBlock() instanceof ShulkerBoxBlock) { + int invSize = 27; + //#if MC >= 12006 + DefaultedList returnStacks = DefaultedList.ofSize(invSize, ItemStack.EMPTY); + ContainerComponent container = stack.getComponents().get(DataComponentTypes.CONTAINER); + if (container != null) { + container.copyTo(returnStacks); + for (ItemStack returnStack : returnStacks) { + Item returnItem = returnStack.getItem(); + if (returnItem != null) { + ITEMS.add(returnItem); + } + } + } + //#else + //$$ NbtCompound compound = stack.getSubNbt("BlockEntityTag"); + //$$ if (compound == null) { + //$$ continue; + //$$ } + //$$ DefaultedList returnStacks = DefaultedList.ofSize(invSize, ItemStack.EMPTY); + //$$ if (compound.contains("Items")) { + //$$ Inventories.readNbt(compound, returnStacks); + //$$ } + //$$ for (ItemStack returnStack : returnStacks) { + //$$ Item returnItem = returnStack.getItem(); + //$$ if (returnItem != null) { + //$$ ITEMS.add(returnItem); + //$$ } + //$$ } + //#endif + } + } + } + } + + public static PlayerInventory getInventory(ClientPlayerEntity player) { + //#if MC>=11700 + return player.getInventory(); + //#else + //$$ return player.inventory; + //#endif + } + + public static boolean isCreative(ClientPlayerEntity player) { + //#if MC>=11700 + return player.getAbilities().creativeMode; + //#else + //$$ return player.abilities.creativeMode; + //#endif + } + + public static boolean isToolLikeItem(Item item) { + // ToolItem or FlintAndSteelItem or ShearsItem + //#if MC>=12105 + return item instanceof FlintAndSteelItem || item instanceof ShearsItem; + //#elseif MC>=12102 + //$$ return item instanceof MiningToolItem || item instanceof FlintAndSteelItem || item instanceof ShearsItem; + //#else + //$$ return item instanceof ToolItem || item instanceof FlintAndSteelItem || item instanceof ShearsItem; + //#endif + } + + public static void decrementCount(boolean isCreative) { + if (isCreative) { + lastCount = 65536; + slotCounts.computeIfPresent(trackedSelectedSlot, (key, value) -> 65536); + return; + } + if (lastCount > 0 && usedSlots.get(trackedSelectedSlot) != null && !isToolLikeItem(usedSlots.get(trackedSelectedSlot))) { + lastCount--; + slotCounts.computeIfPresent(trackedSelectedSlot, (key, value) -> value - 1); + } + } + private static int getPtr() { + parsePickblockableSlots(); + if (pickBlockableSlots.isEmpty()) { + return -1; + } + ptr++; + ptr = ptr % pickBlockableSlots.size(); + return ptr; + } + + private static void parsePickblockableSlots() { + String pickBlockableSlot = Configs.Generic.PICK_BLOCKABLE_SLOTS.getStringValue(); + if (!pickBlockableSlot.equals(cachedPickBlockableSlots)) { + cachedPickBlockableSlots = pickBlockableSlot; + pickBlockableSlots.clear(); + for (String s : pickBlockableSlot.split(",")) { + try { + int i = Integer.parseInt(s); + if (i>0 && i<10) { + pickBlockableSlots.add(i-1); + } + } catch (NumberFormatException e) { + // ignore + } + } + } + } + + // getAvailableSlot() is used to get the slot that the item is in, or the next available slot if it's not in the hotbar + public static int getAvailableSlot(Item item) { + if (usedSlots.containsValue(item)) { + for (Integer i : usedSlots.keySet()) { + if (usedSlots.get(i) == item) { + return i; + } + } + return -1; + } + parsePickblockableSlots(); + if (usedSlots.size() == pickBlockableSlots.size()) { //full + return getPtr(); + } + for (int i = 0; i < 9; i++) { + if (usedSlots.containsKey(i) || !pickBlockableSlots.contains(i)) { + continue; + } + return i; + } + return -1; + } + + private static int searchSlot(Item item) { + for (Integer i : usedSlots.keySet()) { + if (usedSlots.get(i) == item && slotCounts.getOrDefault(i, 0) > 0) { + return i; + } + } + return -1; + } + + public static ItemStack getMainHandStack(ClientPlayerEntity player) { + return player.getMainHandStack(); + } + + public static boolean areItemsExact(ItemStack a, ItemStack b) { + // ToolItem or FlintAndSteelItem + return exceptToolItems(a, b); + } + + public static boolean areItemsExact(ItemStack a, ItemStack b, boolean allowNamed) { + if (allowNamed) { + return areItemsExactAllowNamed(a, b); + } + return exceptToolItems(a, b); + } + + private static boolean exceptToolItems(ItemStack a, ItemStack b) { + if (isToolLikeItem(a.getItem()) || isToolLikeItem(b.getItem())) { + return a.getItem() == b.getItem(); + } + boolean isItemEqual = ItemStack.areItemsEqual(a, b); + //#if MC >= 12006 + boolean nbtCondition = LitematicaMixinMod.PRINTER_IGNORE_NBT.getBooleanValue() || ItemStack.areItemsAndComponentsEqual(a, b); + //#else + //$$ boolean nbtCondition = PRINTER_IGNORE_NBT.getBooleanValue() || ItemStack.canCombine(a, b); + //#endif + return isItemEqual && nbtCondition; + } + + public static boolean areItemsExactCount(ItemStack a, ItemStack b, boolean allowNamed) { + if (a.getCount() != b.getCount()) { + return false; + } + if (allowNamed) { + return areItemsExactAllowNamed(a, b); + } + //#if MC >= 12006 + boolean nbtCondition = LitematicaMixinMod.PRINTER_IGNORE_NBT.getBooleanValue() || ItemStack.areItemsAndComponentsEqual(a, b); + //#else + //$$ boolean nbtCondition = PRINTER_IGNORE_NBT.getBooleanValue() || ItemStack.canCombine(a, b); + //#endif + return ItemStack.areItemsEqual(a, b) && nbtCondition; + } + + public static ItemStack getStackForState(MinecraftClient client, BlockState state, World world, BlockPos pos) { + // if state is nether portal block, return FLINT_AND_STEEL + if (state.isOf(Blocks.NETHER_PORTAL)) { + if (!PRINTER_LIT_PORTAL_USE_FIRECHARGE.getBooleanValue()) return Items.FLINT_AND_STEEL.getDefaultStack(); + else { + return Items.FIRE_CHARGE.getDefaultStack(); + } + } + ItemStack stack = isReplaceableWaterFluidSource(state) && PRINTER_PLACE_ICE.getBooleanValue() ? Items.ICE.getDefaultStack() : MaterialCache.getInstance().getRequiredBuildItemForState(state, world, pos); + if (PRINTER_PRINT_DIRT_VARIANTS.getBooleanValue() && !canPickItem(client, stack)) { + if (state.isOf(Blocks.FARMLAND)) stack = Items.DIRT.getDefaultStack(); + else if (state.isOf(Blocks.DIRT_PATH)) stack = Items.DIRT.getDefaultStack(); + } + return stack; + } + + public static boolean areItemsExactAllowNamed(ItemStack a, ItemStack b) { + // ToolItem or FlintAndSteelItem + if (isToolLikeItem(a.getItem()) || isToolLikeItem(b.getItem())) { + return a.getItem() == b.getItem(); + } + //#if MC>=12102 + else if (EquipmentUtils.isRegularTool(a) || EquipmentUtils.isRegularTool(b)) { + //#else + //$$ else if (a.getItem() instanceof ToolItem || b.getItem() instanceof ToolItem) { + //#endif + return false; // safety + } + //#if MC >= 12006 + return ItemStack.areItemsEqual(a, b) || a.getMaxCount() == b.getMaxCount() && a.contains(DataComponentTypes.CUSTOM_NAME) && b.contains(DataComponentTypes.CUSTOM_NAME); + //#else + //$$ return ItemStack.areItemsEqual(a, b) || a.getMaxCount() == b.getMaxCount() && a.hasCustomName() && b.hasCustomName(); + //#endif + } + + public static boolean requiresSwap(ClientPlayerEntity player, ItemStack stack) { + int selectedSlot = getInventory(player).getSelectedSlot(); + if (usedSlots.get(selectedSlot) != null) { + return stack.getItem() != usedSlots.get(selectedSlot) || slotCounts.getOrDefault(selectedSlot, 0) <= 0; + } + return previousItem == null || lastCount == 0 ? !areItemsExact(getMainHandStack(player), stack) : !areItemsExact(previousItem.getDefaultStack(), stack); + } + + public static boolean canSwap(ClientPlayerEntity player, ItemStack stack) { + if (isCreative(player)) { + return true; + } + int slotNum = getSlotWithStack(player, stack); + return slotNum != -1; + } + + public static int getSlotWithItem(PlayerInventory inv, ItemStack stack) { + for (int i = 0; i < inv.getMainStacks().size(); i++) { + if (ItemStack.areItemsEqual(inv.getStack(i), stack)) { + return i; + } + } + return -1; + } + + public static boolean canSwap(ClientPlayerEntity player, Predicate predicate) { + if (isCreative(player)) { + return true; + } + Inventory inv = getInventory(player); + for (int i = 0; i < inv.size(); i++) { + ItemStack stack = inv.getStack(i); + //#if MC>=12102 + if (EquipmentUtils.isRegularTool(stack) && predicate.test(stack)) { + //#else + //$$ if (stack.getItem() instanceof ToolItem && predicate.test(stack)) { + //#endif + return true; + } + } + return false; + } + + synchronized public static boolean swapToItem(MinecraftClient client, ItemStack stack) { + MessageHolder.sendOrderMessage("Trying to swap item into " + stack.getItem()); + ClientPlayerEntity player = client.player; + int maxChange = LitematicaMixinMod.PRINTER_MAX_ITEM_CHANGES.getIntegerValue(); + if (player == null || client.interactionManager == null) { + MessageHolder.sendOrderMessage("Player or interaction manager was null"); + return false; + } + //getInventory(player).updateItems(); + if (stack.getItem() != handlingItem) { + if (maxChange != 0 && itemChangeCount > maxChange) { + MessageHolder.sendOrderMessage("Exceeded item change count"); + return false; + } + } + if (!requiresSwap(player, stack)) { + assert trackedSelectedSlot == -1 || trackedSelectedSlot == getInventory(player).getSelectedSlot() : + "Selected slot changed for external reason! : expected " + trackedSelectedSlot + ", current " + getInventory(player).getSelectedSlot(); + assert previousItem == null || previousItem == stack.getItem() : "Handling item : " + handlingItem + " was not equal to " + stack.getItem(); + MessageHolder.sendOrderMessage("Didn't require swap for item " + stack.getItem() + " previous handling item : " + previousItem); + lastCount = isCreative(player) ? 65536 : getMainHandStack(player).getCount(); + if (usedSlots.containsValue(stack.getItem())) { + if (searchSlot(stack.getItem()) != trackedSelectedSlot) { + MessageHolder.sendMessageUncheckedUnique("Hotbar has duplicate item references, which should not happen!"); + } + } + trackedSelectedSlot = getInventory(player).getSelectedSlot(); + usedSlots.put(getInventory(player).getSelectedSlot(), getMainHandStack(player).getItem()); + slotCounts.put(getInventory(player).getSelectedSlot(), lastCount); + previousItem = stack.getItem(); + lastWorkedTick = tickCount; + return true; + } + if (usedSlots.containsValue(stack.getItem())) { + int slot = searchSlot(stack.getItem()); + if (slot != -1) { + getInventory(player).setSelectedSlot(slot); + trackedSelectedSlot = getInventory(player).getSelectedSlot(); + usedSlots.put(trackedSelectedSlot, stack.getItem()); + slotCounts.put(trackedSelectedSlot, stack.getCount()); + lastCount = stack.getCount(); + previousItem = stack.getItem(); + handlingItem = previousItem; + lastWorkedTick = tickCount; + MessageHolder.sendOrderMessage("Selected slot " + getInventory(player).getSelectedSlot() + " based on cache for " + stack.getItem()); + client.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(player).getSelectedSlot())); + return !getInventory(player).getSelectedStack().isEmpty(); + } + else { + MessageHolder.sendOrderMessage("Used slot contains stack but cannot find " + stack.getItem()); + } + } + MessageHolder.sendOrderMessage("Trying survival Swap"); + if (survivalSwap(client, player, stack)) { + usedSlots.put(getInventory(player).getSelectedSlot(), stack.getItem()); + slotCounts.put(trackedSelectedSlot, getMainHandStack(player).getCount()); + MessageHolder.sendOrderMessage("Swapped to item " + stack.getItem()); + handlingItem = stack.getItem(); + previousItem = handlingItem; + itemChangeCount++; + lastWorkedTick = tickCount; + return true; + } + MessageHolder.sendOrderMessage("Survival swap failed, trying creative swap"); + return creativeSwap(client, player, stack); + } + + synchronized public static ItemStack findItem(MinecraftClient client, Predicate predicate) { + ClientPlayerEntity player = client.player; + if (player == null) { + return ItemStack.EMPTY; + } + Inventory inv = getInventory(player); + for (int i = 0; i < inv.size(); i++) { + ItemStack stack = inv.getStack(i); + //#if MC>=12102 + if (EquipmentUtils.isRegularTool(stack) && predicate.test(stack)) { + //#else + //$$ if (stack.getItem() instanceof ToolItem && predicate.test(stack)) { + //#endif + return stack; + } + } + return ItemStack.EMPTY; + } + + synchronized public static boolean swapToItem(MinecraftClient client, Predicate predicate) { + ItemStack stack = findItem(client, predicate); + return swapToItem(client, stack); + } + + public static int getSlotWithStack(ClientPlayerEntity player, ItemStack stack) { + PlayerInventory inv = getInventory(player); + //#if MC>=12102 + return EquipmentUtils.isRegularTool(stack) || isToolLikeItem(stack.getItem()) ? getSlotWithItem(inv, stack) :getSlotWIthStackIgnoreNbt(getInventory(player), stack); + //#else + //$$ return stack.getItem() instanceof ToolItem || isToolLikeItem(stack.getItem()) ? getSlotWithItem(inv, stack) :getSlotWIthStackIgnoreNbt(getInventory(player), stack); + //#endif + } + + public static void printAllItems(PlayerInventory inv, ItemStack stack) { + // Debug + for (int i = 0; i < inv.getMainStacks().size(); i++) { + //#if MC >= 12006 + boolean areNbtsEqual = ItemStack.areItemsAndComponentsEqual(inv.getStack(i), stack); + //#else + //$$ boolean areNbtsEqual = ItemStack.canCombine(inv.getStack(i), stack); + //#endif + boolean areItemsEqual = ItemStack.areItemsEqual(inv.getStack(i), stack); + MessageHolder.sendUniqueDebugMessage("Slot " + i + ", " + inv.getStack(i).getItem() + " : " + areItemsEqual + " : " + areNbtsEqual); + } + } + + public static int getSlotWIthStackIgnoreNbt(PlayerInventory inv, ItemStack stack) { + // Debug + //printAllItems(inv, stack); + int defaultSlot = inv.getSlotWithStack(stack); + if (defaultSlot != -1) { + return defaultSlot; + } + if (!PRINTER_IGNORE_NBT.getBooleanValue()) { + return defaultSlot; + } + for (int i = 0; i < inv.getMainStacks().size(); i++) { + boolean areItemsEqual = ItemStack.areItemsEqual(inv.getStack(i), stack); + if (areItemsEqual) { + return i; + } + } + return -1; + } + + public static int getSlotWithStack(PlayerInventory inv, ItemStack stack) { + int findingStack = getSlotWIthStackIgnoreNbt(inv, stack); + //#if MC>=12102 + return EquipmentUtils.isRegularTool(stack) || isToolLikeItem(stack.getItem()) ? getSlotWithItem(inv, stack) :findingStack; + //#else + //$$ return stack.getItem() instanceof ToolItem || isToolLikeItem(stack.getItem()) ? getSlotWithItem(inv, stack) :findingStack; + //#endif + } + + @SuppressWarnings("ConstantConditions") + private static boolean creativeSwap(MinecraftClient client, ClientPlayerEntity player, ItemStack stack) { + if (!isCreative(player)) { + MessageHolder.sendOrderMessage("Player is not in creative mode"); + return false; + } + int selectedSlot = getAvailableSlot(stack.getItem()); + if (selectedSlot == -1) { + MessageHolder.sendOrderMessage("No available slot for " + stack.getItem()); + return false; + } + MessageHolder.sendOrderMessage("Clicked creative stack " + stack.getItem() + " for slot " + selectedSlot); + //getInventory(player).addPickBlock(stack); + getInventory(player).setSelectedSlot(selectedSlot); + client.interactionManager.clickCreativeStack(stack, 36 + selectedSlot); + client.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(getInventory(player).getSelectedSlot())); + trackedSelectedSlot = selectedSlot; + getInventory(player).getMainStacks().set(selectedSlot, stack); + usedSlots.put(getInventory(player).getSelectedSlot(), stack.getItem()); + slotCounts.put(trackedSelectedSlot, 65536); + lastCount = 65536; + handlingItem = stack.getItem(); + previousItem = handlingItem; + itemChangeCount++; + lastWorkedTick = tickCount; + return true; + } + + @SuppressWarnings("ConstantConditions") + private static boolean survivalSwap(MinecraftClient client, ClientPlayerEntity player, ItemStack stack) { + if (!canSwap(player, stack)) { + return false; + } + if (areItemsExact(player.getOffHandStack(), stack) && !areItemsExact(getMainHandStack(player), stack)) { + lastCount = isCreative(player) ? 65536 : client.player.getOffHandStack().getCount(); + client.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN)); + return true; + } + int slot = getSlotWithStack(player, stack); + if (slot == -1) { + return false; + } + if (PlayerInventory.isValidHotbarIndex(slot)) { + if (usedSlots.get(slot) != null) { + MessageHolder.sendOrderMessage("Hotbar slot should have been handled before, so it must be error!"); + MessageHolder.sendOrderMessage("Expected : " + usedSlots.get(slot) + " but current client handles : " + stack.getItem()); + return false; + } + getInventory(player).setSelectedSlot(slot); + trackedSelectedSlot = slot; + MessageHolder.sendOrderMessage("Selected hotbar Slot " + slot); + lastCount = isCreative(player) ? 65536 : getInventory(player).getStack(slot).getCount(); + client.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(slot)); + } else { + int selectedSlot = getAvailableSlot(stack.getItem()); + if (selectedSlot == -1) { + MessageHolder.sendOrderMessage("All hotbar slots are used"); + return false; + } + lastCount = isCreative(player) ? 65536 : getInventory(player).getStack(slot).getCount(); + MessageHolder.sendOrderMessage("Slot at " + slot + "(" + getInventory(player).getStack(slot).getItem() + ")" + " is swapped with " + selectedSlot + "(" + getInventory(player).getMainStacks().get(selectedSlot) + ")"); + usedSlots.put(selectedSlot, stack.getItem()); + client.interactionManager.clickSlot(player.playerScreenHandler.syncId, slot, selectedSlot, SlotActionType.SWAP, player); + getInventory(player).setSelectedSlot(selectedSlot); + trackedSelectedSlot = selectedSlot; + + } + try { + assert ItemStack.areEqual(getMainHandStack(player), stack); + } catch (Exception e) { + MessageHolder.sendMessageUncheckedUnique(player, stack.toString() + " does not match with " + player.getMainHandStack().toString() + "!"); + } + return true; + } + + public static List getRequiredStackInSchematic(World schematicWorld, MinecraftClient minecraftClient, BlockPos pos) { + final ClientPlayerEntity player = minecraftClient.player; + List result = new ArrayList<>(); + BlockEntity blockEntity = schematicWorld.getBlockEntity(pos); + if (blockEntity == null) { + return result; + } + if (blockEntity instanceof LootableContainerBlockEntity) { + // might use JAVA 8 + LootableContainerBlockEntity containerBlockEntity = (LootableContainerBlockEntity) blockEntity; + if (containerBlockEntity.isEmpty()) { + return result; + } + if (containerBlockEntity.canPlayerUse(player)) { + for (int i = 0; i < (containerBlockEntity).size(); i++) { + result.add(containerBlockEntity.getStack(i)); + } + } else { + MessageHolder.sendMessageUncheckedUnique(player, "Container at " + pos.toShortString() + "can't be opened by player!"); + } + } + return result; + } + + public static boolean hasItemInSchematic(World schematicWorld, BlockPos pos) { + BlockEntity blockEntity = schematicWorld.getBlockEntity(pos); + if (blockEntity == null) { + return false; + } + if (blockEntity instanceof LootableContainerBlockEntity) { + // might use JAVA 8 + LootableContainerBlockEntity containerBlockEntity = (LootableContainerBlockEntity) blockEntity; + if (containerBlockEntity.isEmpty()) { + return false; + } + for (int i = 0; i < (containerBlockEntity).size(); i++) { + if (!containerBlockEntity.getStack(i).isEmpty()) { + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/versions/mapping-1.21.5-1.21.4.txt b/versions/mapping-1.21.5-1.21.4.txt new file mode 100644 index 00000000..e69de29b